From 7b6860447122bc7111f90339859b3a399eaecb95 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 3 Oct 2015 13:33:22 +0200 Subject: [PATCH] Some changes/fixes to conspire ability. --- .../src/mage/sets/shadowmoor/AEthertow.java | 2 +- .../sets/shadowmoor/BarkshellBlessing.java | 8 +- .../src/mage/sets/shadowmoor/BurnTrail.java | 4 +- .../mage/sets/shadowmoor/DisturbingPlot.java | 2 +- .../sets/shadowmoor/GhastlyDiscovery.java | 2 +- .../mage/sets/shadowmoor/Giantbaiting.java | 2 +- .../mage/sets/shadowmoor/GleefulSabotage.java | 4 +- .../mage/sets/shadowmoor/MemorySluice.java | 6 +- .../mage/sets/shadowmoor/MineExcavation.java | 8 +- .../mage/sets/shadowmoor/TraitorsRoar.java | 2 +- .../abilities/keywords/ConspireTest.java | 95 ++++++--- Mage/src/mage/abilities/AbilityImpl.java | 32 +-- .../abilities/keyword/ConspireAbility.java | 191 +++++++++++------- Mage/src/mage/game/stack/StackObjImpl.java | 4 +- 14 files changed, 234 insertions(+), 128 deletions(-) diff --git a/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java b/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java index 2e384acbeb..14e313f4a5 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/AEthertow.java @@ -58,7 +58,7 @@ public class AEthertow extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public AEthertow(final AEthertow card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java b/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java index 7916a18bfe..db328a7899 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/BarkshellBlessing.java @@ -41,7 +41,7 @@ import mage.target.common.TargetCreaturePermanent; * @author jeffwadsworth */ public class BarkshellBlessing extends CardImpl { - + public BarkshellBlessing(UUID ownerId) { super(ownerId, 224, "Barkshell Blessing", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G/W}"); this.expansionSetCode = "SHM"; @@ -51,13 +51,13 @@ public class BarkshellBlessing extends CardImpl { this.getSpellAbility().addEffect(new BoostTargetEffect(2, 2, Duration.EndOfTurn)); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } - + public BarkshellBlessing(final BarkshellBlessing card) { super(card); } - + @Override public BarkshellBlessing copy() { return new BarkshellBlessing(this); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java index 0e4a193204..1a0e264ca3 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/BurnTrail.java @@ -48,9 +48,9 @@ public class BurnTrail extends CardImpl { // Burn Trail deals 3 damage to target creature or player. this.getSpellAbility().addEffect(new DamageTargetEffect(3)); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); - + // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public BurnTrail(final BurnTrail card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java b/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java index 5e22bf9f40..9d7cf44593 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/DisturbingPlot.java @@ -51,7 +51,7 @@ public class DisturbingPlot extends CardImpl { this.getSpellAbility().addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard"))); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java b/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java index 2b317197e3..2ba3dd8d91 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GhastlyDiscovery.java @@ -52,7 +52,7 @@ public class GhastlyDiscovery extends CardImpl { this.getSpellAbility().addEffect(new GhastlyDiscoveryEffect()); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); } public GhastlyDiscovery(final GhastlyDiscovery card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java b/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java index 43ca95f1ff..146c80a3a3 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Giantbaiting.java @@ -58,7 +58,7 @@ public class Giantbaiting extends CardImpl { this.getSpellAbility().addEffect(new GiantbaitingEffect()); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.NONE)); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java index 16869be838..faf0258405 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java @@ -49,9 +49,9 @@ public class GleefulSabotage extends CardImpl { // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); - + // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public GleefulSabotage(final GleefulSabotage card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java b/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java index 61329fd77a..80c47736dd 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/MemorySluice.java @@ -48,10 +48,10 @@ public class MemorySluice extends CardImpl { // Target player puts the top four cards of his or her library into his or her graveyard. this.getSpellAbility().addEffect(new PutLibraryIntoGraveTargetEffect(4)); this.getSpellAbility().addTarget(new TargetPlayer()); - + // Conspire - this.addAbility(new ConspireAbility(this)); - + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); + } public MemorySluice(final MemorySluice card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java b/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java index b26e24c4b1..4b5484404e 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/MineExcavation.java @@ -43,9 +43,9 @@ import mage.target.common.TargetCardInGraveyard; * @author jeffwadsworth */ public class MineExcavation extends CardImpl { - + private static final FilterCard filter = new FilterCard("artifact or enchantment card in a graveyard"); - + static { filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); @@ -58,9 +58,9 @@ public class MineExcavation extends CardImpl { // Return target artifact or enchantment card from a graveyard to its owner's hand. this.getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter)); - + // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } public MineExcavation(final MineExcavation card) { diff --git a/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java b/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java index 90021a3dbb..df7145b391 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/TraitorsRoar.java @@ -64,7 +64,7 @@ public class TraitorsRoar extends CardImpl { this.getSpellAbility().addEffect(new TraitorsRoarEffect()); // Conspire - this.addAbility(new ConspireAbility(this)); + this.addAbility(new ConspireAbility(getId(), ConspireAbility.ConspireTargets.ONE)); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java index eda7266347..a9ef3d1e76 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ConspireTest.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -40,31 +39,31 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class ConspireTest extends CardTestPlayerBase { /** - * 702.77. Conspire - * 702.77a Conspire is a keyword that represents two abilities. The first is a static ability that functions - * while the spell with conspire is on the stack. The second is a triggered ability that functions - * while the spell with conspire is on the stack. “Conspire” means “As an additional cost to cast - * this spell, you may tap two untapped creatures you control that each share a color with it” and - * “When you cast this spell, if its conspire cost was paid, copy it. If the spell has any targets, you - * may choose new targets for the copy.” Paying a spell’s conspire cost follows the rules for - * paying additional costs in rules 601.2b and 601.2e–g. - * - * 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers based on - * its own payment, not any other instance of conspire - * - */ - - /** - * Burn Trail - * Sorcery, 3R (4) - * Burn Trail deals 3 damage to target creature or player. - * - * Conspire (As you cast this spell, you may tap two untapped creatures you - * control that share a color with it. When you do, copy it and you may - * choose a new target for the copy.) + * 702.77. Conspire 702.77a Conspire is a keyword that represents two + * abilities. The first is a static ability that functions while the spell + * with conspire is on the stack. The second is a triggered ability that + * functions while the spell with conspire is on the stack. “Conspire” means + * “As an additional cost to cast this spell, you may tap two untapped + * creatures you control that each share a color with it” and “When you cast + * this spell, if its conspire cost was paid, copy it. If the spell has any + * targets, you may choose new targets for the copy.” Paying a spell’s + * conspire cost follows the rules for paying additional costs in rules + * 601.2b and 601.2e–g. + * + * 702.77b If a spell has multiple instances of conspire, each is paid + * separately and triggers based on its own payment, not any other instance + * of conspire + * + */ + /** + * Burn Trail Sorcery, 3R (4) Burn Trail deals 3 damage to target creature + * or player. + * + * Conspire (As you cast this spell, you may tap two untapped creatures you + * control that share a color with it. When you do, copy it and you may + * choose a new target for the copy.) * */ - @Test public void testConspire() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); @@ -72,7 +71,6 @@ public class ConspireTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); addCard(Zone.HAND, playerA, "Burn Trail"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB); setChoice(playerA, "Yes"); @@ -93,7 +91,6 @@ public class ConspireTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin"); addCard(Zone.HAND, playerA, "Burn Trail"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Burn Trail", playerB); setChoice(playerA, "No"); @@ -107,4 +104,50 @@ public class ConspireTest extends CardTestPlayerBase { } + @Test + public void testWortTheRaidmother() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 7); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Lightning Bolt"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother");// {4}{R/G}{R/G} + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "Yes"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertLife(playerB, 14); + + } + + @Test + public void testWortTheRaidmotherWithConspireSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 10); + addCard(Zone.BATTLEFIELD, playerA, "Raging Goblin", 2); + // When Wort, the Raidmother enters the battlefield, put two 1/1 red and green Goblin Warrior creature tokens onto the battlefield. + // Each red or green instant or sorcery spell you cast has conspire. + // (As you cast the spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.) + addCard(Zone.HAND, playerA, "Wort, the Raidmother"); + addCard(Zone.HAND, playerA, "Burn Trail"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Wort, the Raidmother"); // {4}{R/G}{R/G} + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Burn Trail", playerB); + setChoice(playerA, "Yes"); // use Conspire from Burn Trail itself + setChoice(playerA, "Yes"); // use Conspire gained from Wort, the Raidmother + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertPermanentCount(playerA, "Wort, the Raidmother", 1); + assertLife(playerB, 11); + assertLife(playerA, 20); + assertGraveyardCount(playerA, "Burn Trail", 1); + + } } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 5074663605..150ddd0747 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -445,20 +445,28 @@ public abstract class AbilityImpl implements Ability { public boolean activateAlternateOrAdditionalCosts(MageObject sourceObject, boolean noMana, Player controller, Game game) { boolean alternativeCostisUsed = false; if (sourceObject != null && !(sourceObject instanceof Permanent) && !(this instanceof FlashbackAbility)) { - for (Ability ability : sourceObject.getAbilities()) { - // if cast for noMana no Alternative costs are allowed - if (!noMana && ability instanceof AlternativeSourceCosts) { - AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; - if (alternativeSpellCosts.isAvailable(this, game)) { - if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { - // only one alternative costs may be activated - alternativeCostisUsed = true; - break; + Abilities abilities = null; + if (sourceObject instanceof Card) { + abilities = ((Card) sourceObject).getAbilities(game); + } else { + sourceObject.getAbilities(); + } + if (abilities != null) { + for (Ability ability : abilities) { + // if cast for noMana no Alternative costs are allowed + if (!noMana && ability instanceof AlternativeSourceCosts) { + AlternativeSourceCosts alternativeSpellCosts = (AlternativeSourceCosts) ability; + if (alternativeSpellCosts.isAvailable(this, game)) { + if (alternativeSpellCosts.askToActivateAlternativeCosts(this, game)) { + // only one alternative costs may be activated + alternativeCostisUsed = true; + break; + } } } - } - if (ability instanceof OptionalAdditionalSourceCosts) { - ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); + if (ability instanceof OptionalAdditionalSourceCosts) { + ((OptionalAdditionalSourceCosts) ability).addOptionalAdditionalCosts(this, game); + } } } // controller specific alternate spell costs diff --git a/Mage/src/mage/abilities/keyword/ConspireAbility.java b/Mage/src/mage/abilities/keyword/ConspireAbility.java index fe470c425f..7d48fe50e0 100644 --- a/Mage/src/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/mage/abilities/keyword/ConspireAbility.java @@ -27,14 +27,16 @@ */ package mage.abilities.keyword; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; +import java.util.UUID; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.StaticAbility; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.Costs; -import mage.abilities.costs.OptionalAdditionalCost; import mage.abilities.costs.OptionalAdditionalCostImpl; import mage.abilities.costs.OptionalAdditionalSourceCosts; import mage.abilities.costs.common.TapTargetCost; @@ -50,48 +52,77 @@ import mage.filter.predicate.permanent.TappedPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; -/** - * 702.77. Conspire 702.77a Conspire is a keyword that represents two abilities. - * The first is a static ability that functions while the spell with conspire is - * on the stack. The second is a triggered ability that functions while the - * spell with conspire is on the stack. "Conspire" means "As an additional cost - * to cast this spell, you may tap two untapped creatures you control that each - * share a color with it" and "When you cast this spell, if its conspire cost - * was paid, copy it. If the spell has any targets, you may choose new targets - * for the copy." Paying a spell’s conspire cost follows the rules for paying - * additional costs in rules 601.2b and 601.2e–g. 702.77b If a spell has - * multiple instances of conspire, each is paid separately and triggers based on - * its own payment, not any other instance of conspire. * +/* + * 702.77. Conspire + * 702.77a Conspire is a keyword that represents two abilities. + * The first is a static ability that functions while the spell with conspire is on the stack. + * The second is a triggered ability that functions while the spell with conspire is on the stack. + * "Conspire" means "As an additional cost to cast this spell, + * you may tap two untapped creatures you control that each share a color with it" + * and "When you cast this spell, if its conspire cost was paid, copy it. + * If the spell has any targets, you may choose new targets for the copy." + * Paying a spell’s conspire cost follows the rules for paying additional costs in rules 601.2b and 601.2e–g. + * 702.77b If a spell has multiple instances of conspire, each is paid separately and triggers + * based on its own payment, not any other instance of conspire. * * * @author jeffwadsworth heavily based off the replicate keyword by LevelX */ public class ConspireAbility extends StaticAbility implements OptionalAdditionalSourceCosts { private static final String keywordText = "Conspire"; - private static final String reminderTextCost = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("two untapped creatures you control that share a color with it"); + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("untapped creatures you control that share a color with it"); + protected static final String CONSPIRE_ACTIVATION_KEY = "ConspireActivation"; static { filter.add(Predicates.not(new TappedPredicate())); filter.add(new SharesColorWithSourcePredicate()); } - Cost costConspire = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true)); - OptionalAdditionalCost conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderTextCost, costConspire); + public enum ConspireTargets { - public ConspireAbility(Card card) { + NONE, + ONE, + MORE + } + + private UUID conspireId; + private String reminderText; + private OptionalAdditionalCostImpl conspireCost; + + /** + * Unique Id for a ConspireAbility but may not change while a continuous + * effect gives Conspire + * + * @param conspireId + * @param conspireTargets controls the content of the reminder text + */ + public ConspireAbility(UUID conspireId, ConspireTargets conspireTargets) { super(Zone.STACK, null); - setRuleAtTheTop(false); - addSubAbility(new ConspireTriggeredAbility()); + this.conspireId = conspireId; + switch (conspireTargets) { + case NONE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it.)"; + break; + case ONE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; + break; + case MORE: + reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new targets for the copy.)"; + break; + } + conspireCost = new OptionalAdditionalCostImpl(keywordText, "-", reminderText, + new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true))); + addSubAbility(new ConspireTriggeredAbility(conspireId)); } public ConspireAbility(final ConspireAbility ability) { super(ability); - conspireCost = ability.conspireCost; + this.conspireId = ability.conspireId; + this.conspireCost = ability.conspireCost.copy(); + this.reminderText = ability.reminderText; } @Override @@ -106,18 +137,21 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional } } - @Override - public boolean isActivated() { - if (conspireCost != null) { - return conspireCost.isActivated(); - } - return false; + public UUID getConspireId() { + return conspireId; } - public void resetConspire() { - if (conspireCost != null) { - conspireCost.reset(); + @Override + public boolean isActivated() { + throw new UnsupportedOperationException("Use ConspireAbility.isActivated(Ability ability, Game game) method instead!"); + } + + public boolean isActivated(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations != null) { + return activations.contains(getConspireId()); } + return false; } @Override @@ -125,9 +159,9 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional if (ability instanceof SpellAbility) { Player player = game.getPlayer(controllerId); if (player != null) { - this.resetConspire(); - if (player.chooseUse(Outcome.Benefit, new StringBuilder("Pay ").append(conspireCost.getText(false)).append(" ?").toString(), ability, game)) { - conspireCost.activate(); + resetConspire(ability, game); + if (player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) { + activateConspire(ability, game); for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext();) { Cost cost = (Cost) it.next(); ability.getCosts().add(cost.copy()); @@ -137,6 +171,22 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional } } + private void activateConspire(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations == null) { + activations = new HashSet<>(); + game.getState().setValue(CONSPIRE_ACTIVATION_KEY + ability.getId(), activations); + } + activations.add(getConspireId()); + } + + private void resetConspire(Ability ability, Game game) { + Set activations = (Set) game.getState().getValue(CONSPIRE_ACTIVATION_KEY + ability.getId()); + if (activations != null) { + activations.remove(getConspireId()); + } + } + @Override public String getRule() { StringBuilder sb = new StringBuilder(); @@ -167,13 +217,17 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional class ConspireTriggeredAbility extends TriggeredAbilityImpl { - public ConspireTriggeredAbility() { + private UUID conspireId; + + public ConspireTriggeredAbility(UUID conspireId) { super(Zone.STACK, new ConspireEffect()); + this.conspireId = conspireId; this.setRuleVisible(false); } private ConspireTriggeredAbility(final ConspireTriggeredAbility ability) { super(ability); + this.conspireId = ability.conspireId; } @Override @@ -188,20 +242,18 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.sourceId)) { - StackObject spell = game.getStack().getStackObject(this.sourceId); - if (spell instanceof Spell) { - Card card = game.getCard(spell.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ConspireAbility) { - if (((ConspireAbility) ability).isActivated()) { - for (Effect effect : this.getEffects()) { - effect.setValue("ConspireSpell", spell); - } - return true; + if (event.getSourceId().equals(getSourceId())) { + Spell spell = game.getStack().getSpell(event.getSourceId()); + for (Ability ability : spell.getAbilities(game)) { + if (ability instanceof ConspireAbility + && ((ConspireAbility) ability).getConspireId().equals(getConspireId())) { + if (((ConspireAbility) ability).isActivated(spell.getSpellAbility(), game)) { + for (Effect effect : this.getEffects()) { + if (effect instanceof ConspireEffect) { + ((ConspireEffect) effect).setConspiredSpell(spell); } } + return true; } } } @@ -209,52 +261,53 @@ class ConspireTriggeredAbility extends TriggeredAbilityImpl { return false; } + public UUID getConspireId() { + return conspireId; + } + @Override public String getRule() { - return "Conspire: As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose a new target for the copy.)"; + return "When you pay the conspire costs, copy it and you may choose a new target for the copy."; } } class ConspireEffect extends OneShotEffect { + private Spell conspiredSpell; + public ConspireEffect() { super(Outcome.Copy); } public ConspireEffect(final ConspireEffect effect) { super(effect); + this.conspiredSpell = effect.conspiredSpell; } @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Spell spell = (Spell) this.getValue("ConspireSpell"); - if (spell != null) { - Card card = game.getCard(spell.getSourceId()); - if (card != null) { - for (Ability ability : card.getAbilities()) { - if (ability instanceof ConspireAbility) { - if (((ConspireAbility) ability).isActivated()) { - ((ConspireAbility) ability).resetConspire(); - } - } - } - Spell copy = spell.copySpell(); - copy.setControllerId(source.getControllerId()); - copy.setCopiedSpell(true); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - if (!game.isSimulation()) { - game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString()); - } - return true; + if (controller != null && conspiredSpell != null) { + Card card = game.getCard(conspiredSpell.getSourceId()); + if (card != null) { + Spell copy = conspiredSpell.copySpell(); + copy.setControllerId(source.getControllerId()); + copy.setCopiedSpell(true); + game.getStack().push(copy); + copy.chooseNewTargets(game, source.getControllerId()); + if (!game.isSimulation()) { + game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game)); } + return true; } } return false; } + public void setConspiredSpell(Spell conspiredSpell) { + this.conspiredSpell = conspiredSpell; + } + @Override public ConspireEffect copy() { return new ConspireEffect(this); diff --git a/Mage/src/mage/game/stack/StackObjImpl.java b/Mage/src/mage/game/stack/StackObjImpl.java index 8ca3c7a6be..6cf33e65ea 100644 --- a/Mage/src/mage/game/stack/StackObjImpl.java +++ b/Mage/src/mage/game/stack/StackObjImpl.java @@ -106,6 +106,7 @@ public abstract class StackObjImpl implements StackObject { public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { Player targetController = game.getPlayer(targetControllerId); if (targetController != null) { + StringBuilder oldTargetDescription = new StringBuilder(); StringBuilder newTargetDescription = new StringBuilder(); // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability Abilities objectAbilities = new AbilitiesImpl<>(); @@ -118,6 +119,7 @@ public abstract class StackObjImpl implements StackObject { // Some spells can have more than one mode for (UUID modeId : ability.getModes().getSelectedModes()) { Mode mode = ability.getModes().get(modeId); + oldTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); for (Target target : mode.getTargets()) { Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); // clear the old target and copy all targets from new target @@ -131,7 +133,7 @@ public abstract class StackObjImpl implements StackObject { } } - if (newTargetDescription.length() > 0 && !game.isSimulation()) { + if (!newTargetDescription.toString().equals(oldTargetDescription.toString()) && !game.isSimulation()) { game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString()); } return true;