Phyrexian mana now correctly a payment choice.

Per rule 601.2b, it is not determined at the pay costs step,
but at the "choice" step, long before costs are determined.

This fixes trinisphere interactions and should be consistent with the
rules.
This commit is contained in:
Nathaniel Brandes 2017-03-08 20:03:28 -08:00
parent 209e2d13c1
commit ce1f4a3bf8
47 changed files with 234 additions and 222 deletions

View file

@ -148,7 +148,6 @@ public final class UI {
} }
public static String getDisplayManaCost (String manaCost) { public static String getDisplayManaCost (String manaCost) {
manaCost = manaCost.replace("/", "");
// A pipe in the cost means "process left of the pipe as the card color, but display right of the pipe as the cost". // A pipe in the cost means "process left of the pipe as the card color, but display right of the pipe as the cost".
int pipePosition = manaCost.indexOf("{|}"); int pipePosition = manaCost.indexOf("{|}");
if (pipePosition != -1) { if (pipePosition != -1) {

View file

@ -823,9 +823,6 @@ public class HumanPlayer extends PlayerImpl {
protected boolean playManaHandling(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) { protected boolean playManaHandling(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) {
updateGameStatePriority("playMana", game); updateGameStatePriority("playMana", game);
Map<String, Serializable> options = new HashMap<>(); Map<String, Serializable> options = new HashMap<>();
if (unpaid.getText().contains("P}")) {
options.put(Constants.Option.SPECIAL_BUTTON, (Serializable) "Pay 2 life");
}
game.firePlayManaEvent(playerId, "Pay " + promptText, options); game.firePlayManaEvent(playerId, "Pay " + promptText, options);
waitForResponse(game); waitForResponse(game);
if (!this.canRespond()) { if (!this.canRespond()) {
@ -838,18 +835,6 @@ public class HumanPlayer extends PlayerImpl {
} else if (response.getString() != null && response.getString().equals("special")) { } else if (response.getString() != null && response.getString().equals("special")) {
if (unpaid instanceof ManaCostsImpl) { if (unpaid instanceof ManaCostsImpl) {
specialManaAction(unpaid, game); specialManaAction(unpaid, game);
// TODO: delve or convoke cards with PhyrexianManaCost won't work together (this combinaton does not exist yet)
@SuppressWarnings("unchecked")
ManaCostsImpl<ManaCost> costs = (ManaCostsImpl<ManaCost>) unpaid;
for (ManaCost cost : costs.getUnpaid()) {
if (cost instanceof PhyrexianManaCost) {
PhyrexianManaCost ph = (PhyrexianManaCost) cost;
if (ph.canPay(null, null, playerId, game)) {
((PhyrexianManaCost) cost).pay(null, game, null, playerId, false, null);
}
break;
}
}
} }
} else if (response.getManaType() != null) { } else if (response.getManaType() != null) {
// this mana type can be paid once from pool // this mana type can be paid once from pool
@ -907,7 +892,7 @@ public class HumanPlayer extends PlayerImpl {
} }
Spell spell = game.getStack().getSpell(abilityToCast.getSourceId()); Spell spell = game.getStack().getSpell(abilityToCast.getSourceId());
if (spell != null && spell.isDoneActivatingManaAbilities()) { if (spell != null && spell.isDoneActivatingManaAbilities()) {
game.informPlayer(this, "You can't no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first."); game.informPlayer(this, "You can no longer use activated mana abilities to pay for the current spell. Cancel and recast the spell and activate mana abilities first.");
return; return;
} }
Zone zone = game.getState().getZone(object.getId()); Zone zone = game.getState().getZone(object.getId());

View file

@ -54,7 +54,7 @@ public class ActOfAggression extends CardImpl {
} }
public ActOfAggression(UUID ownerId, CardSetInfo setInfo) { public ActOfAggression(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{RP}{RP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R/P}{R/P}");
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn));

View file

@ -63,9 +63,9 @@ public class ApostlesBlessing extends CardImpl {
} }
public ApostlesBlessing(UUID ownerId, CardSetInfo setInfo) { public ApostlesBlessing(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{WP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W/P}");
// ({WP} can be paid with either {W} or 2 life.) // ({W/P} can be paid with either {W} or 2 life.)
// Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn. // Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn.
this.getSpellAbility().addEffect(new ApostlesBlessingEffect()); this.getSpellAbility().addEffect(new ApostlesBlessingEffect());
this.getSpellAbility().addTarget(new TargetControlledPermanent(filter)); this.getSpellAbility().addTarget(new TargetControlledPermanent(filter));

View file

@ -57,11 +57,11 @@ import mage.target.common.TargetControlledCreaturePermanent;
public class BirthingPod extends CardImpl { public class BirthingPod extends CardImpl {
public BirthingPod(UUID ownerId, CardSetInfo setInfo) { public BirthingPod(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}{GP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}{G/P}");
// {1}{GP}, {tap}, Sacrifice a creature: Search your library for a creature card with converted mana cost equal to 1 plus the sacrificed creature's converted mana cost, // {1}{G/P}, {tap}, Sacrifice a creature: Search your library for a creature card with converted mana cost equal to 1 plus the sacrificed creature's converted mana cost,
// put that card onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery. // put that card onto the battlefield, then shuffle your library. Activate this ability only any time you could cast a sorcery.
Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new BirthingPodEffect(), new ManaCostsImpl("{1}{GP}")); Ability ability = new ActivateAsSorceryActivatedAbility(Zone.BATTLEFIELD, new BirthingPodEffect(), new ManaCostsImpl("{1}{G/P}"));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent()));
this.addAbility(ability); this.addAbility(ability);

View file

@ -53,7 +53,7 @@ public class BlindingSouleater extends CardImpl {
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// {WP},{T}: Tap target creature. ( can be paid with either or 2 life.) // {W/P},{T}: Tap target creature. ( can be paid with either or 2 life.)
SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,
new TapTargetEffect(), new TapTargetEffect(),
new PhyrexianManaCost(ColoredManaSymbol.W)); new PhyrexianManaCost(ColoredManaSymbol.W));

View file

@ -51,13 +51,13 @@ import java.util.UUID;
public class CathedralMembrane extends CardImpl { public class CathedralMembrane extends CardImpl {
public CathedralMembrane(UUID ownerId, CardSetInfo setInfo) { public CathedralMembrane(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{1}{WP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{1}{W/P}");
this.subtype.add("Wall"); this.subtype.add("Wall");
this.power = new MageInt(0); this.power = new MageInt(0);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// <i>({WP} can be paid with either {W} or 2 life.)</i> // <i>({W/P} can be paid with either {W} or 2 life.)</i>
this.addAbility(DefenderAbility.getInstance()); this.addAbility(DefenderAbility.getInstance());
// When Cathedral Membrane dies during combat, it deals 6 damage to each creature it blocked this combat. // When Cathedral Membrane dies during combat, it deals 6 damage to each creature it blocked this combat.

View file

@ -50,7 +50,7 @@ public class CorrosiveGale extends CardImpl {
} }
public CorrosiveGale(UUID ownerId, CardSetInfo setInfo) { public CorrosiveGale(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{GP}"); super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{G/P}");
this.getSpellAbility().addEffect(new DamageAllEffect(new ManacostVariableValue(), filter)); this.getSpellAbility().addEffect(new DamageAllEffect(new ManacostVariableValue(), filter));

View file

@ -44,7 +44,7 @@ import mage.target.common.TargetCreaturePermanent;
public class Dismember extends CardImpl { public class Dismember extends CardImpl {
public Dismember (UUID ownerId, CardSetInfo setInfo) { public Dismember (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{BP}{BP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{B/P}{B/P}");
// Target creature gets -5/-5 until end of turn. // Target creature gets -5/-5 until end of turn.

View file

@ -42,7 +42,7 @@ import mage.target.TargetPlayer;
public class GitaxianProbe extends CardImpl { public class GitaxianProbe extends CardImpl {
public GitaxianProbe(UUID ownerId, CardSetInfo setInfo) { public GitaxianProbe(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{UP}"); super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{U/P}");
// Look at target player's hand. // Look at target player's hand.
this.getSpellAbility().addEffect(new LookAtTargetPlayerHandEffect()); this.getSpellAbility().addEffect(new LookAtTargetPlayerHandEffect());

View file

@ -41,7 +41,7 @@ import mage.target.common.TargetCreatureOrPlayer;
public class GutShot extends CardImpl { public class GutShot extends CardImpl {
public GutShot(UUID ownerId, CardSetInfo setInfo) { public GutShot(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{RP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{R/P}");
this.getSpellAbility().addEffect(new DamageTargetEffect(1)); this.getSpellAbility().addEffect(new DamageTargetEffect(1));
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());

View file

@ -58,8 +58,8 @@ public class HexParasite extends CardImpl {
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// {X}{BP}: Remove up to X counters from target permanent. For each counter removed this way, Hex Parasite gets +1/+0 until end of turn. // {X}{B/P}: Remove up to X counters from target permanent. For each counter removed this way, Hex Parasite gets +1/+0 until end of turn.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HexParasiteEffect(), new ManaCostsImpl("{X}{BP}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HexParasiteEffect(), new ManaCostsImpl("{X}{B/P}"));
ability.addTarget(new TargetPermanent()); ability.addTarget(new TargetPermanent());
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -64,7 +64,7 @@ public class Lashwrithe extends CardImpl {
this.addAbility(new LivingWeaponAbility()); this.addAbility(new LivingWeaponAbility());
PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter); PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(value, value))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(value, value)));
this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{BP}{BP}"))); this.addAbility(new EquipAbility(Outcome.BoostCreature, new ManaCostsImpl("{B/P}{B/P}")));
} }
public Lashwrithe(final Lashwrithe card) { public Lashwrithe(final Lashwrithe card) {

View file

@ -41,7 +41,7 @@ import mage.filter.common.FilterAttackingCreature;
public class MarrowShards extends CardImpl { public class MarrowShards extends CardImpl {
public MarrowShards(UUID ownerId, CardSetInfo setInfo) { public MarrowShards(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{WP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W/P}");
this.getSpellAbility().addEffect(new DamageAllEffect(1, new FilterAttackingCreature())); this.getSpellAbility().addEffect(new DamageAllEffect(1, new FilterAttackingCreature()));

View file

@ -50,7 +50,7 @@ public class MentalMisstep extends CardImpl {
} }
public MentalMisstep(UUID ownerId, CardSetInfo setInfo) { public MentalMisstep(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{UP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U/P}");
// Counter target spell with converted mana cost 1. // Counter target spell with converted mana cost 1.
this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addEffect(new CounterTargetEffect());

View file

@ -47,7 +47,7 @@ import mage.constants.Zone;
public class MoltensteelDragon extends CardImpl { public class MoltensteelDragon extends CardImpl {
public MoltensteelDragon(UUID ownerId, CardSetInfo setInfo) { public MoltensteelDragon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{RP}{RP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{R/P}{R/P}");
this.subtype.add("Dragon"); this.subtype.add("Dragon");
this.power = new MageInt(4); this.power = new MageInt(4);

View file

@ -43,7 +43,7 @@ import mage.target.common.TargetCreaturePermanent;
public class MutagenicGrowth extends CardImpl { public class MutagenicGrowth extends CardImpl {
public MutagenicGrowth (UUID ownerId, CardSetInfo setInfo) { public MutagenicGrowth (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{GP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G/P}");
this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent());
this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn));

View file

@ -42,11 +42,11 @@ import mage.constants.Zone;
public class NornsAnnex extends CardImpl { public class NornsAnnex extends CardImpl {
public NornsAnnex(UUID ownerId, CardSetInfo setInfo) { public NornsAnnex(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}{WP}{WP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}{W/P}{W/P}");
// {WP} ({WP} can be paid with either or 2 life.) // {W/P} ({W/P} can be paid with either or 2 life.)
// Creatures can't attack you or a planeswalker you control unless their controller pays {WP} for each of those creatures. // Creatures can't attack you or a planeswalker you control unless their controller pays {W/P} for each of those creatures.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl<>("{WP}"), true))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl<>("{W/P}"), true)));
} }
public NornsAnnex(final NornsAnnex card) { public NornsAnnex(final NornsAnnex card) {

View file

@ -42,7 +42,7 @@ import mage.target.common.TargetCardInGraveyard;
public class NoxiousRevival extends CardImpl { public class NoxiousRevival extends CardImpl {
public NoxiousRevival (UUID ownerId, CardSetInfo setInfo) { public NoxiousRevival (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{GP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{G/P}");
this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true)); this.getSpellAbility().addEffect(new PutOnLibraryTargetEffect(true));
this.getSpellAbility().addTarget(new TargetCardInGraveyard()); this.getSpellAbility().addTarget(new TargetCardInGraveyard());

View file

@ -61,7 +61,7 @@ public class PhyrexianMetamorph extends CardImpl {
} }
public PhyrexianMetamorph(UUID ownerId, CardSetInfo setInfo) { public PhyrexianMetamorph(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{UP}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{U/P}");
this.subtype.add("Shapeshifter"); this.subtype.add("Shapeshifter");
this.power = new MageInt(0); this.power = new MageInt(0);
@ -83,7 +83,7 @@ public class PhyrexianMetamorph extends CardImpl {
}; };
// {UP} ( can be paid with either {U} or 2 life.) // {U/P} ( can be paid with either {U} or 2 life.)
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
Effect effect = new CopyPermanentEffect(filter, phyrexianMetamorphApplier); Effect effect = new CopyPermanentEffect(filter, phyrexianMetamorphApplier);
effect.setText("You may have {this} enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types"); effect.setText("You may have {this} enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types");

View file

@ -44,7 +44,7 @@ import mage.target.common.TargetCreaturePermanent;
public class PithDriller extends CardImpl { public class PithDriller extends CardImpl {
public PithDriller(UUID ownerId, CardSetInfo setInfo) { public PithDriller(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{BP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{B/P}");
this.subtype.add("Horror"); this.subtype.add("Horror");
this.power = new MageInt(2); this.power = new MageInt(2);

View file

@ -41,7 +41,7 @@ import mage.cards.CardSetInfo;
public class PorcelainLegionnaire extends CardImpl { public class PorcelainLegionnaire extends CardImpl {
public PorcelainLegionnaire(UUID ownerId, CardSetInfo setInfo) { public PorcelainLegionnaire(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{WP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{W/P}");
this.subtype.add("Soldier"); this.subtype.add("Soldier");
this.power = new MageInt(3); this.power = new MageInt(3);

View file

@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget;
public class PostmortemLunge extends CardImpl { public class PostmortemLunge extends CardImpl {
public PostmortemLunge(UUID ownerId, CardSetInfo setInfo) { public PostmortemLunge(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{BP}"); super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{B/P}");
// Return target creature card with converted mana cost X from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step. // Return target creature card with converted mana cost X from your graveyard to the battlefield. It gains haste. Exile it at the beginning of the next end step.
this.getSpellAbility().addEffect(new PostmortemLungeEffect()); this.getSpellAbility().addEffect(new PostmortemLungeEffect());

View file

@ -49,7 +49,7 @@ import mage.target.common.TargetCreatureOrPlayer;
public class RageExtractor extends CardImpl { public class RageExtractor extends CardImpl {
public RageExtractor(UUID ownerId, CardSetInfo setInfo) { public RageExtractor(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}{RP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{4}{R/P}");
this.addAbility(new RageExtractorTriggeredAbility()); this.addAbility(new RageExtractorTriggeredAbility());

View file

@ -46,7 +46,7 @@ import mage.game.permanent.Permanent;
public class RuthlessInvasion extends CardImpl { public class RuthlessInvasion extends CardImpl {
public RuthlessInvasion (UUID ownerId, CardSetInfo setInfo) { public RuthlessInvasion (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{RP}"); super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R/P}");
this.getSpellAbility().addEffect(new RuthlessInvasionEffect()); this.getSpellAbility().addEffect(new RuthlessInvasionEffect());
} }

View file

@ -41,7 +41,7 @@ import mage.cards.CardSetInfo;
public class SlashPanther extends CardImpl { public class SlashPanther extends CardImpl {
public SlashPanther(UUID ownerId, CardSetInfo setInfo) { public SlashPanther(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{RP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{R/P}");
this.subtype.add("Cat"); this.subtype.add("Cat");
this.power = new MageInt(4); this.power = new MageInt(4);

View file

@ -61,8 +61,8 @@ public class Spellskite extends CardImpl {
this.power = new MageInt(0); this.power = new MageInt(0);
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// {UP}: Change a target of target spell or ability to Spellskite. // {U/P}: Change a target of target spell or ability to Spellskite.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpellskiteEffect(), new ManaCostsImpl("{UP}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpellskiteEffect(), new ManaCostsImpl("{U/P}"));
ability.addTarget(new TargetStackObject()); ability.addTarget(new TargetStackObject());
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -41,7 +41,7 @@ import mage.cards.CardSetInfo;
public class SpinedThopter extends CardImpl { public class SpinedThopter extends CardImpl {
public SpinedThopter(UUID ownerId, CardSetInfo setInfo) { public SpinedThopter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{UP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}{U/P}");
this.subtype.add("Thopter"); this.subtype.add("Thopter");
this.power = new MageInt(2); this.power = new MageInt(2);

View file

@ -63,7 +63,7 @@ public class SurgicalExtraction extends CardImpl {
} }
public SurgicalExtraction(UUID ownerId, CardSetInfo setInfo) { public SurgicalExtraction(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{BP}"); super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B/P}");
// Choose target card in a graveyard other than a basic land card. Search its owner's graveyard, // Choose target card in a graveyard other than a basic land card. Search its owner's graveyard,
// hand, and library for any number of cards with the same name as that card and exile them. // hand, and library for any number of cards with the same name as that card and exile them.

View file

@ -41,7 +41,7 @@ import mage.cards.CardSetInfo;
public class TezzeretsGambit extends CardImpl { public class TezzeretsGambit extends CardImpl {
public TezzeretsGambit(UUID ownerId, CardSetInfo setInfo) { public TezzeretsGambit(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{UP}"); super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U/P}");
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2));
this.getSpellAbility().addEffect(new ProliferateEffect()); this.getSpellAbility().addEffect(new ProliferateEffect());

View file

@ -41,7 +41,7 @@ import mage.cards.CardSetInfo;
public class ThunderingTanadon extends CardImpl { public class ThunderingTanadon extends CardImpl {
public ThunderingTanadon(UUID ownerId, CardSetInfo setInfo) { public ThunderingTanadon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{GP}{GP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{4}{G/P}{G/P}");
this.subtype.add("Beast"); this.subtype.add("Beast");
this.power = new MageInt(5); this.power = new MageInt(5);

View file

@ -52,7 +52,7 @@ public class TrespassingSouleater extends CardImpl {
this.power = new MageInt(2); this.power = new MageInt(2);
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// {UP}: Trespassing Souleater can't be blocked this turn. // {U/P}: Trespassing Souleater can't be blocked this turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
new CantBeBlockedSourceEffect(Duration.EndOfTurn), new CantBeBlockedSourceEffect(Duration.EndOfTurn),
new PhyrexianManaCost(ColoredManaSymbol.U))); new PhyrexianManaCost(ColoredManaSymbol.U)));

View file

@ -42,7 +42,7 @@ import mage.cards.CardSetInfo;
public class VaultSkirge extends CardImpl { public class VaultSkirge extends CardImpl {
public VaultSkirge(UUID ownerId, CardSetInfo setInfo) { public VaultSkirge(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{1}{BP}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{1}{B/P}");
this.subtype.add("Imp"); this.subtype.add("Imp");
this.power = new MageInt(1); this.power = new MageInt(1);

View file

@ -51,10 +51,10 @@ public class HideousEndTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Plains"); addCard(Zone.BATTLEFIELD, playerB, "Plains");
addCard(Zone.BATTLEFIELD, playerB, "Copper Myr"); addCard(Zone.BATTLEFIELD, playerB, "Copper Myr");
// Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn. // Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn.
addCard(Zone.HAND, playerB, "Apostle's Blessing"); addCard(Zone.HAND, playerB, "Blessed Breath");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hideous End", "Copper Myr"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hideous End", "Copper Myr");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Apostle's Blessing", "Copper Myr"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Blessed Breath", "Copper Myr");
setChoice(playerB, "Black"); setChoice(playerB, "Black");
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);

View file

@ -88,7 +88,7 @@ public class MimicVatTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1); addCard(Zone.BATTLEFIELD, playerA, "Phyrexian Vault", 1);
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph", 1);// Creature {3}{UP} addCard(Zone.HAND, playerA, "Phyrexian Metamorph", 1);// Creature {3}{U/P}
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);

View file

@ -44,7 +44,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{U/P}
addCard(Zone.HAND, playerA, "Cloudshift"); addCard(Zone.HAND, playerA, "Cloudshift");
//Flying //Flying
@ -64,7 +64,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();
assertLife(playerA, 24); assertLife(playerA, 22);
assertLife(playerB, 20); assertLife(playerB, 20);
assertGraveyardCount(playerA, "Cloudshift", 1); assertGraveyardCount(playerA, "Cloudshift", 1);
@ -87,7 +87,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{U/P}
// Flying // Flying
// When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control. // When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control.
@ -109,7 +109,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
setStopAt(3, PhaseStep.END_COMBAT); setStopAt(3, PhaseStep.END_COMBAT);
execute(); execute();
assertLife(playerA, 20); assertLife(playerA, 18);
assertLife(playerB, 18); assertLife(playerB, 18);
assertPermanentCount(playerA, "Ponyback Brigade", 1); assertPermanentCount(playerA, "Ponyback Brigade", 1);
@ -128,7 +128,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{U/P}
addCard(Zone.BATTLEFIELD, playerB, "Alloy Myr", 1); addCard(Zone.BATTLEFIELD, playerB, "Alloy Myr", 1);
addCard(Zone.BATTLEFIELD, playerB, "Kitesail", 1); addCard(Zone.BATTLEFIELD, playerB, "Kitesail", 1);

View file

@ -57,7 +57,7 @@ public class CostModificationTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
// Look at target player's hand. // Look at target player's hand.
// Draw a card. // Draw a card.
addCard(Zone.HAND, playerB, "Gitaxian Probe"); // Sorcery {UP} addCard(Zone.HAND, playerB, "Gitaxian Probe"); // Sorcery {U/P}
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Gitaxian Probe", playerA); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Gitaxian Probe", playerA);
setStopAt(2, PhaseStep.BEGIN_COMBAT); setStopAt(2, PhaseStep.BEGIN_COMBAT);

View file

@ -1,111 +0,0 @@
package org.mage.test.cards.single;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Created by goesta on 12/02/2017. Modified by jeffwadsworth
*/
public class SpellskiteTest extends CardTestPlayerBase {
@Test
public void testThatSpellSkiteCantBeTargetedTwiceOrMore() {
/* According to rules, the same object can be a legal target only
once for each instances of the word target in the text
of a spell or ability. In this case, the target can't be changed
due to Spellskite already being a target.
*/
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Spellskite");
addCard(Zone.BATTLEFIELD, playerB, "Scute Mob");
addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Fiery Justice");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Justice");
addTarget(playerA, "Scute Mob");
setChoice(playerA, "X=1");
addTarget(playerA, "Spellskite");
setChoice(playerA, "X=4");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Fiery Justice", "Fiery Justice");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 2);
}
public void testThatSplitDamageCanGetRedirected() {
/* Standard redirect test
The Spellskite should die from the 5 damage that was redirected to it
*/
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Spellskite");// 0/4 creature
addCard(Zone.BATTLEFIELD, playerB, "Scute Mob"); // 1/1 creauture
addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Fiery Justice");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Justice"); // 5 damage distributed to any number of targets
addTarget(playerA, "Scute Mob");
setChoice(playerA, "X=5");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Fiery Justice", "Fiery Justice");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 1);
assertPermanentCount(playerB, "Scute Mob", 1);
}
public void testThatSplitDamageGetsRedirectedFromTheCorrectChoice() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Spellskite");// 0/4 creature
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); // 1/1 creauture
addCard(Zone.BATTLEFIELD, playerB, "Royal Assassin");
addCard(Zone.BATTLEFIELD, playerB, "Blinking Spirit");
addCard(Zone.BATTLEFIELD, playerB, "Pearled Unicorn");
addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Fiery Justice");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Justice"); // 5 damage distributed to any number of targets
addTarget(playerA, "Memnite");
setChoice(playerA, "X=1");
addTarget(playerA, "Royal Assassin");
setChoice(playerA, "X=1");
addTarget(playerA, "Blinking Spirit");
setChoice(playerA, "X=1");
addTarget(playerA, "Pearled Unicorn");
setChoice(playerA, "X=2");//the unicorn deserves it
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Fiery Justice", "Fiery Justice");
setChoice(playerA, "No");
setChoice(playerA, "No");
setChoice(playerA, "No");
setChoice(playerA, "Yes"); //of course
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 3);
assertPermanentCount(playerB, "Pearled Unicorn", 1);//it lives on
assertPowerToughness(playerB, "Spellskite", 0, 2);
}
}

View file

@ -52,13 +52,13 @@ public class BecomesTheTargetTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{WP},{T}: Tap target creature", "Silvercoat Lion"); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{W/P},{T}: Tap target creature", "Silvercoat Lion");
setStopAt(2, PhaseStep.BEGIN_COMBAT); setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertLife(playerA, 20); assertLife(playerA, 20);
assertLife(playerB, 20); assertLife(playerB, 18);
assertPermanentCount(playerA, "Silvercoat Lion", 0); assertPermanentCount(playerA, "Silvercoat Lion", 0);
assertTapped("Silvercoat Lion", true); assertTapped("Silvercoat Lion", true);

View file

@ -157,7 +157,7 @@ public class SacredGroundTest extends CardTestPlayerBase {
// Choose target card in a graveyard other than a basic land card. Search its owner's graveyard, // Choose target card in a graveyard other than a basic land card. Search its owner's graveyard,
// hand, and library for any number of cards with the same name as that card and exile them. // hand, and library for any number of cards with the same name as that card and exile them.
// Then that player shuffles his or her library. // Then that player shuffles his or her library.
addCard(Zone.HAND, playerA, "Surgical Extraction"); // Instant {BP} addCard(Zone.HAND, playerA, "Surgical Extraction"); // Instant {B/P}
addCard(Zone.BATTLEFIELD, playerB, "Caves of Koilos", 1); addCard(Zone.BATTLEFIELD, playerB, "Caves of Koilos", 1);
/** /**
@ -177,7 +177,7 @@ public class SacredGroundTest extends CardTestPlayerBase {
assertGraveyardCount(playerA, "Surgical Extraction", 1); assertGraveyardCount(playerA, "Surgical Extraction", 1);
assertExileCount("Caves of Koilos", 1); assertExileCount("Caves of Koilos", 1);
assertLife(playerA, 20); assertLife(playerA, 18);
assertLife(playerB, 18); assertLife(playerB, 18);
} }

View file

@ -52,7 +52,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Lightning Bolt"); addCard(Zone.HAND, playerA, "Lightning Bolt");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{UP}: Change a target of target spell or ability to {this}.", "Lightning Bolt"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{U/P}: Change a target of target spell or ability to {this}.", "Lightning Bolt");
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
@ -60,7 +60,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
assertGraveyardCount(playerA, "Lightning Bolt", 1); assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertPermanentCount(playerA, "Spellskite", 1); assertPermanentCount(playerA, "Spellskite", 1);
assertLife(playerA, 20); assertLife(playerA, 18);
assertLife(playerB, 20); assertLife(playerB, 20);
assertPowerToughness(playerA, "Spellskite", 3, 7); assertPowerToughness(playerA, "Spellskite", 3, 7);
@ -90,7 +90,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Vedalken Shackles", 1); addCard(Zone.BATTLEFIELD, playerA, "Vedalken Shackles", 1);
addCard(Zone.BATTLEFIELD, playerB, "Island", 6); addCard(Zone.BATTLEFIELD, playerB, "Island", 6);
// {UP}: Change a target of target spell or ability to Spellskite. // {U/P}: Change a target of target spell or ability to Spellskite.
addCard(Zone.BATTLEFIELD, playerB, "Spellskite", 1); addCard(Zone.BATTLEFIELD, playerB, "Spellskite", 1);
// {4}{U}{U} // {4}{U}{U}
// Whenever Frost Titan becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays 2. // Whenever Frost Titan becomes the target of a spell or ability an opponent controls, counter that spell or ability unless its controller pays 2.
@ -102,7 +102,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Frost Titan"); castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Frost Titan");
addTarget(playerB, "Silvercoat Lion"); addTarget(playerB, "Silvercoat Lion");
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{UP}: Change a target", "stack ability (Whenever {this} enters "); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{U/P}: Change a target", "stack ability (Whenever {this} enters ");
setStopAt(2, PhaseStep.BEGIN_COMBAT); setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute(); execute();
@ -141,7 +141,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
setModeChoice(playerA, "1"); // Counter target spell setModeChoice(playerA, "1"); // Counter target spell
setModeChoice(playerA, "2"); // return target permanent to its owner's hand setModeChoice(playerA, "2"); // return target permanent to its owner's hand
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Cryptic Command", "Cryptic Command"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{U/P}: Change a target of target spell or ability to {this}.", "Cryptic Command", "Cryptic Command");
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
@ -154,7 +154,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Silvercoat Lion", 1); assertPermanentCount(playerB, "Silvercoat Lion", 1);
assertLife(playerA, 20); assertLife(playerA, 20);
assertLife(playerB, 20); assertLife(playerB, 18);
} }
@ -182,7 +182,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
setModeChoice(playerA, "2"); // return target permanent to its owner's hand setModeChoice(playerA, "2"); // return target permanent to its owner's hand
setModeChoice(playerA, "3"); // tap all creatures your opponents control setModeChoice(playerA, "3"); // tap all creatures your opponents control
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Cryptic Command"); activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{U/P}: Change a target of target spell or ability to {this}.", "Cryptic Command");
setStopAt(2, PhaseStep.BEGIN_COMBAT); setStopAt(2, PhaseStep.BEGIN_COMBAT);
execute(); execute();
@ -194,7 +194,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
assertTapped("Silvercoat Lion", true); assertTapped("Silvercoat Lion", true);
assertLife(playerA, 20); assertLife(playerA, 20);
assertLife(playerB, 20); assertLife(playerB, 18);
} }
@ -214,7 +214,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Lightning Bolt"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{U/P}: Change a target of target spell or ability to {this}.", "Lightning Bolt");
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
@ -222,7 +222,7 @@ public class SpellskiteTest extends CardTestPlayerBase {
assertGraveyardCount(playerA, "Lightning Bolt", 1); assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertLife(playerA, 20); assertLife(playerA, 20);
assertLife(playerB, 20); assertLife(playerB, 18);
} }
@ -250,4 +250,102 @@ public class SpellskiteTest extends CardTestPlayerBase {
assertPowerToughness(playerB, "Spellskite", 3, 7); assertPowerToughness(playerB, "Spellskite", 3, 7);
} }
@Test
public void testThatSpellSkiteCantBeTargetedTwiceOrMore() {
/* According to rules, the same object can be a legal target only
once for each instances of the word target in the text
of a spell or ability. In this case, the target can't be changed
due to Spellskite already being a target.
*/
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Spellskite");
addCard(Zone.BATTLEFIELD, playerB, "Scute Mob");
addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Fiery Justice");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Justice");
addTarget(playerA, "Scute Mob");
setChoice(playerA, "X=1");
addTarget(playerA, "Spellskite");
setChoice(playerA, "X=4");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Fiery Justice", "Fiery Justice");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 2);
}
public void testThatSplitDamageCanGetRedirected() {
/* Standard redirect test
The Spellskite should die from the 5 damage that was redirected to it
*/
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Spellskite");// 0/4 creature
addCard(Zone.BATTLEFIELD, playerB, "Scute Mob"); // 1/1 creauture
addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Fiery Justice");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Justice"); // 5 damage distributed to any number of targets
addTarget(playerA, "Scute Mob");
setChoice(playerA, "X=5");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Fiery Justice", "Fiery Justice");
setChoice(playerA, "Yes");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 1);
assertPermanentCount(playerB, "Scute Mob", 1);
}
public void testThatSplitDamageGetsRedirectedFromTheCorrectChoice() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerB, "Spellskite");// 0/4 creature
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); // 1/1 creauture
addCard(Zone.BATTLEFIELD, playerB, "Royal Assassin");
addCard(Zone.BATTLEFIELD, playerB, "Blinking Spirit");
addCard(Zone.BATTLEFIELD, playerB, "Pearled Unicorn");
addCard(Zone.BATTLEFIELD, playerB, "Island");
addCard(Zone.HAND, playerA, "Fiery Justice");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiery Justice"); // 5 damage distributed to any number of targets
addTarget(playerA, "Memnite");
setChoice(playerA, "X=1");
addTarget(playerA, "Royal Assassin");
setChoice(playerA, "X=1");
addTarget(playerA, "Blinking Spirit");
setChoice(playerA, "X=1");
addTarget(playerA, "Pearled Unicorn");
setChoice(playerA, "X=2");//the unicorn deserves it
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{UP}: Change a target of target spell or ability to {this}.", "Fiery Justice", "Fiery Justice");
setChoice(playerA, "No");
setChoice(playerA, "No");
setChoice(playerA, "No");
setChoice(playerA, "Yes"); //of course
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerB, 3);
assertPermanentCount(playerB, "Pearled Unicorn", 1);//it lives on
assertPowerToughness(playerB, "Spellskite", 0, 2);
}
} }

View file

@ -18,7 +18,7 @@ public class PostMortemLungeTest extends CardTestPlayerBase {
public void testExilesCreatureAtEndStep() { public void testExilesCreatureAtEndStep() {
/* /*
{P}{X} - Sorcery {X}{B/P} - Sorcery
Return target creature card with converted mana cost X from your graveyard to the battlefield. Return target creature card with converted mana cost X from your graveyard to the battlefield.
It gains haste. Exile it at the beginning of the next end step. It gains haste. Exile it at the beginning of the next end step.
*/ */

View file

@ -43,7 +43,7 @@ public class ThragtuskTest extends CardTestPlayerBase {
/** /**
* Test if a Thragtusk is copied by a PhyrexianMetamorph that both triggers * Test if a Thragtusk is copied by a PhyrexianMetamorph that both triggers
* cotrrect work * correct work
*/ */
@Test @Test
public void testPhyrexianMetamorph() { public void testPhyrexianMetamorph() {
@ -69,7 +69,7 @@ public class ThragtuskTest extends CardTestPlayerBase {
assertGraveyardCount(playerA, "Phyrexian Metamorph", 1); assertGraveyardCount(playerA, "Phyrexian Metamorph", 1);
assertGraveyardCount(playerB, "Public Execution", 1); assertGraveyardCount(playerB, "Public Execution", 1);
assertLife(playerA, 25); assertLife(playerA, 23);
assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start
assertPermanentCount(playerA, "Beast", 1); assertPermanentCount(playerA, "Beast", 1);
@ -116,7 +116,7 @@ public class ThragtuskTest extends CardTestPlayerBase {
assertGraveyardCount(playerA, "Phyrexian Metamorph", 1); assertGraveyardCount(playerA, "Phyrexian Metamorph", 1);
assertGraveyardCount(playerB, "Public Execution", 1); assertGraveyardCount(playerB, "Public Execution", 1);
assertLife(playerA, 25); assertLife(playerA, 23);
assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start assertLife(playerB, 20); // Thragtusk ETB ability does not trigger if set to battlefield on test game start
assertPermanentCount(playerA, "Beast", 0); assertPermanentCount(playerA, "Beast", 0);

View file

@ -31,11 +31,9 @@ import mage.MageObject;
import mage.MageObjectReference; import mage.MageObjectReference;
import mage.Mana; import mage.Mana;
import mage.abilities.costs.*; import mage.abilities.costs.*;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.*;
import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.Effects; import mage.abilities.effects.Effects;
@ -62,6 +60,7 @@ import mage.watchers.Watcher;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -298,6 +297,9 @@ public abstract class AbilityImpl implements Ability {
&& game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()), this)) { && game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()), this)) {
return false; return false;
} }
handlePhyrexianManaCosts(game, sourceId, controller);
for (UUID modeId : this.getModes().getSelectedModes()) { for (UUID modeId : this.getModes().getSelectedModes()) {
this.getModes().setActiveMode(modeId); this.getModes().setActiveMode(modeId);
//20121001 - 601.2c //20121001 - 601.2c
@ -503,6 +505,27 @@ public abstract class AbilityImpl implements Ability {
return announceString.toString(); return announceString.toString();
} }
/**
* 601.2b
* If a cost that will be paid as the spell is being cast includes Phyrexian mana symbols,
* the player announces whether he or she intends to pay 2 life or the corresponding colored mana cost for each of those symbols.
*/
private void handlePhyrexianManaCosts(Game game, UUID sourceId, Player controller) {
Iterator<ManaCost> costIterator = manaCostsToPay.iterator();
while(costIterator.hasNext()) {
ManaCost cost = costIterator.next();
if(cost instanceof PhyrexianManaCost) {
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost)cost;
PayLifeCost payLifeCost = new PayLifeCost(2);
if(payLifeCost.canPay(this, sourceId, controller.getId(), game) &&
controller.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + "?", this, game)) {
costIterator.remove();
costs.add(payLifeCost);
}
}
}
}
/** /**
* Handles X mana costs and sets manaCostsToPay. * Handles X mana costs and sets manaCostsToPay.
* *

View file

@ -50,34 +50,20 @@ public class PhyrexianManaCost extends ColoredManaCost {
super(manaCost); super(manaCost);
} }
@Override
public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) {
assignColored(ability, game, pool, this.mana, costToPay);
}
@Override @Override
public String getText() { public String getText() {
return '{' + mana.toString() + "P}"; return '{' + mana.toString() + "/P}";
}
public String getBaseText() {
return super.getText();
} }
@Override @Override
public PhyrexianManaCost getUnpaid() { public ColoredManaCost getUnpaid() {
return this; return new ColoredManaCost(this);
} }
@Override
public boolean canPay(Ability ability, UUID sourceId, UUID controllerId, Game game) {
if (!game.getPlayer(controllerId).isLifeTotalCanChange()) {
return false;
}
return game.getPlayer(controllerId).getLife() >= 2;
}
@Override
public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) {
this.paid = game.getPlayer(controllerId).loseLife(2, game, false) == 2;
return paid;
}
@Override @Override
public PhyrexianManaCost copy() { public PhyrexianManaCost copy() {

View file

@ -29,8 +29,13 @@ package mage.abilities.effects;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.costs.Cost; import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCosts;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.mana.PhyrexianManaCost;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.game.Game; import mage.game.Game;
@ -38,6 +43,9 @@ import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent.EventType;
import mage.players.Player; import mage.players.Player;
import java.util.Iterator;
import java.util.List;
/** /**
* *
* @author LevelX2 * @author LevelX2
@ -141,6 +149,7 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm
attackBlockManaTax.clearPaid(); attackBlockManaTax.clearPaid();
if (attackBlockManaTax.canPay(source, source.getSourceId(), player.getId(), game) if (attackBlockManaTax.canPay(source, source.getSourceId(), player.getId(), game)
&& player.chooseUse(Outcome.Neutral, chooseText, source, game)) { && player.chooseUse(Outcome.Neutral, chooseText, source, game)) {
handlePhyrexianManaCosts(manaCosts, player, source, game);
if (attackBlockManaTax instanceof ManaCostsImpl) { if (attackBlockManaTax instanceof ManaCostsImpl) {
if (attackBlockManaTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) { if (attackBlockManaTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) {
return false; return false;
@ -152,6 +161,27 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm
return false; return false;
} }
private void handlePhyrexianManaCosts(ManaCosts<ManaCost> manaCosts, Player player, Ability source, Game game) {
Iterator<ManaCost> manaCostIterator = manaCosts.iterator();
Costs<PayLifeCost> costs = new CostsImpl<>();
while(manaCostIterator.hasNext()) {
ManaCost manaCost = manaCostIterator.next();
if(manaCost instanceof PhyrexianManaCost) {
PhyrexianManaCost phyrexianManaCost = (PhyrexianManaCost)manaCost;
PayLifeCost payLifeCost = new PayLifeCost(2);
if(payLifeCost.canPay(source, source.getSourceId(), player.getId(), game) &&
player.chooseUse(Outcome.LoseLife, "Pay 2 life instead of " + phyrexianManaCost.getBaseText() + "?", source, game)) {
manaCostIterator.remove();
costs.add(payLifeCost);
}
}
}
costs.pay(source, game, source.getSourceId(), player.getId(), false, null);
}
private boolean handleOtherCosts(Cost attackBlockOtherTax, GameEvent event, Ability source, Game game) { private boolean handleOtherCosts(Cost attackBlockOtherTax, GameEvent event, Ability source, Game game) {
Player player = game.getPlayer(event.getPlayerId()); Player player = game.getPlayer(event.getPlayerId());
if (player != null) { if (player != null) {

View file

@ -2438,7 +2438,9 @@ public abstract class PlayerImpl implements Player, Serializable {
if (!copy.canActivate(playerId, game)) { if (!copy.canActivate(playerId, game)) {
return false; return false;
} }
if(available != null) {
game.getContinuousEffects().costModification(copy, game); game.getContinuousEffects().costModification(copy, game);
}
Card card = game.getCard(ability.getSourceId()); Card card = game.getCard(ability.getSourceId());
if (card != null) { if (card != null) {