mirror of
https://github.com/correl/mage.git
synced 2024-12-29 03:00:15 +00:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # Mage.Sets/src/mage/cards/r/RashmiAndRagavan.java
This commit is contained in:
commit
5dbb68f72f
12 changed files with 625 additions and 39 deletions
80
Mage.Sets/src/mage/cards/c/ConjurersMantle.java
Normal file
80
Mage.Sets/src/mage/cards/c/ConjurersMantle.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksAttachedTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
import mage.abilities.keyword.EquipAbility;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ConjurersMantle extends CardImpl {
|
||||
|
||||
private static final FilterCard filter
|
||||
= new FilterCard("a card that shares a creature type with that creature");
|
||||
|
||||
static {
|
||||
filter.add(ConjurersMantlePredicate.instance);
|
||||
}
|
||||
|
||||
public ConjurersMantle(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{W}");
|
||||
|
||||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
||||
// Equipped creature gets +1/+1 and has vigilance.
|
||||
Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 1));
|
||||
ability.addEffect(new GainAbilityAttachedEffect(
|
||||
VigilanceAbility.getInstance(), AttachmentType.EQUIPMENT
|
||||
).setText("and has vigilance"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever equipped creature attacks, look at the top six cards of your library. You may reveal a card that shares a creature type with that creature from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
|
||||
this.addAbility(new AttacksAttachedTriggeredAbility(new LookLibraryAndPickControllerEffect(
|
||||
6, 1, filter, PutCards.HAND, PutCards.BOTTOM_RANDOM
|
||||
), AttachmentType.EQUIPMENT, false, SetTargetPointer.PERMANENT));
|
||||
|
||||
// Equip {1}
|
||||
this.addAbility(new EquipAbility(1, false));
|
||||
}
|
||||
|
||||
private ConjurersMantle(final ConjurersMantle card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConjurersMantle copy() {
|
||||
return new ConjurersMantle(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum ConjurersMantlePredicate implements ObjectSourcePlayerPredicate<Card> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
|
||||
return input
|
||||
.getSource()
|
||||
.getEffects()
|
||||
.stream()
|
||||
.map(effect -> effect.getTargetPointer().getFirst(game, input.getSource()))
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(permanent -> permanent.shareCreatureTypes(game, input.getObject()));
|
||||
}
|
||||
}
|
90
Mage.Sets/src/mage/cards/d/DanceWithCalamity.java
Normal file
90
Mage.Sets/src/mage/cards/d/DanceWithCalamity.java
Normal file
|
@ -0,0 +1,90 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class DanceWithCalamity extends CardImpl {
|
||||
|
||||
public DanceWithCalamity(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{7}{R}");
|
||||
|
||||
// Shuffle your library. As many times as you choose, you may exile the top card of your library. If the total mana value of the cards exiled this way is 13 or less, you may cast any number of spells from among those cards without paying their mana costs.
|
||||
this.getSpellAbility().addEffect(new DanceWithCalamityEffect());
|
||||
}
|
||||
|
||||
private DanceWithCalamity(final DanceWithCalamity card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DanceWithCalamity copy() {
|
||||
return new DanceWithCalamity(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DanceWithCalamityEffect extends OneShotEffect {
|
||||
|
||||
DanceWithCalamityEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "shuffle your library. As many times as you choose, you may exile the top card of your library. " +
|
||||
"If the total mana value of the cards exiled this way is 13 or less, you may cast any number " +
|
||||
"of spells from among those cards without paying their mana costs";
|
||||
}
|
||||
|
||||
private DanceWithCalamityEffect(final DanceWithCalamityEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DanceWithCalamityEffect copy() {
|
||||
return new DanceWithCalamityEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
Cards cards = new CardsImpl();
|
||||
while (player.getLibrary().hasCards()) {
|
||||
int totalMV = cards
|
||||
.getCards(game)
|
||||
.stream()
|
||||
.mapToInt(MageObject::getManaValue)
|
||||
.sum();
|
||||
if (!player.chooseUse(
|
||||
outcome, "Exile the top card of your library?",
|
||||
"Current total mana value is " + totalMV,
|
||||
"Yes", "No", source, game
|
||||
)) {
|
||||
break;
|
||||
}
|
||||
Card card = player.getLibrary().getFromTop(game);
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
cards.add(card);
|
||||
}
|
||||
if (cards
|
||||
.getCards(game)
|
||||
.stream()
|
||||
.mapToInt(MageObject::getManaValue)
|
||||
.sum() <= 13) {
|
||||
CardUtil.castMultipleWithAttributeForFree(player, source, game, cards, StaticFilters.FILTER_CARD);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
62
Mage.Sets/src/mage/cards/d/DarksteelSplicer.java
Normal file
62
Mage.Sets/src/mage/cards/d/DarksteelSplicer.java
Normal file
|
@ -0,0 +1,62 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.EntersBattlefieldThisOrAnotherTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.OpponentsCount;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.keyword.IndestructibleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.permanent.token.PhyrexianGolemToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class DarksteelSplicer extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterPermanent(SubType.PHYREXIAN, "nontoken Phyrexian");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
private static final FilterPermanent filter2 = new FilterPermanent(SubType.GOLEM, "Golems");
|
||||
|
||||
public DarksteelSplicer(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}");
|
||||
|
||||
this.subtype.add(SubType.PHYREXIAN);
|
||||
this.subtype.add(SubType.ARTIFICER);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Whenever Darksteel Splicer or another nontoken Phyrexian enters the battlefield under your control, create X 3/3 colorless Phyrexian Golem artifact creature tokens, where X is the number of opponents you have.
|
||||
this.addAbility(new EntersBattlefieldThisOrAnotherTriggeredAbility(
|
||||
new CreateTokenEffect(new PhyrexianGolemToken(), OpponentsCount.instance),
|
||||
filter, false, true
|
||||
));
|
||||
|
||||
// Golems you control have indestructible.
|
||||
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
|
||||
IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
||||
)));
|
||||
}
|
||||
|
||||
private DarksteelSplicer(final DarksteelSplicer card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DarksteelSplicer copy() {
|
||||
return new DarksteelSplicer(this);
|
||||
}
|
||||
}
|
81
Mage.Sets/src/mage/cards/e/EmergentWoodwurm.java
Normal file
81
Mage.Sets/src/mage/cards/e/EmergentWoodwurm.java
Normal file
|
@ -0,0 +1,81 @@
|
|||
package mage.cards.e;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.SourcePermanentPowerCount;
|
||||
import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
|
||||
import mage.abilities.keyword.BackupAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.PutCards;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterPermanentCard;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class EmergentWoodwurm extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterPermanentCard("permanent card with mana value X or less");
|
||||
|
||||
static {
|
||||
filter.add(EmergentWoodwurmPredicate.instance);
|
||||
}
|
||||
|
||||
private static final DynamicValue xValue = new SourcePermanentPowerCount(false);
|
||||
|
||||
public EmergentWoodwurm(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{G}");
|
||||
|
||||
this.subtype.add(SubType.WURM);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Backup 3
|
||||
BackupAbility backupAbility = new BackupAbility(this, 3);
|
||||
this.addAbility(backupAbility);
|
||||
|
||||
// Whenever this creature attacks, look at the top X cards of your library, where X is its power. You may put a permanent card with mana value X or less from among them onto the battlefield. Put the rest on the bottom of your library in a random order.
|
||||
backupAbility.addAbility(new AttacksTriggeredAbility(new LookLibraryAndPickControllerEffect(
|
||||
xValue, 1, filter, PutCards.BATTLEFIELD, PutCards.BOTTOM_RANDOM
|
||||
)).setTriggerPhrase("Whenever this creature attacks, "));
|
||||
}
|
||||
|
||||
private EmergentWoodwurm(final EmergentWoodwurm card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmergentWoodwurm copy() {
|
||||
return new EmergentWoodwurm(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum EmergentWoodwurmPredicate implements ObjectSourcePlayerPredicate<Card> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
|
||||
return Optional
|
||||
.of(input)
|
||||
.map(ObjectSourcePlayer::getSource)
|
||||
.map(ability -> ability.getSourcePermanentOrLKI(game))
|
||||
.filter(Objects::nonNull)
|
||||
.map(MageObject::getPower)
|
||||
.map(MageInt::getValue)
|
||||
.map(i -> input.getObject().getManaValue() <= i)
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
59
Mage.Sets/src/mage/cards/h/HedronDetonator.java
Normal file
59
Mage.Sets/src/mage/cards/h/HedronDetonator.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
package mage.cards.h;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.ExileTopXMayPlayUntilEndOfTurnEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class HedronDetonator extends CardImpl {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledArtifactPermanent("artifacts");
|
||||
|
||||
public HedronDetonator(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
|
||||
|
||||
this.subtype.add(SubType.GOBLIN);
|
||||
this.subtype.add(SubType.ARTIFICER);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Whenever an artifact enters the battlefield under your control, Hedron Detonator deals 1 damage to target opponent.
|
||||
Ability ability = new EntersBattlefieldControlledTriggeredAbility(
|
||||
new DamageTargetEffect(1), StaticFilters.FILTER_PERMANENT_ARTIFACT
|
||||
);
|
||||
ability.addTarget(new TargetOpponent());
|
||||
this.addAbility(ability);
|
||||
|
||||
// {T}, Sacrifice two artifacts: Exile the top card of your library. You may play that card this turn.
|
||||
ability = new SimpleActivatedAbility(new ExileTopXMayPlayUntilEndOfTurnEffect(1), new TapSourceCost());
|
||||
ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(2, filter)));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private HedronDetonator(final HedronDetonator card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HedronDetonator copy() {
|
||||
return new HedronDetonator(this);
|
||||
}
|
||||
}
|
104
Mage.Sets/src/mage/cards/m/MirrorStyleMaster.java
Normal file
104
Mage.Sets/src/mage/cards/m/MirrorStyleMaster.java
Normal file
|
@ -0,0 +1,104 @@
|
|||
package mage.cards.m;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.keyword.BackupAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.AttackingPredicate;
|
||||
import mage.filter.predicate.permanent.ModifiedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class MirrorStyleMaster extends CardImpl {
|
||||
|
||||
public MirrorStyleMaster(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.WARRIOR);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Backup 1
|
||||
BackupAbility backupAbility = new BackupAbility(this, 1);
|
||||
this.addAbility(backupAbility);
|
||||
|
||||
// Whenever this creature attacks, for each attacking modified creature you control, create a tapped and attacking token that's a copy of that creature. Exile those tokens at end of combat.
|
||||
backupAbility.addAbility(new AttacksTriggeredAbility(new MirrorStyleMasterEffect())
|
||||
.setTriggerPhrase("Whenever this creature attacks, "));
|
||||
}
|
||||
|
||||
private MirrorStyleMaster(final MirrorStyleMaster card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MirrorStyleMaster copy() {
|
||||
return new MirrorStyleMaster(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MirrorStyleMasterEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent();
|
||||
|
||||
static {
|
||||
filter.add(AttackingPredicate.instance);
|
||||
filter.add(ModifiedPredicate.instance);
|
||||
}
|
||||
|
||||
MirrorStyleMasterEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "for each attacking modified creature you control, create a tapped and attacking token " +
|
||||
"that's a copy of that creature. Exile those tokens at end of combat";
|
||||
}
|
||||
|
||||
private MirrorStyleMasterEffect(final MirrorStyleMasterEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MirrorStyleMasterEffect copy() {
|
||||
return new MirrorStyleMasterEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
List<Permanent> permanents = new ArrayList<>();
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(
|
||||
filter, source.getControllerId(), source, game
|
||||
)) {
|
||||
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(
|
||||
null, null, false, 1, true, true
|
||||
);
|
||||
effect.setSavedPermanent(permanent);
|
||||
effect.apply(game, source);
|
||||
permanents.addAll(effect.getAddedPermanents());
|
||||
}
|
||||
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(
|
||||
new ExileTargetEffect()
|
||||
.setTargetPointer(new FixedTargets(permanents, game))
|
||||
.setText("exile those tokens")
|
||||
), source);
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -44,8 +44,8 @@ public final class RashmiAndRagavan extends CardImpl {
|
|||
this.toughness = new MageInt(4);
|
||||
|
||||
// Whenever you cast your first spell during each of your turns,
|
||||
// exile the top card of target opponent’s library and create a Treasure token.
|
||||
// Then you may cast the exiled card without paying its mana cost if it’s a spell with mana value
|
||||
// exile the top card of target opponent's library and create a Treasure token.
|
||||
// Then you may cast the exiled card without paying its mana cost if it's a spell with mana value
|
||||
// less than the number of artifacts you control.
|
||||
// If you don’t cast it this way, you may cast it this turn.
|
||||
this.addAbility(new RashmiAndRagavanTriggeredAbility());
|
||||
|
@ -97,9 +97,9 @@ class RashmiAndRagavanTriggeredAbility extends SpellCastControllerTriggeredAbili
|
|||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever you cast your first spell during each of your turns, exile the top card of target "
|
||||
+ "opponent’s library and create a Treasure token. Then you may cast the exiled card without "
|
||||
+ "paying its mana cost if it’s a spell with mana value less than the number of artifacts you "
|
||||
+ "control. If you don’t cast it this way, you may cast it this turn.";
|
||||
+ "opponent's library and create a Treasure token. Then you may cast the exiled card without "
|
||||
+ "paying its mana cost if it's a spell with mana value less than the number of artifacts you "
|
||||
+ "control. If you don't cast it this way, you may cast it this turn.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,9 +107,9 @@ class RashmiAndRagavanEffect extends OneShotEffect {
|
|||
|
||||
RashmiAndRagavanEffect() {
|
||||
super(Outcome.PlayForFree);
|
||||
this.staticText = "exile the top card of target opponent’s library and create a Treasure token. "
|
||||
+ "Then you may cast the exiled card without paying its mana cost if it’s a spell with mana value "
|
||||
+ "less than the number of artifacts you control. If you don’t cast it this way, "
|
||||
this.staticText = "exile the top card of target opponent's library and create a Treasure token. "
|
||||
+ "Then you may cast the exiled card without paying its mana cost if it's a spell with mana value "
|
||||
+ "less than the number of artifacts you control. If you don't cast it this way, "
|
||||
+ "you may cast it this turn";
|
||||
}
|
||||
|
||||
|
|
69
Mage.Sets/src/mage/cards/s/SaintTraftAndRemKarolus.java
Normal file
69
Mage.Sets/src/mage/cards/s/SaintTraftAndRemKarolus.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BecomesTappedSourceTriggeredAbility;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.IfAbilityHasResolvedXTimesEffect;
|
||||
import mage.abilities.effects.common.UntapSourceEffect;
|
||||
import mage.abilities.keyword.ConvokeAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.game.permanent.token.AngelToken;
|
||||
import mage.game.permanent.token.RedHumanToken;
|
||||
import mage.game.permanent.token.SpiritBlueToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class SaintTraftAndRemKarolus extends CardImpl {
|
||||
|
||||
private static final FilterSpell filter = new FilterSpell("a spell that has convoke");
|
||||
|
||||
static {
|
||||
filter.add(new AbilityPredicate(ConvokeAbility.class));
|
||||
}
|
||||
|
||||
public SaintTraftAndRemKarolus(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{U}{R}{W}");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Whenever Saint Traft and Rem Karolus becomes tapped, create a 1/1 red Human creature token if this is the first time this ability has resolved this turn. If it's the second time, create a 1/1 blue Spirit creature token with flying. If it's the third time, create a 4/4 white Angel creature token with flying.
|
||||
Ability ability = new BecomesTappedSourceTriggeredAbility(new IfAbilityHasResolvedXTimesEffect(
|
||||
Outcome.PutCreatureInPlay, 1, new CreateTokenEffect(new RedHumanToken())
|
||||
).setText("create a 1/1 red Human creature token if this is the first time this ability has resolved this turn"));
|
||||
ability.addEffect(new IfAbilityHasResolvedXTimesEffect(
|
||||
Outcome.PutCreatureInPlay, 2, new CreateTokenEffect(new SpiritBlueToken())
|
||||
).setText("If it's the second time, create a 1/1 blue Spirit creature token with flying"));
|
||||
ability.addEffect(new IfAbilityHasResolvedXTimesEffect(
|
||||
Outcome.PutCreatureInPlay, 3, new CreateTokenEffect(new AngelToken())
|
||||
).setText("If it's the third time, create a 4/4 white Angel creature token with flying"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever you cast a spell that has convoke, untap Saint Traft and Rem Karolus.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(new UntapSourceEffect(), filter, false));
|
||||
}
|
||||
|
||||
private SaintTraftAndRemKarolus(final SaintTraftAndRemKarolus card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SaintTraftAndRemKarolus copy() {
|
||||
return new SaintTraftAndRemKarolus(this);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,14 @@
|
|||
package mage.cards.v;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbility;
|
||||
import mage.abilities.common.SpellCastOpponentTriggeredAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.SourceMatchesFilterCondition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
@ -22,14 +21,17 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class VeiledSentry extends CardImpl {
|
||||
|
||||
private static final Condition condition = new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT);
|
||||
|
||||
public VeiledSentry(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}");
|
||||
|
||||
// When an opponent casts a spell, if Veiled Sentry is an enchantment, Veiled Sentry becomes an Illusion creature with power and toughness each equal to that spell's converted mana cost.
|
||||
TriggeredAbility ability = new SpellCastOpponentTriggeredAbility(Zone.BATTLEFIELD, new VeiledSentryEffect(), new FilterSpell(), false, SetTargetPointer.SPELL);
|
||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, new SourceMatchesFilterCondition(StaticFilters.FILTER_PERMANENT_ENCHANTMENT),
|
||||
"Whenever an opponent casts a spell, if Veiled Sentry is an enchantment, Veil Sentry becomes an Illusion creature with power and toughness equal to that spell's mana value."));
|
||||
|
||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||
new SpellCastOpponentTriggeredAbility(new VeiledSentryEffect(), false),
|
||||
condition, "Whenever an opponent casts a spell, if {this} is an enchantment, " +
|
||||
"{this} becomes an Illusion creature with power and toughness equal to that spell's mana value."
|
||||
));
|
||||
}
|
||||
|
||||
private VeiledSentry(final VeiledSentry card) {
|
||||
|
@ -44,6 +46,8 @@ public final class VeiledSentry extends CardImpl {
|
|||
|
||||
class VeiledSentryEffect extends ContinuousEffectImpl {
|
||||
|
||||
private int spellMV = 0;
|
||||
|
||||
public VeiledSentryEffect() {
|
||||
super(Duration.Custom, Outcome.BecomeCreature);
|
||||
staticText = "{this} becomes an Illusion creature with power and toughness equal to that spell's mana value";
|
||||
|
@ -51,6 +55,7 @@ class VeiledSentryEffect extends ContinuousEffectImpl {
|
|||
|
||||
public VeiledSentryEffect(final VeiledSentryEffect effect) {
|
||||
super(effect);
|
||||
this.spellMV = effect.spellMV;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,30 +64,32 @@ class VeiledSentryEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
Permanent veiledSentry = game.getPermanent(source.getSourceId());
|
||||
Spell spellCast = game.getSpell(targetPointer.getFirst(game, source));
|
||||
if (spellCast != null) {
|
||||
game.getState().setValue(source + "cmcSpell", spellCast.getManaValue());
|
||||
public void init(Ability source, Game game) {
|
||||
super.init(source, game);
|
||||
Spell spell = (Spell) getValue("spellCast");
|
||||
if (spell != null) {
|
||||
spellMV = spell.getManaValue();
|
||||
}
|
||||
if (veiledSentry == null) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null) {
|
||||
discard();
|
||||
return false;
|
||||
}
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
veiledSentry.removeAllCardTypes(game);
|
||||
veiledSentry.removeAllSubTypes(game);
|
||||
veiledSentry.addCardType(game, CardType.CREATURE);
|
||||
veiledSentry.addSubType(game, SubType.ILLUSION);
|
||||
permanent.removeAllCardTypes(game);
|
||||
permanent.removeAllSubTypes(game);
|
||||
permanent.addCardType(game, CardType.CREATURE);
|
||||
permanent.addSubType(game, SubType.ILLUSION);
|
||||
break;
|
||||
|
||||
case PTChangingEffects_7:
|
||||
if (game.getState().getValue(source + "cmcSpell") != null) {
|
||||
int cmc = (int) game.getState().getValue(source + "cmcSpell");
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
veiledSentry.addPower(cmc);
|
||||
veiledSentry.addToughness(cmc);
|
||||
}
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
permanent.getPower().setModifiedBaseValue(spellMV);
|
||||
permanent.getToughness().setModifiedBaseValue(spellMV);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -72,6 +72,7 @@ public final class MarchOfTheMachineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Commander's Sphere", 352, Rarity.COMMON, mage.cards.c.CommandersSphere.class));
|
||||
cards.add(new SetCardInfo("Conclave Mentor", 320, Rarity.UNCOMMON, mage.cards.c.ConclaveMentor.class));
|
||||
cards.add(new SetCardInfo("Conclave Tribunal", 178, Rarity.UNCOMMON, mage.cards.c.ConclaveTribunal.class));
|
||||
cards.add(new SetCardInfo("Conjurer's Mantle", 12, Rarity.RARE, mage.cards.c.ConjurersMantle.class));
|
||||
cards.add(new SetCardInfo("Constable of the Realm", 179, Rarity.UNCOMMON, mage.cards.c.ConstableOfTheRealm.class));
|
||||
cards.add(new SetCardInfo("Corpse Knight", 321, Rarity.UNCOMMON, mage.cards.c.CorpseKnight.class));
|
||||
cards.add(new SetCardInfo("Coveted Jewel", 353, Rarity.RARE, mage.cards.c.CovetedJewel.class));
|
||||
|
@ -79,6 +80,8 @@ public final class MarchOfTheMachineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Cultivate", 295, Rarity.COMMON, mage.cards.c.Cultivate.class));
|
||||
cards.add(new SetCardInfo("Cultivator's Caravan", 354, Rarity.RARE, mage.cards.c.CultivatorsCaravan.class));
|
||||
cards.add(new SetCardInfo("Curse of Opulence", 274, Rarity.UNCOMMON, mage.cards.c.CurseOfOpulence.class));
|
||||
cards.add(new SetCardInfo("Dance with Calamity", 29, Rarity.RARE, mage.cards.d.DanceWithCalamity.class));
|
||||
cards.add(new SetCardInfo("Darksteel Splicer", 13, Rarity.RARE, mage.cards.d.DarksteelSplicer.class));
|
||||
cards.add(new SetCardInfo("Death-Greeter's Champion", 30, Rarity.RARE, mage.cards.d.DeathGreetersChampion.class));
|
||||
cards.add(new SetCardInfo("Deluxe Dragster", 21, Rarity.RARE, mage.cards.d.DeluxeDragster.class));
|
||||
cards.add(new SetCardInfo("Despark", 322, Rarity.UNCOMMON, mage.cards.d.Despark.class));
|
||||
|
@ -90,6 +93,7 @@ public final class MarchOfTheMachineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Echo Storm", 221, Rarity.RARE, mage.cards.e.EchoStorm.class));
|
||||
cards.add(new SetCardInfo("Elite Scaleguard", 181, Rarity.UNCOMMON, mage.cards.e.EliteScaleguard.class));
|
||||
cards.add(new SetCardInfo("Elspeth, Sun's Champion", 182, Rarity.MYTHIC, mage.cards.e.ElspethSunsChampion.class));
|
||||
cards.add(new SetCardInfo("Emergent Woodwurm", 37, Rarity.RARE, mage.cards.e.EmergentWoodwurm.class));
|
||||
cards.add(new SetCardInfo("Emeria Angel", 183, Rarity.RARE, mage.cards.e.EmeriaAngel.class));
|
||||
cards.add(new SetCardInfo("Enduring Scalelord", 325, Rarity.UNCOMMON, mage.cards.e.EnduringScalelord.class));
|
||||
cards.add(new SetCardInfo("Ephemeral Shields", 184, Rarity.COMMON, mage.cards.e.EphemeralShields.class));
|
||||
|
@ -138,6 +142,7 @@ public final class MarchOfTheMachineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Hamza, Guardian of Arashin", 327, Rarity.UNCOMMON, mage.cards.h.HamzaGuardianOfArashin.class));
|
||||
cards.add(new SetCardInfo("Heaven // Earth", 328, Rarity.RARE, mage.cards.h.HeavenEarth.class));
|
||||
cards.add(new SetCardInfo("Hedron Archive", 359, Rarity.UNCOMMON, mage.cards.h.HedronArchive.class));
|
||||
cards.add(new SetCardInfo("Hedron Detonator", 31, Rarity.RARE, mage.cards.h.HedronDetonator.class));
|
||||
cards.add(new SetCardInfo("Hellkite Igniter", 284, Rarity.RARE, mage.cards.h.HellkiteIgniter.class));
|
||||
cards.add(new SetCardInfo("Herald of Hoofbeats", 22, Rarity.RARE, mage.cards.h.HeraldOfHoofbeats.class));
|
||||
cards.add(new SetCardInfo("Herald's Horn", 360, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class));
|
||||
|
@ -192,6 +197,7 @@ public final class MarchOfTheMachineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Mikaeus, the Lunarch", 197, Rarity.RARE, mage.cards.m.MikaeusTheLunarch.class));
|
||||
cards.add(new SetCardInfo("Mind Stone", 364, Rarity.UNCOMMON, mage.cards.m.MindStone.class));
|
||||
cards.add(new SetCardInfo("Mindless Automaton", 365, Rarity.UNCOMMON, mage.cards.m.MindlessAutomaton.class));
|
||||
cards.add(new SetCardInfo("Mirror-Style Master", 32, Rarity.RARE, mage.cards.m.MirrorStyleMaster.class));
|
||||
cards.add(new SetCardInfo("Mortify", 337, Rarity.UNCOMMON, mage.cards.m.Mortify.class));
|
||||
cards.add(new SetCardInfo("Mossfire Valley", 414, Rarity.RARE, mage.cards.m.MossfireValley.class));
|
||||
cards.add(new SetCardInfo("Mosswort Bridge", 415, Rarity.RARE, mage.cards.m.MosswortBridge.class));
|
||||
|
@ -238,6 +244,7 @@ public final class MarchOfTheMachineCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Root Out", 311, Rarity.COMMON, mage.cards.r.RootOut.class));
|
||||
cards.add(new SetCardInfo("Saheeli's Artistry", 234, Rarity.RARE, mage.cards.s.SaheelisArtistry.class));
|
||||
cards.add(new SetCardInfo("Saheeli, Sublime Artificer", 338, Rarity.UNCOMMON, mage.cards.s.SaheeliSublimeArtificer.class));
|
||||
cards.add(new SetCardInfo("Saint Traft and Rem Karolus", 9, Rarity.MYTHIC, mage.cards.s.SaintTraftAndRemKarolus.class));
|
||||
cards.add(new SetCardInfo("Sandsteppe War Riders", 39, Rarity.RARE, mage.cards.s.SandsteppeWarRiders.class));
|
||||
cards.add(new SetCardInfo("Schema Thief", 24, Rarity.RARE, mage.cards.s.SchemaThief.class));
|
||||
cards.add(new SetCardInfo("Scrap Trawler", 373, Rarity.RARE, mage.cards.s.ScrapTrawler.class));
|
||||
|
|
|
@ -1283,6 +1283,8 @@ public class VerifyCardDataTest {
|
|||
if (token == null) {
|
||||
errorsList.add("Error: token must have default constructor with zero params: " + tokenClass.getName());
|
||||
} else if (tokDataNamesIndex.getOrDefault(token.getName().replace(" Token", ""), "").isEmpty()) {
|
||||
// how-to fix: public token must be downloadable, so tok-data must contain miss set
|
||||
// (also don't forget to add new set to scryfall download)
|
||||
errorsList.add("Error: can't find data in card-pictures-tok.txt for token: " + tokenClass.getName() + " -> " + token.getName());
|
||||
}
|
||||
}
|
||||
|
@ -1312,20 +1314,45 @@ public class VerifyCardDataTest {
|
|||
}
|
||||
// set uses tokens, but tok data miss it
|
||||
setsWithTokens.forEach((setCode, sourceCards) -> {
|
||||
if (!tokDataTokensBySetIndex.containsKey(setCode)) {
|
||||
List<CardDownloadData> setTokens = tokDataTokensBySetIndex.getOrDefault(setCode, null);
|
||||
if (setTokens == null) {
|
||||
// it's not a problem -- just find set's cards without real tokens for image tests
|
||||
// (most use cases: promo sets)
|
||||
warningsList.add("info, set has cards with token abilities, but it haven't token data: "
|
||||
// Possible reasons:
|
||||
// - promo sets with cards without tokens (nothing to do with it)
|
||||
// - miss set from tok-data (must add new set to tok-data and scryfall download)
|
||||
warningsList.add("info, set's cards uses tokens but tok-data haven't it: "
|
||||
+ setCode + " - " + sourceCards.stream().map(MageObject::getName).collect(Collectors.joining(", ")));
|
||||
} else {
|
||||
// Card can be checked on scryfall like "set:set_code oracle:token_name oracle:token"
|
||||
// Possible reasons for un-used tokens:
|
||||
// - normal use case: tok-data contains wrong token data and must be removed
|
||||
// - normal use case: card uses wrong rules text (must contain "create" and "token" words)
|
||||
// - rare use case: un-implemented card that uses a token
|
||||
setTokens.forEach(token -> {
|
||||
if (token.getName().contains("Plane - ")) {
|
||||
// cards don't put it to the game, so no related cards
|
||||
return;
|
||||
}
|
||||
String needTokenName = token.getName()
|
||||
.replace(" Token", "")
|
||||
.replace("Emblem ", "");
|
||||
// need add card name, so it will skip no name emblems like Sarkhan, the Dragonspeaker
|
||||
if (sourceCards.stream()
|
||||
.map(card -> card.getName() + " - " + String.join(", ", card.getRules()))
|
||||
.noneMatch(s -> s.contains(needTokenName))) {
|
||||
warningsList.add("info, tok-data has un-used tokens: "
|
||||
+ token.getSet() + " - " + token.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// tok data have tokens, but cards from set are miss
|
||||
tokDataTokensBySetIndex.forEach((setCode, setTokens) -> {
|
||||
if (!setsWithTokens.containsKey(setCode)) {
|
||||
// possible reasons:
|
||||
// - bad: outdated set code in tokens database (must use exists set codes, see additional check with token's codes)
|
||||
// - ok: promo set contains additional tokens for main set -- it's ok and must be ignored (example: Saproling in E02)
|
||||
warningsList.add("warning, tok data has set with tokens, but real set haven't cards with it: "
|
||||
// Possible reasons:
|
||||
// - outdated set code in tokens database (must be fixed by new set code, another verify check it)
|
||||
// - promo set contains additional tokens for main set (it's ok and must be ignored, example: Saproling in E02)
|
||||
warningsList.add("warning, tok-data has tokens, but real set haven't cards with it: "
|
||||
+ setCode + " - " + setTokens.stream().map(CardDownloadData::getName).collect(Collectors.joining(", ")));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ import java.util.Arrays;
|
|||
public final class VoiceOfResurgenceToken extends TokenImpl {
|
||||
|
||||
public VoiceOfResurgenceToken() {
|
||||
super("Elemental Token", "X/X green and white Elemental creature with with \"This creature's power and toughness are each equal to the number of creatures you control.");
|
||||
super("Elemental Token", "green and white Elemental creature token with \"This creature's power and toughness are each equal to the number of creatures you control.");
|
||||
setOriginalExpansionSetCode("DGM");
|
||||
cardType.add(CardType.CREATURE);
|
||||
color.setGreen(true);
|
||||
|
|
Loading…
Reference in a new issue