From ce1f4a3bf8e04bf2683362846116f4368cf179fd Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Wed, 8 Mar 2017 20:03:28 -0800 Subject: [PATCH] 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. --- .../main/java/org/mage/card/arcane/UI.java | 1 - .../src/mage/player/human/HumanPlayer.java | 17 +-- .../src/mage/cards/a/ActOfAggression.java | 2 +- .../src/mage/cards/a/ApostlesBlessing.java | 4 +- Mage.Sets/src/mage/cards/b/BirthingPod.java | 6 +- .../src/mage/cards/b/BlindingSouleater.java | 2 +- .../src/mage/cards/c/CathedralMembrane.java | 4 +- Mage.Sets/src/mage/cards/c/CorrosiveGale.java | 2 +- Mage.Sets/src/mage/cards/d/Dismember.java | 2 +- Mage.Sets/src/mage/cards/g/GitaxianProbe.java | 2 +- Mage.Sets/src/mage/cards/g/GutShot.java | 2 +- Mage.Sets/src/mage/cards/h/HexParasite.java | 4 +- Mage.Sets/src/mage/cards/l/Lashwrithe.java | 2 +- Mage.Sets/src/mage/cards/m/MarrowShards.java | 2 +- Mage.Sets/src/mage/cards/m/MentalMisstep.java | 2 +- .../src/mage/cards/m/MoltensteelDragon.java | 2 +- .../src/mage/cards/m/MutagenicGrowth.java | 2 +- Mage.Sets/src/mage/cards/n/NornsAnnex.java | 8 +- .../src/mage/cards/n/NoxiousRevival.java | 2 +- .../src/mage/cards/p/PhyrexianMetamorph.java | 4 +- Mage.Sets/src/mage/cards/p/PithDriller.java | 2 +- .../mage/cards/p/PorcelainLegionnaire.java | 2 +- .../src/mage/cards/p/PostmortemLunge.java | 2 +- Mage.Sets/src/mage/cards/r/RageExtractor.java | 2 +- .../src/mage/cards/r/RuthlessInvasion.java | 2 +- Mage.Sets/src/mage/cards/s/SlashPanther.java | 2 +- Mage.Sets/src/mage/cards/s/Spellskite.java | 4 +- Mage.Sets/src/mage/cards/s/SpinedThopter.java | 2 +- .../src/mage/cards/s/SurgicalExtraction.java | 2 +- .../src/mage/cards/t/TezzeretsGambit.java | 2 +- .../src/mage/cards/t/ThunderingTanadon.java | 2 +- .../mage/cards/t/TrespassingSouleater.java | 2 +- Mage.Sets/src/mage/cards/v/VaultSkirge.java | 2 +- .../oneshot/destroy/HideousEndTest.java | 4 +- .../mage/test/cards/copy/MimicVatTest.java | 2 +- .../cards/copy/PhyrexianMetamorphTest.java | 10 +- .../modification/CostModificationTest.java | 2 +- .../test/cards/single/SpellskiteTest.java | 111 ---------------- .../cards/triggers/BecomesTheTargetTest.java | 4 +- .../test/cards/triggers/SacredGroundTest.java | 4 +- .../test/cards/triggers/SpellskiteTest.java | 118 ++++++++++++++++-- .../triggers/delayed/PostMortemLungeTest.java | 2 +- .../cards/triggers/dies/ThragtuskTest.java | 6 +- .../main/java/mage/abilities/AbilityImpl.java | 31 ++++- .../costs/mana/PhyrexianManaCost.java | 28 ++--- .../PayCostToAttackBlockEffectImpl.java | 30 +++++ .../main/java/mage/players/PlayerImpl.java | 4 +- 47 files changed, 234 insertions(+), 222 deletions(-) delete mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/SpellskiteTest.java diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/UI.java b/Mage.Client/src/main/java/org/mage/card/arcane/UI.java index e701cce8a03..0b2ed25f00f 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/UI.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/UI.java @@ -148,7 +148,6 @@ public final class UI { } 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". int pipePosition = manaCost.indexOf("{|}"); if (pipePosition != -1) { diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 6e1974efe58..6fe57b2d0a0 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -823,9 +823,6 @@ public class HumanPlayer extends PlayerImpl { protected boolean playManaHandling(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) { updateGameStatePriority("playMana", game); Map options = new HashMap<>(); - if (unpaid.getText().contains("P}")) { - options.put(Constants.Option.SPECIAL_BUTTON, (Serializable) "Pay 2 life"); - } game.firePlayManaEvent(playerId, "Pay " + promptText, options); waitForResponse(game); if (!this.canRespond()) { @@ -838,18 +835,6 @@ public class HumanPlayer extends PlayerImpl { } else if (response.getString() != null && response.getString().equals("special")) { if (unpaid instanceof ManaCostsImpl) { specialManaAction(unpaid, game); - // TODO: delve or convoke cards with PhyrexianManaCost won't work together (this combinaton does not exist yet) - @SuppressWarnings("unchecked") - ManaCostsImpl costs = (ManaCostsImpl) 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) { // 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()); 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; } Zone zone = game.getState().getZone(object.getId()); diff --git a/Mage.Sets/src/mage/cards/a/ActOfAggression.java b/Mage.Sets/src/mage/cards/a/ActOfAggression.java index dbfbafa4e18..f8e9df80eed 100644 --- a/Mage.Sets/src/mage/cards/a/ActOfAggression.java +++ b/Mage.Sets/src/mage/cards/a/ActOfAggression.java @@ -54,7 +54,7 @@ public class ActOfAggression extends CardImpl { } 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().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); diff --git a/Mage.Sets/src/mage/cards/a/ApostlesBlessing.java b/Mage.Sets/src/mage/cards/a/ApostlesBlessing.java index e6039af9c10..78d95a968c9 100644 --- a/Mage.Sets/src/mage/cards/a/ApostlesBlessing.java +++ b/Mage.Sets/src/mage/cards/a/ApostlesBlessing.java @@ -63,9 +63,9 @@ public class ApostlesBlessing extends CardImpl { } 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. this.getSpellAbility().addEffect(new ApostlesBlessingEffect()); this.getSpellAbility().addTarget(new TargetControlledPermanent(filter)); diff --git a/Mage.Sets/src/mage/cards/b/BirthingPod.java b/Mage.Sets/src/mage/cards/b/BirthingPod.java index 2ede7fdac70..79db6507537 100644 --- a/Mage.Sets/src/mage/cards/b/BirthingPod.java +++ b/Mage.Sets/src/mage/cards/b/BirthingPod.java @@ -57,11 +57,11 @@ import mage.target.common.TargetControlledCreaturePermanent; public class BirthingPod extends CardImpl { 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. - 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 SacrificeTargetCost(new TargetControlledCreaturePermanent())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/BlindingSouleater.java b/Mage.Sets/src/mage/cards/b/BlindingSouleater.java index c77c476e5d8..5cd65289f26 100644 --- a/Mage.Sets/src/mage/cards/b/BlindingSouleater.java +++ b/Mage.Sets/src/mage/cards/b/BlindingSouleater.java @@ -53,7 +53,7 @@ public class BlindingSouleater extends CardImpl { this.power = new MageInt(1); 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, new TapTargetEffect(), new PhyrexianManaCost(ColoredManaSymbol.W)); diff --git a/Mage.Sets/src/mage/cards/c/CathedralMembrane.java b/Mage.Sets/src/mage/cards/c/CathedralMembrane.java index d464bd56e72..544b9137aa8 100644 --- a/Mage.Sets/src/mage/cards/c/CathedralMembrane.java +++ b/Mage.Sets/src/mage/cards/c/CathedralMembrane.java @@ -51,13 +51,13 @@ import java.util.UUID; public class CathedralMembrane extends CardImpl { 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.power = new MageInt(0); this.toughness = new MageInt(3); - // ({WP} can be paid with either {W} or 2 life.) + // ({W/P} can be paid with either {W} or 2 life.) this.addAbility(DefenderAbility.getInstance()); // When Cathedral Membrane dies during combat, it deals 6 damage to each creature it blocked this combat. diff --git a/Mage.Sets/src/mage/cards/c/CorrosiveGale.java b/Mage.Sets/src/mage/cards/c/CorrosiveGale.java index d833980c121..40ba94ad44e 100644 --- a/Mage.Sets/src/mage/cards/c/CorrosiveGale.java +++ b/Mage.Sets/src/mage/cards/c/CorrosiveGale.java @@ -50,7 +50,7 @@ public class CorrosiveGale extends CardImpl { } 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)); diff --git a/Mage.Sets/src/mage/cards/d/Dismember.java b/Mage.Sets/src/mage/cards/d/Dismember.java index 3939c51769c..72db3fa5301 100644 --- a/Mage.Sets/src/mage/cards/d/Dismember.java +++ b/Mage.Sets/src/mage/cards/d/Dismember.java @@ -44,7 +44,7 @@ import mage.target.common.TargetCreaturePermanent; public class Dismember extends CardImpl { 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. diff --git a/Mage.Sets/src/mage/cards/g/GitaxianProbe.java b/Mage.Sets/src/mage/cards/g/GitaxianProbe.java index 4e2353124c4..8bf19e2dc5b 100644 --- a/Mage.Sets/src/mage/cards/g/GitaxianProbe.java +++ b/Mage.Sets/src/mage/cards/g/GitaxianProbe.java @@ -42,7 +42,7 @@ import mage.target.TargetPlayer; public class GitaxianProbe extends CardImpl { 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. this.getSpellAbility().addEffect(new LookAtTargetPlayerHandEffect()); diff --git a/Mage.Sets/src/mage/cards/g/GutShot.java b/Mage.Sets/src/mage/cards/g/GutShot.java index 5166e4ce7d1..3fe09ad40e7 100644 --- a/Mage.Sets/src/mage/cards/g/GutShot.java +++ b/Mage.Sets/src/mage/cards/g/GutShot.java @@ -41,7 +41,7 @@ import mage.target.common.TargetCreatureOrPlayer; public class GutShot extends CardImpl { 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().addTarget(new TargetCreatureOrPlayer()); diff --git a/Mage.Sets/src/mage/cards/h/HexParasite.java b/Mage.Sets/src/mage/cards/h/HexParasite.java index 63bb4f7dc6b..e57539bbf17 100644 --- a/Mage.Sets/src/mage/cards/h/HexParasite.java +++ b/Mage.Sets/src/mage/cards/h/HexParasite.java @@ -58,8 +58,8 @@ public class HexParasite extends CardImpl { this.power = 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. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HexParasiteEffect(), new ManaCostsImpl("{X}{BP}")); + // {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}{B/P}")); ability.addTarget(new TargetPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/Lashwrithe.java b/Mage.Sets/src/mage/cards/l/Lashwrithe.java index f1430f0a9d4..ddc499ae161 100644 --- a/Mage.Sets/src/mage/cards/l/Lashwrithe.java +++ b/Mage.Sets/src/mage/cards/l/Lashwrithe.java @@ -64,7 +64,7 @@ public class Lashwrithe extends CardImpl { this.addAbility(new LivingWeaponAbility()); PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(filter); 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) { diff --git a/Mage.Sets/src/mage/cards/m/MarrowShards.java b/Mage.Sets/src/mage/cards/m/MarrowShards.java index 80a5832bb5e..00dd8f4615f 100644 --- a/Mage.Sets/src/mage/cards/m/MarrowShards.java +++ b/Mage.Sets/src/mage/cards/m/MarrowShards.java @@ -41,7 +41,7 @@ import mage.filter.common.FilterAttackingCreature; public class MarrowShards extends CardImpl { 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())); diff --git a/Mage.Sets/src/mage/cards/m/MentalMisstep.java b/Mage.Sets/src/mage/cards/m/MentalMisstep.java index 3c5a86e4e72..ef520c581d1 100644 --- a/Mage.Sets/src/mage/cards/m/MentalMisstep.java +++ b/Mage.Sets/src/mage/cards/m/MentalMisstep.java @@ -50,7 +50,7 @@ public class MentalMisstep extends CardImpl { } 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. this.getSpellAbility().addEffect(new CounterTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java b/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java index 2a5131a39eb..613b525cd68 100644 --- a/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java +++ b/Mage.Sets/src/mage/cards/m/MoltensteelDragon.java @@ -47,7 +47,7 @@ import mage.constants.Zone; public class MoltensteelDragon extends CardImpl { 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.power = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/m/MutagenicGrowth.java b/Mage.Sets/src/mage/cards/m/MutagenicGrowth.java index efa0cfea1d6..dd82416c2f3 100644 --- a/Mage.Sets/src/mage/cards/m/MutagenicGrowth.java +++ b/Mage.Sets/src/mage/cards/m/MutagenicGrowth.java @@ -43,7 +43,7 @@ import mage.target.common.TargetCreaturePermanent; public class MutagenicGrowth extends CardImpl { 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().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); diff --git a/Mage.Sets/src/mage/cards/n/NornsAnnex.java b/Mage.Sets/src/mage/cards/n/NornsAnnex.java index 5ee1fed2c4a..0a72b7b9f8a 100644 --- a/Mage.Sets/src/mage/cards/n/NornsAnnex.java +++ b/Mage.Sets/src/mage/cards/n/NornsAnnex.java @@ -42,11 +42,11 @@ import mage.constants.Zone; public class NornsAnnex extends CardImpl { 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.) - // Creatures can't attack you or a planeswalker you control unless their controller pays {WP} for each of those creatures. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl<>("{WP}"), true))); + // {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 {W/P} for each of those creatures. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackYouUnlessPayManaAllEffect(new ManaCostsImpl<>("{W/P}"), true))); } public NornsAnnex(final NornsAnnex card) { diff --git a/Mage.Sets/src/mage/cards/n/NoxiousRevival.java b/Mage.Sets/src/mage/cards/n/NoxiousRevival.java index 2692fc62dea..d802a6565d3 100644 --- a/Mage.Sets/src/mage/cards/n/NoxiousRevival.java +++ b/Mage.Sets/src/mage/cards/n/NoxiousRevival.java @@ -42,7 +42,7 @@ import mage.target.common.TargetCardInGraveyard; public class NoxiousRevival extends CardImpl { 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().addTarget(new TargetCardInGraveyard()); diff --git a/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java b/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java index 7fa187afc3b..c56fd2e4c78 100644 --- a/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java +++ b/Mage.Sets/src/mage/cards/p/PhyrexianMetamorph.java @@ -61,7 +61,7 @@ public class PhyrexianMetamorph extends CardImpl { } 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.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. 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"); diff --git a/Mage.Sets/src/mage/cards/p/PithDriller.java b/Mage.Sets/src/mage/cards/p/PithDriller.java index 2049cf2f102..6df97e0c212 100644 --- a/Mage.Sets/src/mage/cards/p/PithDriller.java +++ b/Mage.Sets/src/mage/cards/p/PithDriller.java @@ -44,7 +44,7 @@ import mage.target.common.TargetCreaturePermanent; public class PithDriller extends CardImpl { 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.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/p/PorcelainLegionnaire.java b/Mage.Sets/src/mage/cards/p/PorcelainLegionnaire.java index 67fd014fb1e..2ec9cc2350a 100644 --- a/Mage.Sets/src/mage/cards/p/PorcelainLegionnaire.java +++ b/Mage.Sets/src/mage/cards/p/PorcelainLegionnaire.java @@ -41,7 +41,7 @@ import mage.cards.CardSetInfo; public class PorcelainLegionnaire extends CardImpl { 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.power = new MageInt(3); diff --git a/Mage.Sets/src/mage/cards/p/PostmortemLunge.java b/Mage.Sets/src/mage/cards/p/PostmortemLunge.java index fc10cd35b6a..35f22cb0920 100644 --- a/Mage.Sets/src/mage/cards/p/PostmortemLunge.java +++ b/Mage.Sets/src/mage/cards/p/PostmortemLunge.java @@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget; public class PostmortemLunge extends CardImpl { 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. this.getSpellAbility().addEffect(new PostmortemLungeEffect()); diff --git a/Mage.Sets/src/mage/cards/r/RageExtractor.java b/Mage.Sets/src/mage/cards/r/RageExtractor.java index 866c1af8517..6d3f98edd5e 100644 --- a/Mage.Sets/src/mage/cards/r/RageExtractor.java +++ b/Mage.Sets/src/mage/cards/r/RageExtractor.java @@ -49,7 +49,7 @@ import mage.target.common.TargetCreatureOrPlayer; public class RageExtractor extends CardImpl { 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()); diff --git a/Mage.Sets/src/mage/cards/r/RuthlessInvasion.java b/Mage.Sets/src/mage/cards/r/RuthlessInvasion.java index 56c7a95a201..d438d1d5f72 100644 --- a/Mage.Sets/src/mage/cards/r/RuthlessInvasion.java +++ b/Mage.Sets/src/mage/cards/r/RuthlessInvasion.java @@ -46,7 +46,7 @@ import mage.game.permanent.Permanent; public class RuthlessInvasion extends CardImpl { 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()); } diff --git a/Mage.Sets/src/mage/cards/s/SlashPanther.java b/Mage.Sets/src/mage/cards/s/SlashPanther.java index bbadca8d955..6ae24861513 100644 --- a/Mage.Sets/src/mage/cards/s/SlashPanther.java +++ b/Mage.Sets/src/mage/cards/s/SlashPanther.java @@ -41,7 +41,7 @@ import mage.cards.CardSetInfo; public class SlashPanther extends CardImpl { 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.power = new MageInt(4); diff --git a/Mage.Sets/src/mage/cards/s/Spellskite.java b/Mage.Sets/src/mage/cards/s/Spellskite.java index d613df99c60..b0b3ee3e6a7 100644 --- a/Mage.Sets/src/mage/cards/s/Spellskite.java +++ b/Mage.Sets/src/mage/cards/s/Spellskite.java @@ -61,8 +61,8 @@ public class Spellskite extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(4); - // {UP}: Change a target of target spell or ability to Spellskite. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpellskiteEffect(), new ManaCostsImpl("{UP}")); + // {U/P}: Change a target of target spell or ability to Spellskite. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SpellskiteEffect(), new ManaCostsImpl("{U/P}")); ability.addTarget(new TargetStackObject()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/s/SpinedThopter.java b/Mage.Sets/src/mage/cards/s/SpinedThopter.java index c920b900493..31e4977f221 100644 --- a/Mage.Sets/src/mage/cards/s/SpinedThopter.java +++ b/Mage.Sets/src/mage/cards/s/SpinedThopter.java @@ -41,7 +41,7 @@ import mage.cards.CardSetInfo; public class SpinedThopter extends CardImpl { 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.power = new MageInt(2); diff --git a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java index 449c5aeb146..6da76539b66 100644 --- a/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java +++ b/Mage.Sets/src/mage/cards/s/SurgicalExtraction.java @@ -63,7 +63,7 @@ public class SurgicalExtraction extends CardImpl { } 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, // hand, and library for any number of cards with the same name as that card and exile them. diff --git a/Mage.Sets/src/mage/cards/t/TezzeretsGambit.java b/Mage.Sets/src/mage/cards/t/TezzeretsGambit.java index f5a8033352c..1f658f36a40 100644 --- a/Mage.Sets/src/mage/cards/t/TezzeretsGambit.java +++ b/Mage.Sets/src/mage/cards/t/TezzeretsGambit.java @@ -41,7 +41,7 @@ import mage.cards.CardSetInfo; public class TezzeretsGambit extends CardImpl { 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 ProliferateEffect()); diff --git a/Mage.Sets/src/mage/cards/t/ThunderingTanadon.java b/Mage.Sets/src/mage/cards/t/ThunderingTanadon.java index 40c6b566a9f..91af7f20d55 100644 --- a/Mage.Sets/src/mage/cards/t/ThunderingTanadon.java +++ b/Mage.Sets/src/mage/cards/t/ThunderingTanadon.java @@ -41,7 +41,7 @@ import mage.cards.CardSetInfo; public class ThunderingTanadon extends CardImpl { 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.power = new MageInt(5); diff --git a/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java b/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java index a6013178d53..62a1f0dd706 100644 --- a/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java +++ b/Mage.Sets/src/mage/cards/t/TrespassingSouleater.java @@ -52,7 +52,7 @@ public class TrespassingSouleater extends CardImpl { this.power = 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, new CantBeBlockedSourceEffect(Duration.EndOfTurn), new PhyrexianManaCost(ColoredManaSymbol.U))); diff --git a/Mage.Sets/src/mage/cards/v/VaultSkirge.java b/Mage.Sets/src/mage/cards/v/VaultSkirge.java index ec08b1d2ad7..a8407d5a42b 100644 --- a/Mage.Sets/src/mage/cards/v/VaultSkirge.java +++ b/Mage.Sets/src/mage/cards/v/VaultSkirge.java @@ -42,7 +42,7 @@ import mage.cards.CardSetInfo; public class VaultSkirge extends CardImpl { 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.power = new MageInt(1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HideousEndTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HideousEndTest.java index 1972a6ebce2..99c887f4e68 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HideousEndTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/destroy/HideousEndTest.java @@ -51,10 +51,10 @@ public class HideousEndTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Plains"); 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. - 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, playerB, "Apostle's Blessing", "Copper Myr"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Blessed Breath", "Copper Myr"); setChoice(playerB, "Black"); setStopAt(1, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java index 01aca004194..ca5ab2ebfd9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/MimicVatTest.java @@ -88,7 +88,7 @@ public class MimicVatTest extends CardTestPlayerBase { 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. - 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); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhyrexianMetamorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhyrexianMetamorphTest.java index 3404c3f0378..14e90953a85 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhyrexianMetamorphTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhyrexianMetamorphTest.java @@ -44,7 +44,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase { 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. - addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} + addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{U/P} addCard(Zone.HAND, playerA, "Cloudshift"); //Flying @@ -64,7 +64,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_TURN); execute(); - assertLife(playerA, 24); + assertLife(playerA, 22); assertLife(playerB, 20); assertGraveyardCount(playerA, "Cloudshift", 1); @@ -87,7 +87,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase { 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. - addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} + addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{U/P} // 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. @@ -109,7 +109,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase { setStopAt(3, PhaseStep.END_COMBAT); execute(); - assertLife(playerA, 20); + assertLife(playerA, 18); assertLife(playerB, 18); assertPermanentCount(playerA, "Ponyback Brigade", 1); @@ -128,7 +128,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase { 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. - 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, "Kitesail", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java index 810812f9be5..0cb57e84577 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modification/CostModificationTest.java @@ -57,7 +57,7 @@ public class CostModificationTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); // Look at target player's hand. // 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); setStopAt(2, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/SpellskiteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/SpellskiteTest.java deleted file mode 100644 index acab953a30b..00000000000 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/SpellskiteTest.java +++ /dev/null @@ -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); - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java index f94f7eb6876..7f90d651e39 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BecomesTheTargetTest.java @@ -52,13 +52,13 @@ public class BecomesTheTargetTest extends CardTestPlayerBase { 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); execute(); assertLife(playerA, 20); - assertLife(playerB, 20); + assertLife(playerB, 18); assertPermanentCount(playerA, "Silvercoat Lion", 0); assertTapped("Silvercoat Lion", true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SacredGroundTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SacredGroundTest.java index 1fa82368c50..0ac639ffa43 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SacredGroundTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SacredGroundTest.java @@ -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, // 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. - 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); /** @@ -177,7 +177,7 @@ public class SacredGroundTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Surgical Extraction", 1); assertExileCount("Caves of Koilos", 1); - assertLife(playerA, 20); + assertLife(playerA, 18); assertLife(playerB, 18); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellskiteTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellskiteTest.java index a2ecfa35d3f..d342098352f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellskiteTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellskiteTest.java @@ -52,7 +52,7 @@ public class SpellskiteTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Lightning Bolt"); 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); execute(); @@ -60,7 +60,7 @@ public class SpellskiteTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Lightning Bolt", 1); assertPermanentCount(playerA, "Spellskite", 1); - assertLife(playerA, 20); + assertLife(playerA, 18); assertLife(playerB, 20); assertPowerToughness(playerA, "Spellskite", 3, 7); @@ -90,7 +90,7 @@ public class SpellskiteTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Vedalken Shackles", 1); 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); // {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. @@ -102,7 +102,7 @@ public class SpellskiteTest extends CardTestPlayerBase { castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Frost Titan"); 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); execute(); @@ -141,7 +141,7 @@ public class SpellskiteTest extends CardTestPlayerBase { setModeChoice(playerA, "1"); // Counter target spell 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); execute(); @@ -154,7 +154,7 @@ public class SpellskiteTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Silvercoat Lion", 1); 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, "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); execute(); @@ -194,7 +194,7 @@ public class SpellskiteTest extends CardTestPlayerBase { assertTapped("Silvercoat Lion", true); 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); - 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); execute(); @@ -222,7 +222,7 @@ public class SpellskiteTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Lightning Bolt", 1); assertLife(playerA, 20); - assertLife(playerB, 20); + assertLife(playerB, 18); } @@ -250,4 +250,102 @@ public class SpellskiteTest extends CardTestPlayerBase { 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); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/PostMortemLungeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/PostMortemLungeTest.java index d4683706998..bb9dbe61437 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/PostMortemLungeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/delayed/PostMortemLungeTest.java @@ -18,7 +18,7 @@ public class PostMortemLungeTest extends CardTestPlayerBase { 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. It gains haste. Exile it at the beginning of the next end step. */ diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java index f74cdd5f74b..467db2066af 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/dies/ThragtuskTest.java @@ -43,7 +43,7 @@ public class ThragtuskTest extends CardTestPlayerBase { /** * Test if a Thragtusk is copied by a PhyrexianMetamorph that both triggers - * cotrrect work + * correct work */ @Test public void testPhyrexianMetamorph() { @@ -69,7 +69,7 @@ public class ThragtuskTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Phyrexian Metamorph", 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 assertPermanentCount(playerA, "Beast", 1); @@ -116,7 +116,7 @@ public class ThragtuskTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Phyrexian Metamorph", 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 assertPermanentCount(playerA, "Beast", 0); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index 32626f779c9..7cbce9ac759 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -31,11 +31,9 @@ import mage.MageObject; import mage.MageObjectReference; import mage.Mana; import mage.abilities.costs.*; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCost; -import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.costs.mana.VariableManaCost; +import mage.abilities.costs.mana.*; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.Effects; @@ -62,6 +60,7 @@ import mage.watchers.Watcher; import org.apache.log4j.Logger; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; 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)) { return false; } + + handlePhyrexianManaCosts(game, sourceId, controller); + for (UUID modeId : this.getModes().getSelectedModes()) { this.getModes().setActiveMode(modeId); //20121001 - 601.2c @@ -503,6 +505,27 @@ public abstract class AbilityImpl implements Ability { 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 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. * diff --git a/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java index 703f61ddff3..3dfad98d515 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/PhyrexianManaCost.java @@ -50,34 +50,20 @@ public class PhyrexianManaCost extends ColoredManaCost { super(manaCost); } - @Override - public void assignPayment(Game game, Ability ability, ManaPool pool, Cost costToPay) { - assignColored(ability, game, pool, this.mana, costToPay); - } - @Override public String getText() { - return '{' + mana.toString() + "P}"; + return '{' + mana.toString() + "/P}"; + } + + public String getBaseText() { + return super.getText(); } @Override - public PhyrexianManaCost getUnpaid() { - return this; + public ColoredManaCost getUnpaid() { + 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 public PhyrexianManaCost copy() { diff --git a/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java b/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java index 6b3c56b6fea..9eb3e16e82f 100644 --- a/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java +++ b/Mage/src/main/java/mage/abilities/effects/PayCostToAttackBlockEffectImpl.java @@ -29,8 +29,13 @@ package mage.abilities.effects; import mage.abilities.Ability; 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.ManaCostsImpl; +import mage.abilities.costs.mana.PhyrexianManaCost; import mage.constants.Duration; import mage.constants.Outcome; import mage.game.Game; @@ -38,6 +43,9 @@ import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; import mage.players.Player; +import java.util.Iterator; +import java.util.List; + /** * * @author LevelX2 @@ -141,6 +149,7 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm attackBlockManaTax.clearPaid(); if (attackBlockManaTax.canPay(source, source.getSourceId(), player.getId(), game) && player.chooseUse(Outcome.Neutral, chooseText, source, game)) { + handlePhyrexianManaCosts(manaCosts, player, source, game); if (attackBlockManaTax instanceof ManaCostsImpl) { if (attackBlockManaTax.payOrRollback(source, game, source.getSourceId(), event.getPlayerId())) { return false; @@ -152,6 +161,27 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm return false; } + private void handlePhyrexianManaCosts(ManaCosts manaCosts, Player player, Ability source, Game game) { + Iterator manaCostIterator = manaCosts.iterator(); + Costs 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) { Player player = game.getPlayer(event.getPlayerId()); if (player != null) { diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 03b5ab4d5af..e22021f7294 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2438,7 +2438,9 @@ public abstract class PlayerImpl implements Player, Serializable { if (!copy.canActivate(playerId, game)) { return false; } - game.getContinuousEffects().costModification(copy, game); + if(available != null) { + game.getContinuousEffects().costModification(copy, game); + } Card card = game.getCard(ability.getSourceId()); if (card != null) {