From 209e2d13c1e216cdc8f5974c5aae9355bafca969 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 8 Mar 2017 12:05:36 -0600 Subject: [PATCH 01/15] - Fix for Spellskite in situations where it was also a target. Fixed the test for Spellskite. --- Mage.Sets/src/mage/cards/h/Hex.java | 2 +- Mage.Sets/src/mage/cards/s/Spellskite.java | 6 +- .../test/cards/single/SpellskiteTest.java | 77 ++++++++++++++++++- 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/Mage.Sets/src/mage/cards/h/Hex.java b/Mage.Sets/src/mage/cards/h/Hex.java index 86bd5e3bf2..0c6705e4aa 100644 --- a/Mage.Sets/src/mage/cards/h/Hex.java +++ b/Mage.Sets/src/mage/cards/h/Hex.java @@ -46,7 +46,7 @@ public class Hex extends CardImpl { // Destroy six target creatures. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(6, 6)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent(6)); } public Hex(final Hex card) { diff --git a/Mage.Sets/src/mage/cards/s/Spellskite.java b/Mage.Sets/src/mage/cards/s/Spellskite.java index 08340c9b3a..d613df99c6 100644 --- a/Mage.Sets/src/mage/cards/s/Spellskite.java +++ b/Mage.Sets/src/mage/cards/s/Spellskite.java @@ -129,12 +129,14 @@ class SpellskiteEffect extends OneShotEffect { for (Target target : targets) { for (UUID targetId : target.getTargets()) { String name = getTargetName(targetId, game); - if (!targetId.equals(source.getSourceId()) && target.getTargets().contains(source.getSourceId())) { + if (targetId.equals(source.getSourceId()) + || target.getTargets().contains(source.getSourceId())) { // you can't change this target to source because the source is already another targetId of that target. twoTimesTarget = true; continue; } - if (target.canTarget(stackObject.getControllerId(), source.getSourceId(), sourceAbility, game)) { + if (target.canTarget(stackObject.getControllerId(), source.getSourceId(), sourceAbility, game) + && !twoTimesTarget) { validTargets = true; if (name != null && controller.chooseUse(Outcome.Neutral, "Change target from " + name + " to " + sourceObject.getLogName() + '?', source, game)) { 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 index 145f6eb300..acab953a30 100644 --- 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 @@ -6,12 +6,17 @@ import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; /** - * Created by goesta on 12/02/2017. + * Created by goesta on 12/02/2017. Modified by jeffwadsworth */ public class SpellskiteTest extends CardTestPlayerBase { @Test - public void testThatSplitDamageCanGetRedirected() { + 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); @@ -34,7 +39,73 @@ public class SpellskiteTest extends CardTestPlayerBase { 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); - assertPowerToughness(playerB, "Scute Mob", 1, 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); } } From ce1f4a3bf8e04bf2683362846116f4368cf179fd Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Wed, 8 Mar 2017 20:03:28 -0800 Subject: [PATCH 02/15] 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 e701cce8a0..0b2ed25f00 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 6e1974efe5..6fe57b2d0a 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 dbfbafa4e1..f8e9df80ee 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 e6039af9c1..78d95a968c 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 2ede7fdac7..79db650753 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 c77c476e5d..5cd65289f2 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 d464bd56e7..544b9137aa 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 d833980c12..40ba94ad44 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 3939c51769..72db3fa530 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 4e2353124c..8bf19e2dc5 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 5166e4ce7d..3fe09ad40e 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 63bb4f7dc6..e57539bbf1 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 f1430f0a9d..ddc499ae16 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 80a5832bb5..00dd8f4615 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 3c5a86e4e7..ef520c581d 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 2a5131a39e..613b525cd6 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 efa0cfea1d..dd82416c2f 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 5ee1fed2c4..0a72b7b9f8 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 2692fc62de..d802a6565d 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 7fa187afc3..c56fd2e4c7 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 2049cf2f10..6df97e0c21 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 67fd014fb1..2ec9cc2350 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 fc10cd35b6..35f22cb092 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 866c1af851..6d3f98edd5 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 56c7a95a20..d438d1d5f7 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 bbadca8d95..6ae2486151 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 d613df99c6..b0b3ee3e6a 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 c920b90049..31e4977f22 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 449c5aeb14..6da76539b6 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 f5a8033352..1f658f36a4 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 40c6b566a9..91af7f20d5 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 a6013178d5..62a1f0dd70 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 ec08b1d2ad..a8407d5a42 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 1972a6ebce..99c887f4e6 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 01aca00419..ca5ab2ebfd 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 3404c3f037..14e90953a8 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 810812f9be..0cb57e8457 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 acab953a30..0000000000 --- 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 f94f7eb687..7f90d651e3 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 1fa82368c5..0ac639ffa4 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 a2ecfa35d3..d342098352 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 d468370699..bb9dbe6143 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 f74cdd5f74..467db2066a 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 32626f779c..7cbce9ac75 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 703f61ddff..3dfad98d51 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 6b3c56b6fe..9eb3e16e82 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 03b5ab4d5a..e22021f729 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) { From 495e20ae1a78531f32e9ab32be536a7712685c4d Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Wed, 8 Mar 2017 20:42:44 -0800 Subject: [PATCH 03/15] Fix target defender cases that don't target. --- Mage/src/main/java/mage/target/common/TargetDefender.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java index 24f8ace2c2..3a3131d636 100644 --- a/Mage/src/main/java/mage/target/common/TargetDefender.java +++ b/Mage/src/main/java/mage/target/common/TargetDefender.java @@ -135,12 +135,12 @@ public class TargetDefender extends TargetImpl { MageObject targetSource = game.getObject(sourceId); for (UUID playerId: game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(player, game)) { + if (notTarget || (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(player, game))) { possibleTargets.add(playerId); } } for (Permanent permanent: game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { - if (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, game)) { + if (notTarget || (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, game))) { possibleTargets.add(permanent.getId()); } } From 4a9eddb724d072c37639a17f8b0a0844abcfd675 Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Wed, 8 Mar 2017 20:59:01 -0800 Subject: [PATCH 04/15] Properly fix targeting. The notTarget should only invalidate the canBeTargetedBy return. The Filter should still be relevant. --- Mage/src/main/java/mage/target/common/TargetDefender.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage/src/main/java/mage/target/common/TargetDefender.java b/Mage/src/main/java/mage/target/common/TargetDefender.java index 3a3131d636..802d6a9664 100644 --- a/Mage/src/main/java/mage/target/common/TargetDefender.java +++ b/Mage/src/main/java/mage/target/common/TargetDefender.java @@ -135,12 +135,12 @@ public class TargetDefender extends TargetImpl { MageObject targetSource = game.getObject(sourceId); for (UUID playerId: game.getState().getPlayersInRange(sourceControllerId, game)) { Player player = game.getPlayer(playerId); - if (notTarget || (player != null && player.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(player, game))) { + if (player != null && (player.canBeTargetedBy(targetSource, sourceControllerId, game) || notTarget) && filter.match(player, game)) { possibleTargets.add(playerId); } } for (Permanent permanent: game.getBattlefield().getActivePermanents(new FilterPlaneswalkerPermanent(), sourceControllerId, game)) { - if (notTarget || (permanent.canBeTargetedBy(targetSource, sourceControllerId, game) && filter.match(permanent, game))) { + if ((permanent.canBeTargetedBy(targetSource, sourceControllerId, game) || notTarget) && filter.match(permanent, game)) { possibleTargets.add(permanent.getId()); } } From 2ecb415b4db19b9918604e0525d52ef343d9533c Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Wed, 8 Mar 2017 21:41:57 -0800 Subject: [PATCH 05/15] Removed potential integer overflow. If the number passed into max is Integer.MAX_VALUE, like it is with ChoiceOfDamnation, the random number will overflow. So let's not do that. --- .../src/main/java/mage/player/ai/ComputerPlayer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index de54f2db07..b33381c435 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1543,7 +1543,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } //TODO: improve this if (min < max && min == 0) { - return RandomUtil.nextInt(max + 1); + return RandomUtil.nextInt(max); } return min; } From 52fa3690241b27f82e2e025d242e62ae95628a58 Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Wed, 8 Mar 2017 23:57:11 -0800 Subject: [PATCH 06/15] Fix planeswalker loyalty counters. They should be added like normal counters, firing events that can be intercepted by cards like doubling season. --- .../cards/replacement/DoublingSeasonTest.java | 18 ++++++++++++++++++ .../abilities/costs/common/PayLoyaltyCost.java | 5 +++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java index a58b0bb718..fa7a6663c8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java @@ -192,4 +192,22 @@ public class DoublingSeasonTest extends CardTestPlayerBase { assertLife(playerA, 20); } + + @Test + public void testDoublePlaneswalkerLoyalty() { + addCard(Zone.BATTLEFIELD, playerA, "Tibalt, the Fiend-Blooded"); + + addCard(Zone.BATTLEFIELD, playerA, "Doubling Season"); + + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA,"+1: Draw a card, then discard a card at random."); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertCounterCount("Tibalt, the Fiend-Blooded", CounterType.LOYALTY, 4); + } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java index 64945cb846..72f8d5ab38 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java @@ -27,7 +27,6 @@ */ package mage.abilities.costs.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.costs.Cost; import mage.abilities.costs.CostImpl; @@ -35,6 +34,8 @@ import mage.counters.CounterType; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.UUID; + /** * * @author BetaSteward_at_googlemail.com @@ -67,7 +68,7 @@ public class PayLoyaltyCost extends CostImpl { Permanent planeswalker = game.getPermanent(sourceId); if (planeswalker != null && planeswalker.getCounters(game).getCount(CounterType.LOYALTY) + amount >= 0 && planeswalker.canLoyaltyBeUsed(game)) { if (amount > 0) { - planeswalker.getCounters(game).addCounter(CounterType.LOYALTY.createInstance(amount)); + planeswalker.addCounters(CounterType.LOYALTY.createInstance(amount), ability, game, null); } else if (amount < 0) { planeswalker.removeCounters(CounterType.LOYALTY.getName(), Math.abs(amount), game); } From 1a2724c9b2bac3569d50a3f616b356ae26f69d05 Mon Sep 17 00:00:00 2001 From: Justin Herlehy Date: Thu, 9 Mar 2017 01:14:10 -0800 Subject: [PATCH 07/15] Revert: Fix planeswalker loyalty counters. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change was incorrect. Gatherer Ruling: 10/1/2005: Planeswalkers will enter the battlefield with double the normal amount of loyalty counters. However, if you activate an ability whose cost has you put loyalty counters on a planeswalker, the number you put on isn’t doubled. This is because those counters are put on as a cost, not as an effect. --- .../cards/replacement/DoublingSeasonTest.java | 18 ------------------ .../abilities/costs/common/PayLoyaltyCost.java | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java index fa7a6663c8..a58b0bb718 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java @@ -192,22 +192,4 @@ public class DoublingSeasonTest extends CardTestPlayerBase { assertLife(playerA, 20); } - - @Test - public void testDoublePlaneswalkerLoyalty() { - addCard(Zone.BATTLEFIELD, playerA, "Tibalt, the Fiend-Blooded"); - - addCard(Zone.BATTLEFIELD, playerA, "Doubling Season"); - - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA,"+1: Draw a card, then discard a card at random."); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertCounterCount("Tibalt, the Fiend-Blooded", CounterType.LOYALTY, 4); - } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java index 72f8d5ab38..cbc9693c89 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java @@ -68,7 +68,7 @@ public class PayLoyaltyCost extends CostImpl { Permanent planeswalker = game.getPermanent(sourceId); if (planeswalker != null && planeswalker.getCounters(game).getCount(CounterType.LOYALTY) + amount >= 0 && planeswalker.canLoyaltyBeUsed(game)) { if (amount > 0) { - planeswalker.addCounters(CounterType.LOYALTY.createInstance(amount), ability, game, null); + planeswalker.getCounters(game).addCounter(CounterType.LOYALTY.createInstance(amount)); } else if (amount < 0) { planeswalker.removeCounters(CounterType.LOYALTY.getName(), Math.abs(amount), game); } From 9dd1e60f2da2b90749936bf4c7cc46c74d60b447 Mon Sep 17 00:00:00 2001 From: Justin Herlehy Date: Thu, 9 Mar 2017 01:48:34 -0800 Subject: [PATCH 08/15] Implement Clutch of Undeath Implement Clutch of Undeath and add test to ensure functionality. --- .../src/mage/cards/c/ClutchOfUndeath.java | 84 +++++++++++++++++++ Mage.Sets/src/mage/sets/Scourge.java | 4 +- .../conditional/ClutchOfUndeathTest.java | 52 ++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/cards/c/ClutchOfUndeath.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/conditional/ClutchOfUndeathTest.java diff --git a/Mage.Sets/src/mage/cards/c/ClutchOfUndeath.java b/Mage.Sets/src/mage/cards/c/ClutchOfUndeath.java new file mode 100644 index 0000000000..2d7c390abd --- /dev/null +++ b/Mage.Sets/src/mage/cards/c/ClutchOfUndeath.java @@ -0,0 +1,84 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.cards.c; + +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.EnchantedCreatureSubtypeCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +import java.util.UUID; + +/** + * + * @author JRHerlehy + */ +public class ClutchOfUndeath extends CardImpl { + + public ClutchOfUndeath(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); + + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +3/+3 as long as it's a Zombie. Otherwise, it gets -3/-3. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect( + new BoostEnchantedEffect(3, 3), + new BoostEnchantedEffect(-3, -3), + new EnchantedCreatureSubtypeCondition("Zombie"), + "Enchanted creature gets +3/+3 as long as it's a Zombie. Otherwise, it gets -3/-3.") + ) + ); + } + + public ClutchOfUndeath(final ClutchOfUndeath card) { + super(card); + } + + @Override + public ClutchOfUndeath copy() { + return new ClutchOfUndeath(this); + } +} diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index 2a9229ac2c..2ee09b92ce 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -28,8 +28,8 @@ package mage.sets; import mage.cards.ExpansionSet; -import mage.constants.SetType; import mage.constants.Rarity; +import mage.constants.SetType; /** * @@ -69,12 +69,14 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Break Asunder", 113, Rarity.COMMON, mage.cards.b.BreakAsunder.class)); cards.add(new SetCardInfo("Cabal Conditioning", 56, Rarity.RARE, mage.cards.c.CabalConditioning.class)); cards.add(new SetCardInfo("Cabal Interrogator", 57, Rarity.UNCOMMON, mage.cards.c.CabalInterrogator.class)); + cards.add(new SetCardInfo("Clutch of Undeath", 61, Rarity.COMMON, mage.cards.c.ClutchOfUndeath.class)); cards.add(new SetCardInfo("Dawn Elemental", 7, Rarity.RARE, mage.cards.d.DawnElemental.class)); cards.add(new SetCardInfo("Call to the Grave", 58, Rarity.RARE, mage.cards.c.CallToTheGrave.class)); cards.add(new SetCardInfo("Carbonize", 83, Rarity.UNCOMMON, mage.cards.c.Carbonize.class)); cards.add(new SetCardInfo("Carrion Feeder", 59, Rarity.COMMON, mage.cards.c.CarrionFeeder.class)); cards.add(new SetCardInfo("Chartooth Cougar", 84, Rarity.COMMON, mage.cards.c.ChartoothCougar.class)); cards.add(new SetCardInfo("Claws of Wirewood", 114, Rarity.UNCOMMON, mage.cards.c.ClawsOfWirewood.class)); + cards.add(new SetCardInfo("Clutch of Undeath", 61, Rarity.COMMON, mage.cards.c.ClutchOfUndeath.class)); cards.add(new SetCardInfo("Coast Watcher", 30, Rarity.COMMON, mage.cards.c.CoastWatcher.class)); cards.add(new SetCardInfo("Consumptive Goo", 62, Rarity.RARE, mage.cards.c.ConsumptiveGoo.class)); cards.add(new SetCardInfo("Daru Warchief", 6, Rarity.UNCOMMON, mage.cards.d.DaruWarchief.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/conditional/ClutchOfUndeathTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/ClutchOfUndeathTest.java new file mode 100644 index 0000000000..d3c28ace73 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/conditional/ClutchOfUndeathTest.java @@ -0,0 +1,52 @@ +package org.mage.test.cards.conditional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JRHerlehy + * Created on 3/9/17. + */ +public class ClutchOfUndeathTest extends CardTestPlayerBase{ + + @Test + public void testEnchantNonZombie() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Griselbrand"); + addCard(Zone.HAND, playerA, "Clutch of Undeath"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clutch of Undeath", "Griselbrand"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Griselbrand", 4, 4); + } + + @Test + public void testEnchantZombie() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Gurmag Angler"); + addCard(Zone.HAND, playerA, "Clutch of Undeath"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clutch of Undeath", "Gurmag Angler"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Gurmag Angler", 8, 8); + } + + @Test + public void testEnchantChangeling() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Amoeboid Changeling"); + addCard(Zone.HAND, playerA, "Clutch of Undeath"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Clutch of Undeath", "Amoeboid Changeling"); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPowerToughness(playerA, "Amoeboid Changeling", 4, 4); + } +} From 964f2fb9d0f82b356d63abdbe9544a666167ec62 Mon Sep 17 00:00:00 2001 From: Justin Herlehy Date: Thu, 9 Mar 2017 01:57:21 -0800 Subject: [PATCH 09/15] Corrected card listing in Scourge set file. --- Mage.Sets/src/mage/sets/Scourge.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/Scourge.java b/Mage.Sets/src/mage/sets/Scourge.java index 2ee09b92ce..edbe008030 100644 --- a/Mage.Sets/src/mage/sets/Scourge.java +++ b/Mage.Sets/src/mage/sets/Scourge.java @@ -69,8 +69,6 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Break Asunder", 113, Rarity.COMMON, mage.cards.b.BreakAsunder.class)); cards.add(new SetCardInfo("Cabal Conditioning", 56, Rarity.RARE, mage.cards.c.CabalConditioning.class)); cards.add(new SetCardInfo("Cabal Interrogator", 57, Rarity.UNCOMMON, mage.cards.c.CabalInterrogator.class)); - cards.add(new SetCardInfo("Clutch of Undeath", 61, Rarity.COMMON, mage.cards.c.ClutchOfUndeath.class)); - cards.add(new SetCardInfo("Dawn Elemental", 7, Rarity.RARE, mage.cards.d.DawnElemental.class)); cards.add(new SetCardInfo("Call to the Grave", 58, Rarity.RARE, mage.cards.c.CallToTheGrave.class)); cards.add(new SetCardInfo("Carbonize", 83, Rarity.UNCOMMON, mage.cards.c.Carbonize.class)); cards.add(new SetCardInfo("Carrion Feeder", 59, Rarity.COMMON, mage.cards.c.CarrionFeeder.class)); @@ -80,6 +78,7 @@ public class Scourge extends ExpansionSet { cards.add(new SetCardInfo("Coast Watcher", 30, Rarity.COMMON, mage.cards.c.CoastWatcher.class)); cards.add(new SetCardInfo("Consumptive Goo", 62, Rarity.RARE, mage.cards.c.ConsumptiveGoo.class)); cards.add(new SetCardInfo("Daru Warchief", 6, Rarity.UNCOMMON, mage.cards.d.DaruWarchief.class)); + cards.add(new SetCardInfo("Dawn Elemental", 7, Rarity.RARE, mage.cards.d.DawnElemental.class)); cards.add(new SetCardInfo("Day of the Dragons", 31, Rarity.RARE, mage.cards.d.DayOfTheDragons.class)); cards.add(new SetCardInfo("Death's-Head Buzzard", 63, Rarity.COMMON, mage.cards.d.DeathsHeadBuzzard.class)); cards.add(new SetCardInfo("Decree of Annihilation", 85, Rarity.RARE, mage.cards.d.DecreeOfAnnihilation.class)); From dd0c89db1db86168c19614dfc0b42481651e934e Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Thu, 9 Mar 2017 02:24:34 -0800 Subject: [PATCH 10/15] Fix Dack Fayden Emblem. The emblem's effect was referencing the permanent directly. This meant that if the permanent left and came back, the control effect would start working again. --- Mage.Sets/src/mage/cards/d/DackFayden.java | 34 ++++++--- .../test/cards/control/DackFaydenTest.java | 73 +++++++++++++++++++ 2 files changed, 95 insertions(+), 12 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/control/DackFaydenTest.java diff --git a/Mage.Sets/src/mage/cards/d/DackFayden.java b/Mage.Sets/src/mage/cards/d/DackFayden.java index 42c2af272a..71cc828ce7 100644 --- a/Mage.Sets/src/mage/cards/d/DackFayden.java +++ b/Mage.Sets/src/mage/cards/d/DackFayden.java @@ -27,9 +27,6 @@ */ package mage.cards.d; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.SpellAbility; @@ -59,6 +56,11 @@ import mage.players.Player; import mage.target.Target; import mage.target.TargetPlayer; import mage.target.common.TargetArtifactPermanent; +import mage.target.targetpointer.FixedTargets; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** * @@ -135,7 +137,7 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { boolean returnValue = false; - List targettedPermanents = new ArrayList<>(0); + List targetedPermanentIds = new ArrayList<>(0); Player player = game.getPlayer(this.getControllerId()); if (player != null) { if (event.getPlayerId().equals(this.getControllerId())) { @@ -147,7 +149,7 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl { for (UUID targetId : target.getTargets()) { if (game.getBattlefield().containsPermanent(targetId)) { returnValue = true; - targettedPermanents.add(targetId); + targetedPermanentIds.add(targetId); } } } @@ -156,7 +158,7 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl { for (UUID targetId : effect.getTargetPointer().getTargets(game, spellAbility)) { if (game.getBattlefield().containsPermanent(targetId)) { returnValue = true; - targettedPermanents.add(targetId); + targetedPermanentIds.add(targetId); } } } @@ -166,7 +168,15 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl { for (Effect effect : this.getEffects()) { if (effect instanceof DackFaydenEmblemEffect) { DackFaydenEmblemEffect dackEffect = (DackFaydenEmblemEffect) effect; - dackEffect.setPermanents(targettedPermanents); + List permanents = new ArrayList<>(); + for(UUID permanentId : targetedPermanentIds) { + Permanent permanent = game.getPermanent(permanentId); + if(permanent != null) { + permanents.add(permanent); + } + } + + dackEffect.setTargets(permanents, game); } } return returnValue; @@ -180,7 +190,7 @@ class DackFaydenEmblemTriggeredAbility extends TriggeredAbilityImpl { class DackFaydenEmblemEffect extends ContinuousEffectImpl { - protected List permanents; + protected FixedTargets fixedTargets; DackFaydenEmblemEffect() { super(Duration.EndOfGame, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); @@ -189,7 +199,7 @@ class DackFaydenEmblemEffect extends ContinuousEffectImpl { DackFaydenEmblemEffect(final DackFaydenEmblemEffect effect) { super(effect); - this.permanents = effect.permanents; + this.fixedTargets = effect.fixedTargets; } @Override @@ -199,7 +209,7 @@ class DackFaydenEmblemEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { - for (UUID permanentId : this.permanents) { + for (UUID permanentId : fixedTargets.getTargets(game, source)) { Permanent permanent = game.getPermanent(permanentId); if (permanent != null) { permanent.changeControllerId(source.getControllerId(), game); @@ -208,7 +218,7 @@ class DackFaydenEmblemEffect extends ContinuousEffectImpl { return true; } - public void setPermanents(List targettedPermanents) { - this.permanents = new ArrayList<>(targettedPermanents); + public void setTargets(List targetedPermanents, Game game) { + this.fixedTargets = new FixedTargets(targetedPermanents, game); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/DackFaydenTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/DackFaydenTest.java new file mode 100644 index 0000000000..ec07d31058 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/DackFaydenTest.java @@ -0,0 +1,73 @@ +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +public class DackFaydenTest extends CardTestPlayerBase { + + @Test + public void testDackFaydenEmblem() { + + addCard(Zone.BATTLEFIELD, playerA, "Dack Fayden"); + + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter"); + + addCard(Zone.HAND, playerA, "Gut Shot"); + + + for(int i = 1; i < 8; i+= 2) { + activateAbility(i, PhaseStep.PRECOMBAT_MAIN, playerA,"+1: Target player draws two cards, then discards two cards.", playerB); + } + + activateAbility(9, PhaseStep.PRECOMBAT_MAIN, playerA, + "-6: You get an emblem with \"Whenever you cast a spell that targets one or more permanents, gain control of those permanents.\""); + + castSpell(10, PhaseStep.PRECOMBAT_MAIN, playerA, "Gut Shot", "Ornithopter"); + + setStopAt(10, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 20); + + assertCounterCount("Dack Fayden", CounterType.LOYALTY, 1); + assertPermanentCount(playerA, "Ornithopter", 1); + } + + //Ensure that if a permanent moves to a different zone and comes back, + //it won't still be under the controller of Dack's Emblem. + @Test + public void testDackFaydenEmblemAcrossZones() { + addCard(Zone.BATTLEFIELD, playerA, "Dack Fayden"); + + addCard(Zone.BATTLEFIELD, playerB, "Ornithopter"); + + addCard(Zone.HAND, playerA, "Gut Shot"); + + addCard(Zone.HAND, playerA, "Unsummon"); + addCard(Zone.BATTLEFIELD, playerA, "Island"); + + for(int i = 1; i < 8; i+= 2) { + activateAbility(i, PhaseStep.PRECOMBAT_MAIN, playerA,"+1: Target player draws two cards, then discards two cards.", playerB); + } + + activateAbility(9, PhaseStep.PRECOMBAT_MAIN, playerA, + "-6: You get an emblem with \"Whenever you cast a spell that targets one or more permanents, gain control of those permanents.\""); + + castSpell(10, PhaseStep.PRECOMBAT_MAIN, playerA, "Gut Shot", "Ornithopter"); + castSpell(10, PhaseStep.PRECOMBAT_MAIN, playerA, "Unsummon", "Ornithopter", "Gut Shot", StackClause.WHILE_NOT_ON_STACK); + castSpell(10, PhaseStep.PRECOMBAT_MAIN, playerB, "Ornithopter"); + + setStopAt(10, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 20); + + assertCounterCount("Dack Fayden", CounterType.LOYALTY, 1); + assertPermanentCount(playerB, "Ornithopter", 1); + } +} From d7d700f66a6b5e8a7743f484df4c1e7024eb0e39 Mon Sep 17 00:00:00 2001 From: Nathaniel Brandes Date: Thu, 9 Mar 2017 02:30:52 -0800 Subject: [PATCH 11/15] Some clarification around loyalty. Added unit test. --- .../cards/replacement/DoublingSeasonTest.java | 24 +++++++++++++++++++ .../costs/common/PayLoyaltyCost.java | 6 +++++ 2 files changed, 30 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java index a58b0bb718..4130674583 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/DoublingSeasonTest.java @@ -192,4 +192,28 @@ public class DoublingSeasonTest extends CardTestPlayerBase { assertLife(playerA, 20); } + + /** + * Gatherer Ruling: + * 10/1/2005: Planeswalkers will enter the battlefield with double the normal amount of loyalty counters. However, + * if you activate an ability whose cost has you put loyalty counters on a planeswalker, the number you put on isn’t doubled. + * This is because those counters are put on as a cost, not as an effect. + */ + @Test + public void testPlaneswalkerLoyalty() { + addCard(Zone.BATTLEFIELD, playerA, "Tibalt, the Fiend-Blooded"); + + addCard(Zone.BATTLEFIELD, playerA, "Doubling Season"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA,"+1: Draw a card, then discard a card at random."); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + //Should not be doubled + assertCounterCount("Tibalt, the Fiend-Blooded", CounterType.LOYALTY, 3); + } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java index cbc9693c89..24a40a8f3b 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayLoyaltyCost.java @@ -63,6 +63,12 @@ public class PayLoyaltyCost extends CostImpl { return planeswalker != null && planeswalker.getCounters(game).getCount(CounterType.LOYALTY) + amount >= 0 && planeswalker.canLoyaltyBeUsed(game); } + /** + * Gatherer Ruling: + * 10/1/2005: Planeswalkers will enter the battlefield with double the normal amount of loyalty counters. However, + * if you activate an ability whose cost has you put loyalty counters on a planeswalker, the number you put on isn’t doubled. + * This is because those counters are put on as a cost, not as an effect. + **/ @Override public boolean pay(Ability ability, Game game, UUID sourceId, UUID controllerId, boolean noMana, Cost costToPay) { Permanent planeswalker = game.getPermanent(sourceId); From 1646fb491fb83ef42d3a59fa376685e35c11d7d2 Mon Sep 17 00:00:00 2001 From: spjspj Date: Thu, 9 Mar 2017 23:13:56 +1100 Subject: [PATCH 12/15] Slight fix for modern cube --- .../mage/tournament/cubes/ModernCube2017.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/ModernCube2017.java b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/ModernCube2017.java index 77d195a31b..c4c3a422d0 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/ModernCube2017.java +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/src/mage/tournament/cubes/ModernCube2017.java @@ -34,7 +34,6 @@ import mage.game.draft.DraftCube; * @author fireshoes */ public class ModernCube2017 extends DraftCube { - public ModernCube2017() { super("Modern Cube 2017"); // http://magic.wizards.com/en/articles/archive/magic-online/modern-cube-cardlist-2017-03-02 @@ -60,6 +59,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Arbor Elf", "")); cubeCards.add(new CardIdentity("Arc Trail", "")); cubeCards.add(new CardIdentity("Archangel Avacyn", "")); + cubeCards.add(new CardIdentity("Archangel of Thune", "")); cubeCards.add(new CardIdentity("Arid Mesa", "")); cubeCards.add(new CardIdentity("Arlinn Kord", "")); cubeCards.add(new CardIdentity("Ashiok, Nightmare Weaver", "")); @@ -70,6 +70,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Azure Mage", "")); cubeCards.add(new CardIdentity("Banefire", "")); cubeCards.add(new CardIdentity("Baneslayer Angel", "")); + cubeCards.add(new CardIdentity("Banishing Light", "")); cubeCards.add(new CardIdentity("Bant Charm", "")); cubeCards.add(new CardIdentity("Baral, Chief of Compliance", "")); cubeCards.add(new CardIdentity("Batterskull", "")); @@ -124,6 +125,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Coalition Relic", "")); cubeCards.add(new CardIdentity("Coldsteel Heart", "")); cubeCards.add(new CardIdentity("Collective Defiance", "")); + cubeCards.add(new CardIdentity("Collective Effort", "")); cubeCards.add(new CardIdentity("Compulsive Research", "")); cubeCards.add(new CardIdentity("Condemn", "")); cubeCards.add(new CardIdentity("Condescend", "")); @@ -249,13 +251,14 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Hellrider", "")); cubeCards.add(new CardIdentity("Herald of Torment", "")); cubeCards.add(new CardIdentity("Hero of Bladehold", "")); + cubeCards.add(new CardIdentity("Hero of Oxid Ridge", "")); cubeCards.add(new CardIdentity("Hero's Downfall", "")); cubeCards.add(new CardIdentity("Hinterland Harbor", "")); cubeCards.add(new CardIdentity("Honor of the Pure", "")); cubeCards.add(new CardIdentity("Hornet Nest", "")); cubeCards.add(new CardIdentity("Hornet Queen", "")); cubeCards.add(new CardIdentity("Huntmaster of the Fells", "")); - cubeCards.add(new CardIdentity("Hypotic Specter", "")); + cubeCards.add(new CardIdentity("Hypnotic Specter", "")); cubeCards.add(new CardIdentity("Icy Manipulator", "")); cubeCards.add(new CardIdentity("Imposing Sovereign", "")); cubeCards.add(new CardIdentity("Imprisoned in the Moon", "")); @@ -268,7 +271,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Isolated Chapel", "")); cubeCards.add(new CardIdentity("Izzet Charm", "")); cubeCards.add(new CardIdentity("Jace Beleren", "")); - cubeCards.add(new CardIdentity("Jace, Architect of Though", "")); + cubeCards.add(new CardIdentity("Jace, Architect of Thought", "")); cubeCards.add(new CardIdentity("Jace, Unraveler of Secrets", "")); cubeCards.add(new CardIdentity("Jace, Vryn's Prodigy", "")); cubeCards.add(new CardIdentity("Jinxed Choker", "")); @@ -280,7 +283,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Karplusan Forest", "")); cubeCards.add(new CardIdentity("Kessig Prowler", "")); cubeCards.add(new CardIdentity("Kessig Wolf Run", "")); - cubeCards.add(new CardIdentity("Kik-Jiki, Mirror Breaker", "")); + cubeCards.add(new CardIdentity("Kiki-Jiki, Mirror Breaker", "")); cubeCards.add(new CardIdentity("Kiora, Master of the Depths", "")); cubeCards.add(new CardIdentity("Kira, Great Glass-Spinner", "")); cubeCards.add(new CardIdentity("Kitchen Finks", "")); @@ -297,8 +300,8 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Lightning Helix", "")); cubeCards.add(new CardIdentity("Lightning Mauler", "")); cubeCards.add(new CardIdentity("Lightning Strike", "")); - cubeCards.add(new CardIdentity("Liliana of the Veil", "")); cubeCards.add(new CardIdentity("Liliana Vess", "")); + cubeCards.add(new CardIdentity("Liliana of the Veil", "")); cubeCards.add(new CardIdentity("Liliana's Specter", "")); cubeCards.add(new CardIdentity("Liliana, Heretical Healer", "")); cubeCards.add(new CardIdentity("Liliana, the Last Hope", "")); @@ -360,7 +363,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Overwhelming Stampede", "")); cubeCards.add(new CardIdentity("Pack Rat", "")); cubeCards.add(new CardIdentity("Pact of Negation", "")); - cubeCards.add(new CardIdentity("Palldium Myr", "")); + cubeCards.add(new CardIdentity("Palladium Myr", "")); cubeCards.add(new CardIdentity("Path to Exile", "")); cubeCards.add(new CardIdentity("Perilous Myr", "")); cubeCards.add(new CardIdentity("Pestermite", "")); @@ -415,7 +418,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Rune Snag", "")); cubeCards.add(new CardIdentity("Rune-Scarred Demon", "")); cubeCards.add(new CardIdentity("Ruthless Ripper", "")); - cubeCards.add(new CardIdentity("Scader Foundry", "")); + cubeCards.add(new CardIdentity("Sacred Foundry", "")); cubeCards.add(new CardIdentity("Saheeli Rai", "")); cubeCards.add(new CardIdentity("Sakura-Tribe Elder", "")); cubeCards.add(new CardIdentity("Sarkhan, the Dragonspeaker", "")); @@ -425,6 +428,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Seachrome Coast", "")); cubeCards.add(new CardIdentity("Search for Tomorrow", "")); cubeCards.add(new CardIdentity("Searing Blaze", "")); + cubeCards.add(new CardIdentity("Searing Blood", "")); cubeCards.add(new CardIdentity("Seeker of the Way", "")); cubeCards.add(new CardIdentity("Selesnya Charm", "")); cubeCards.add(new CardIdentity("Selfless Spirit", "")); @@ -459,7 +463,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Sower of Temptation", "")); cubeCards.add(new CardIdentity("Spear of Heliod", "")); cubeCards.add(new CardIdentity("Spectral Procession", "")); - cubeCards.add(new CardIdentity("Spell Piece", "")); + cubeCards.add(new CardIdentity("Spell Pierce", "")); cubeCards.add(new CardIdentity("Spell Queller", "")); cubeCards.add(new CardIdentity("Spellskite", "")); cubeCards.add(new CardIdentity("Sphere of the Suns", "")); @@ -488,15 +492,17 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Sword of Light and Shadow", "")); cubeCards.add(new CardIdentity("Sword of War and Peace", "")); cubeCards.add(new CardIdentity("Sylvan Advocate", "")); + cubeCards.add(new CardIdentity("Sylvan Caryatid", "")); cubeCards.add(new CardIdentity("Tamiyo, the Moon Sage", "")); - cubeCards.add(new CardIdentity("Tasigur, the Golder Fang", "")); + cubeCards.add(new CardIdentity("Tasigur, the Golden Fang", "")); cubeCards.add(new CardIdentity("Teferi, Mage of Zhalfir", "")); cubeCards.add(new CardIdentity("Temple Garden", "")); cubeCards.add(new CardIdentity("Temple of Epiphany", "")); - cubeCards.add(new CardIdentity("Temple of Maladay", "")); + cubeCards.add(new CardIdentity("Temple of Malady", "")); cubeCards.add(new CardIdentity("Temple of Mystery", "")); cubeCards.add(new CardIdentity("Temple of Silence", "")); cubeCards.add(new CardIdentity("Temple of Triumph", "")); + cubeCards.add(new CardIdentity("Temporal Isolation", "")); cubeCards.add(new CardIdentity("Temporal Mastery", "")); cubeCards.add(new CardIdentity("Terastodon", "")); cubeCards.add(new CardIdentity("Terramorphic Expanse", "")); @@ -507,7 +513,7 @@ public class ModernCube2017 extends DraftCube { cubeCards.add(new CardIdentity("Thragtusk", "")); cubeCards.add(new CardIdentity("Threads of Disloyalty", "")); cubeCards.add(new CardIdentity("Thriving Grubs", "")); - cubeCards.add(new CardIdentity("Thurn, the Last Troll", "")); + cubeCards.add(new CardIdentity("Thrun, the Last Troll", "")); cubeCards.add(new CardIdentity("Thunderbreak Regent", "")); cubeCards.add(new CardIdentity("Thundermaw Hellkite", "")); cubeCards.add(new CardIdentity("Tidings", "")); From 691c590e6801f4837285e9db7593e8e005e337d0 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 9 Mar 2017 10:15:57 -0600 Subject: [PATCH 13/15] - Fixed Selvala, Heart of the Wilds. --- .../mage/cards/s/SelvalaHeartOfTheWilds.java | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java index 54f495df6d..6546b7956a 100644 --- a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java @@ -29,22 +29,22 @@ package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.mana.DynamicManaAbility; +import mage.abilities.effects.common.AddManaInAnyCombinationEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.Predicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -60,13 +60,13 @@ public class SelvalaHeartOfTheWilds extends CardImpl { static { filter.add(new AnotherPredicate()); - filter.add(new GreatestPowerPredicate()); } private static final String rule = "Whenever another creature enters the battlefield, its controller may draw a card if its power is greater than each other creature's power."; + private static final String rule2 = "Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control."; public SelvalaHeartOfTheWilds(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}{G}"); + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); this.supertype.add("Legendary"); this.subtype.add("Elf"); this.subtype.add("Scout"); @@ -77,8 +77,8 @@ public class SelvalaHeartOfTheWilds extends CardImpl { this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new SelvalaHeartOfTheWildsEffect(), filter, false, SetTargetPointer.PERMANENT, rule)); // {G}, {T}: Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control. - Ability ability = new DynamicManaAbility(new Mana(0, 0, 0, 0, 0, 0, 1, 0), new GreatestPowerYouControlValue(), new ManaCostsImpl<>("{G}"), - "Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control."); + Effect effect = new AddManaInAnyCombinationEffect(new GreatestPowerAmongControlledCreaturesValue(), rule2, ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{G}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -96,6 +96,12 @@ public class SelvalaHeartOfTheWilds extends CardImpl { class SelvalaHeartOfTheWildsEffect extends OneShotEffect { + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent(); + + static { + filter2.add(new GreatestPowerPredicate()); + } + public SelvalaHeartOfTheWildsEffect() { super(Outcome.Benefit); this.staticText = "that creature's controller may draw a card"; @@ -116,27 +122,28 @@ class SelvalaHeartOfTheWildsEffect extends OneShotEffect { if (permanent == null) { permanent = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); } - if (permanent != null) { - Player cardowner = game.getPlayer(permanent.getControllerId()); - if (cardowner.chooseUse(Outcome.DrawCard, "Would you like to draw a card?", source, game)) { - cardowner.drawCards(1, game); + if (permanent != null + && filter2.match(permanent, game)) { + Player permanentController = game.getPlayer(permanent.getControllerId()); + if (permanentController.chooseUse(Outcome.DrawCard, "Would you like to draw a card?", source, game)) { + permanentController.drawCards(1, game); + return true; } } - return true; + return false; } } -class GreatestPowerPredicate implements ObjectSourcePlayerPredicate> { +class GreatestPowerPredicate implements Predicate { @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - int pow = input.getObject().getPower().getValue(); - - for (UUID id : game.getPlayerList()) { - Player player = game.getPlayer(id); + public boolean apply(Permanent input, Game game) { + int power = input.getPower().getValue(); + for (UUID playerId : game.getPlayerList()) { + Player player = game.getPlayer(playerId); if (player != null) { - for (Permanent p : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), id, game)) { - if (p.getPower().getValue() >= pow && !p.equals(input.getObject())) { + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), playerId, game)) { + if (permanent.getPower().getValue() >= power && !permanent.equals(input)) { return false; //we found something with equal/more power } } @@ -158,9 +165,9 @@ class GreatestPowerYouControlValue implements DynamicValue { Player player = game.getPlayer(sourceAbility.getControllerId()); int amount = 0; if (player != null) { - for (Permanent p : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)) { - if (p.getPower().getValue() > amount) { - amount = p.getPower().getValue(); + for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), sourceAbility.getControllerId(), game)) { + if (permanent.getPower().getValue() > amount) { + amount = permanent.getPower().getValue(); } } } @@ -174,6 +181,6 @@ class GreatestPowerYouControlValue implements DynamicValue { @Override public String getMessage() { - return "Add X mana in any combination of colors to your mana pool, where X is the number of creatures with defender you control."; + return "Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control."; } } From cb5fc3037e9bbf424e4840095e6e57be1eed0589 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 9 Mar 2017 10:41:01 -0600 Subject: [PATCH 14/15] - Changed the SimpleActivatedAbility to a SimpleManaAbility on Selvala, Heart of the Wilds. --- Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java index 6546b7956a..53f776fdb4 100644 --- a/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java +++ b/Mage.Sets/src/mage/cards/s/SelvalaHeartOfTheWilds.java @@ -31,7 +31,6 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldAllTriggeredAbility; -import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.DynamicValue; @@ -39,6 +38,8 @@ import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesV import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AddManaInAnyCombinationEffect; +import mage.abilities.effects.common.ManaEffect; +import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; @@ -77,8 +78,8 @@ public class SelvalaHeartOfTheWilds extends CardImpl { this.addAbility(new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new SelvalaHeartOfTheWildsEffect(), filter, false, SetTargetPointer.PERMANENT, rule)); // {G}, {T}: Add X mana in any combination of colors to your mana pool, where X is the greatest power among creatures you control. - Effect effect = new AddManaInAnyCombinationEffect(new GreatestPowerAmongControlledCreaturesValue(), rule2, ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new ManaCostsImpl("{G}")); + ManaEffect manaEffect = new AddManaInAnyCombinationEffect(new GreatestPowerAmongControlledCreaturesValue(), rule2, ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G); + Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, manaEffect, new ManaCostsImpl("{G}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); From 368dd9a5beeb3ed756465e4fb2d8ae65809e950c Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 9 Mar 2017 12:26:50 -0600 Subject: [PATCH 15/15] - Fixed Possibility Storm. Issue: https://github.com/magefree/mage/issues/2685 --- Mage.Sets/src/mage/cards/p/PossibilityStorm.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java index 8e58925238..5595ed16ed 100644 --- a/Mage.Sets/src/mage/cards/p/PossibilityStorm.java +++ b/Mage.Sets/src/mage/cards/p/PossibilityStorm.java @@ -28,8 +28,6 @@ package mage.cards.p; import java.util.EnumSet; -import java.util.List; -import java.util.Set; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; @@ -131,11 +129,18 @@ class PossibilityStormEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); + boolean noLongerOnStack = false; // spell was exiled already by another effect, for example NivMagus Elemental + if (spell == null) { + spell = ((Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK)); + noLongerOnStack = true; + } MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && spell != null) { Player spellController = game.getPlayer(spell.getControllerId()); - if (spellController != null - && spellController.moveCardsToExile(spell, source, game, true, source.getSourceId(), sourceObject.getIdName())) { + if (spellController != null) { + if (!noLongerOnStack) { + spellController.moveCardsToExile(spell, source, game, true, source.getSourceId(), sourceObject.getIdName()); + } if (spellController.getLibrary().hasCards()) { Library library = spellController.getLibrary(); Card card;