From 8823839a42d29f4d3310a344a55708c3d93da975 Mon Sep 17 00:00:00 2001 From: emerald000 Date: Sun, 17 Apr 2016 23:55:11 -0400 Subject: [PATCH] Added framework method for copying a StackAbility without casting it. Modified the effects doing so with the new method. --- .../src/mage/player/ai/SimulatedPlayer2.java | 8 ++- .../mage/player/ai/SimulatedPlayerMCTS.java | 2 + .../src/mage/player/ai/SimulatedPlayer.java | 12 +++-- .../mage/sets/alarareborn/ClovenCasting.java | 48 ++---------------- .../battleforzendikar/ZadaHedronGrinder.java | 2 +- .../sets/commander/RikuOfTwoReflections.java | 48 ++---------------- .../src/mage/sets/commander/WildRicochet.java | 49 +++---------------- .../commander2014/MaliciousAffliction.java | 15 +++--- .../sets/darkascension/CurseOfEchoes.java | 4 +- .../darkascension/IncreasingVengeance.java | 17 ++++--- .../src/mage/sets/eventide/MirrorSheen.java | 44 ++--------------- .../sets/gatecrash/IllusionistsBracers.java | 16 ++---- .../src/mage/sets/legends/ChainLightning.java | 6 +-- Mage.Sets/src/mage/sets/limitedbeta/Fork.java | 2 + .../mage/sets/lorwyn/RingsOfBrighthearth.java | 16 ++---- .../src/mage/sets/magic2010/HiveMind.java | 4 +- .../sets/magic2014/StrionicResonator.java | 15 +----- .../sets/magic2015/KurkeshOnakkeAncient.java | 16 ++---- .../sets/magicorigins/PsychicRebuttal.java | 23 +++++---- .../src/mage/sets/onslaught/ChainOfVapor.java | 15 +++--- .../mage/sets/riseoftheeldrazi/EchoMage.java | 7 +-- .../mage/sets/theros/MeletisCharlatan.java | 15 +++--- .../org/mage/test/player/RandomPlayer.java | 2 + .../effects/common/CopyTargetSpellEffect.java | 19 +++---- .../abilities/effects/common/EpicEffect.java | 5 +- .../abilities/keyword/ConspireAbility.java | 9 ++-- .../abilities/keyword/GravestormAbility.java | 4 +- .../abilities/keyword/ReplicateAbility.java | 8 ++- .../mage/abilities/keyword/StormAbility.java | 4 +- .../main/java/mage/game/events/GameEvent.java | 2 + Mage/src/main/java/mage/game/stack/Spell.java | 12 +++++ .../java/mage/game/stack/StackAbility.java | 17 +++++++ .../java/mage/game/stack/StackObject.java | 2 + .../main/java/mage/players/PlayerImpl.java | 3 ++ 34 files changed, 158 insertions(+), 313 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index 058d42b6e6..3737ba4251 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -419,7 +419,9 @@ public class SimulatedPlayer2 extends ComputerPlayer { if (options.isEmpty()) { logger.debug("simulating -- triggered ability:" + ability); game.getStack().push(new StackAbility(ability, playerId)); - ability.activate(game, false); + if (ability.activate(game, false) && ability.isUsesStack()) { + game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); + } game.applyEffects(); game.getPlayers().resetPassed(); } else { @@ -439,7 +441,9 @@ public class SimulatedPlayer2 extends ComputerPlayer { protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) { Game sim = game.copy(); sim.getStack().push(new StackAbility(ability, playerId)); - ability.activate(sim, false); + if (ability.activate(sim, false) && ability.isUsesStack()) { + game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); + } sim.applyEffects(); SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId); logger.debug("simulating -- node #:" + SimulationNode2.getCount() + " triggered ability option"); diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 6e0ffde444..60212a9ee1 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -47,6 +47,7 @@ import mage.choices.Choice; import mage.constants.Outcome; import mage.game.Game; import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.players.Player; @@ -168,6 +169,7 @@ public class SimulatedPlayerMCTS extends MCTSPlayer { if (ability.isUsesStack()) { game.getStack().push(new StackAbility(ability, playerId)); if (ability.activate(game, false)) { + game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); actionCount++; return true; } diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java index a7547ce964..bbdd0ce972 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/SimulatedPlayer.java @@ -28,6 +28,8 @@ package mage.player.ai; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; @@ -42,9 +44,6 @@ import mage.game.stack.StackAbility; import mage.target.Target; import org.apache.log4j.Logger; -import java.util.*; -import java.util.concurrent.ConcurrentLinkedQueue; - /** * * @author BetaSteward_at_googlemail.com @@ -239,7 +238,9 @@ public class SimulatedPlayer extends ComputerPlayer { if (logger.isDebugEnabled()) logger.debug("simulating -- triggered ability:" + ability); game.getStack().push(new StackAbility(ability, playerId)); - ability.activate(game, false); + if (ability.activate(game, false) && ability.isUsesStack()) { + game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); + } game.applyEffects(); game.getPlayers().resetPassed(); } @@ -258,6 +259,9 @@ public class SimulatedPlayer extends ComputerPlayer { Game sim = game.copy(); sim.getStack().push(new StackAbility(ability, playerId)); ability.activate(sim, false); + if (ability.activate(sim, false) && ability.isUsesStack()) { + game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); + } sim.applyEffects(); SimulationNode newNode = new SimulationNode(parent, sim, playerId); logger.debug(indent(newNode.getDepth()) + "simulating -- node #:" + SimulationNode.getCount() + " triggered ability option"); diff --git a/Mage.Sets/src/mage/sets/alarareborn/ClovenCasting.java b/Mage.Sets/src/mage/sets/alarareborn/ClovenCasting.java index a1c3cab74d..efbb7b370a 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/ClovenCasting.java +++ b/Mage.Sets/src/mage/sets/alarareborn/ClovenCasting.java @@ -28,22 +28,18 @@ package mage.sets.alarareborn; import java.util.UUID; -import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.MulticoloredPredicate; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; /** * @@ -65,8 +61,9 @@ public class ClovenCasting extends CardImpl { this.expansionSetCode = "ARB"; // Whenever you cast a multicolored instant or sorcery spell, you may pay {1}. If you do, copy that spell. You may choose new targets for the copy. - this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new ClovenCastingEffect(), new GenericManaCost(1)), filter, true, true)); - + Effect effect = new CopyTargetSpellEffect(); + effect.setText("copy that spell. You may choose new targets for the copy"); + this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new GenericManaCost(1)), filter, true, true)); } public ClovenCasting(final ClovenCasting card) { @@ -78,38 +75,3 @@ public class ClovenCasting extends CardImpl { return new ClovenCasting(this); } } - -class ClovenCastingEffect extends OneShotEffect { - - public ClovenCastingEffect() { - super(Outcome.Copy); - staticText = "copy that spell. You may choose new targets for the copy"; - } - - public ClovenCastingEffect(final ClovenCastingEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - Player player = game.getPlayer(source.getControllerId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + " copies " + activateMessage); - return true; - } - return false; - } - - @Override - public ClovenCastingEffect copy() { - return new ClovenCastingEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java b/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java index 28a4e0eb10..129686fc33 100644 --- a/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java +++ b/Mage.Sets/src/mage/sets/battleforzendikar/ZadaHedronGrinder.java @@ -196,7 +196,7 @@ class ZadaHedronGrinderEffect extends OneShotEffect { } } } - + game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, copy.getId(), spell.getId(), source.getControllerId())); String activateMessage = copy.getActivatedMessage(game); if (activateMessage.startsWith(" casts ")) { activateMessage = activateMessage.substring(6); diff --git a/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java b/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java index ad2b89f9ae..ce0fbc2e75 100644 --- a/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java +++ b/Mage.Sets/src/mage/sets/commander/RikuOfTwoReflections.java @@ -29,17 +29,15 @@ package mage.sets.commander; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.PutTokenOntoBattlefieldCopyTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SetTargetPointer; import mage.constants.Zone; @@ -49,9 +47,6 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.TokenPredicate; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; /** * @@ -82,10 +77,12 @@ public class RikuOfTwoReflections extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast an instant or sorcery spell, you may pay {U}{R}. If you do, copy that spell. You may choose new targets for the copy. - this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(new RikuOfTwoReflectionsCopyEffect(), new ManaCostsImpl("{U}{R}")), filter, false, true)); + Effect effect = new CopyTargetSpellEffect(); + effect.setText("copy that spell. You may choose new targets for the copy"); + this.addAbility(new SpellCastControllerTriggeredAbility(new DoIfCostPaid(effect, new ManaCostsImpl("{U}{R}")), filter, false, true)); // Whenever another nontoken creature enters the battlefield under your control, you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield. - Effect effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(), + effect = new DoIfCostPaid(new PutTokenOntoBattlefieldCopyTargetEffect(), new ManaCostsImpl("{G}{U}"), "Put a token that's a copy of that creature onto the battlefield?"); effect.setText("you may pay {G}{U}. If you do, put a token that's a copy of that creature onto the battlefield"); this.addAbility(new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, effect, filterPermanent, false, SetTargetPointer.PERMANENT, null)); @@ -100,38 +97,3 @@ public class RikuOfTwoReflections extends CardImpl { return new RikuOfTwoReflections(this); } } - -class RikuOfTwoReflectionsCopyEffect extends OneShotEffect { - - public RikuOfTwoReflectionsCopyEffect() { - super(Outcome.Copy); - staticText = "copy that spell. You may choose new targets for the copy"; - } - - public RikuOfTwoReflectionsCopyEffect(final RikuOfTwoReflectionsCopyEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId());; - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - Player player = game.getPlayer(source.getControllerId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + " copies " + activateMessage); - return true; - } - return false; - } - - @Override - public RikuOfTwoReflectionsCopyEffect copy() { - return new RikuOfTwoReflectionsCopyEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/commander/WildRicochet.java b/Mage.Sets/src/mage/sets/commander/WildRicochet.java index d70469f675..14276e959b 100644 --- a/Mage.Sets/src/mage/sets/commander/WildRicochet.java +++ b/Mage.Sets/src/mage/sets/commander/WildRicochet.java @@ -28,18 +28,15 @@ package mage.sets.commander; import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.ChooseNewTargetsTargetEffect; +import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.filter.FilterStackObject; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.Game; -import mage.game.stack.Spell; -import mage.players.Player; import mage.target.TargetStackObject; /** @@ -61,9 +58,11 @@ public class WildRicochet extends CardImpl { this.expansionSetCode = "CMD"; // You may choose new targets for target instant or sorcery spell. Then copy that spell. You may choose new targets for the copy. - this.getSpellAbility().addEffect(new WildRicochetEffect()); + this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect()); + Effect effect = new CopyTargetSpellEffect(); + effect.setText("Then copy that spell. You may choose new targets for the copy"); + this.getSpellAbility().addEffect(effect); this.getSpellAbility().addTarget(new TargetStackObject(filter)); - } public WildRicochet(final WildRicochet card) { @@ -75,37 +74,3 @@ public class WildRicochet extends CardImpl { return new WildRicochet(this); } } - -class WildRicochetEffect extends OneShotEffect { - - public WildRicochetEffect() { - super(Outcome.Neutral); - staticText = "You may choose new targets for target instant or sorcery spell. Then copy that spell. You may choose new targets for the copy"; - } - - public WildRicochetEffect(final WildRicochetEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(source.getFirstTarget()); - Player you = game.getPlayer(source.getControllerId()); - if (spell != null && you != null && you.chooseUse(Outcome.Benefit, "Do you wish to choose new targets for " + spell.getName() + "?", source, game)) { - spell.chooseNewTargets(game, you.getId()); - } - if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId());; - game.getStack().push(copy); - if (you != null && you.chooseUse(Outcome.Benefit, "Do you wish to choose new targets for the copied " + spell.getName() + "?", source, game)) { - return copy.chooseNewTargets(game, you.getId()); - } - } - return false; - } - - @Override - public WildRicochetEffect copy() { - return new WildRicochetEffect(this); - } -} diff --git a/Mage.Sets/src/mage/sets/commander2014/MaliciousAffliction.java b/Mage.Sets/src/mage/sets/commander2014/MaliciousAffliction.java index e6422dd254..7294ecfd17 100644 --- a/Mage.Sets/src/mage/sets/commander2014/MaliciousAffliction.java +++ b/Mage.Sets/src/mage/sets/commander2014/MaliciousAffliction.java @@ -45,6 +45,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetCreaturePermanent; @@ -106,14 +107,14 @@ class CopySourceSpellEffect extends OneShotEffect { if (controller != null) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - Spell spellCopy = spell.copySpell(source.getControllerId());; - game.getStack().push(spellCopy); - spellCopy.chooseNewTargets(game, controller.getId()); - String activateMessage = spellCopy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); + StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true); + if (stackObjectCopy != null && stackObjectCopy instanceof Spell) { + String activateMessage = ((Spell) stackObjectCopy).getActivatedMessage(game); + if (activateMessage.startsWith(" casts ")) { + activateMessage = activateMessage.substring(6); + } + game.informPlayers(controller.getLogName() + " copies " + activateMessage); } - game.informPlayers(controller.getLogName() + " copies " + activateMessage); return true; } } diff --git a/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java b/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java index cb83b54e46..dd08ebaf84 100644 --- a/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java +++ b/Mage.Sets/src/mage/sets/darkascension/CurseOfEchoes.java @@ -151,9 +151,7 @@ class CurseOfEchoesEffect extends OneShotEffect { if (!playerId.equals(spell.getControllerId())) { Player player = game.getPlayer(playerId); if (player.chooseUse(Outcome.Copy, chooseMessage, source, game)) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, playerId); + spell.createCopyOnStack(game, source, player.getId(), true); } } } diff --git a/Mage.Sets/src/mage/sets/darkascension/IncreasingVengeance.java b/Mage.Sets/src/mage/sets/darkascension/IncreasingVengeance.java index f39db84823..0c47025e72 100644 --- a/Mage.Sets/src/mage/sets/darkascension/IncreasingVengeance.java +++ b/Mage.Sets/src/mage/sets/darkascension/IncreasingVengeance.java @@ -40,6 +40,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; @@ -99,17 +100,17 @@ class IncreasingVengeanceEffect extends OneShotEffect { if (controller != null) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString()); + StackObject stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true); + if (stackObjectCopy != null && stackObjectCopy instanceof Spell) { + game.informPlayers(new StringBuilder(controller.getLogName()).append(((Spell) stackObjectCopy).getActivatedMessage(game)).toString()); + } Spell sourceSpell = (Spell) game.getStack().getStackObject(source.getSourceId()); if (sourceSpell != null) { if (sourceSpell.getFromZone() == Zone.GRAVEYARD) { - copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - game.informPlayers(new StringBuilder(controller.getLogName()).append(copy.getActivatedMessage(game)).toString()); + stackObjectCopy = spell.createCopyOnStack(game, source, source.getControllerId(), true); + if (stackObjectCopy != null && stackObjectCopy instanceof Spell) { + game.informPlayers(new StringBuilder(controller.getLogName()).append(((Spell) stackObjectCopy).getActivatedMessage(game)).toString()); + } } } return true; diff --git a/Mage.Sets/src/mage/sets/eventide/MirrorSheen.java b/Mage.Sets/src/mage/sets/eventide/MirrorSheen.java index f699ff017c..8777596ceb 100644 --- a/Mage.Sets/src/mage/sets/eventide/MirrorSheen.java +++ b/Mage.Sets/src/mage/sets/eventide/MirrorSheen.java @@ -31,10 +31,9 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CopyTargetSpellEffect; import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.FilterSpell; @@ -43,9 +42,7 @@ import mage.filter.predicate.ObjectPlayerPredicate; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; -import mage.game.stack.Spell; import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; @@ -56,7 +53,7 @@ import mage.target.TargetSpell; */ public class MirrorSheen extends CardImpl { - private static final FilterSpell filter = new FilterSpell(); + private static final FilterSpell filter = new FilterSpell("instant or sorcery spell that targets you"); static { filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY))); @@ -68,7 +65,7 @@ public class MirrorSheen extends CardImpl { this.expansionSetCode = "EVE"; // {1}{UR}{UR}: Copy target instant or sorcery spell that targets you. You may choose new targets for the copy. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MirrorSheenEffect(), new ManaCostsImpl("{1}{U/R}{U/R}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CopyTargetSpellEffect(), new ManaCostsImpl("{1}{U/R}{U/R}")); ability.addTarget(new TargetSpell(filter)); this.addAbility(ability); @@ -84,41 +81,6 @@ public class MirrorSheen extends CardImpl { } } -class MirrorSheenEffect extends OneShotEffect { - - public MirrorSheenEffect() { - super(Outcome.Copy); - staticText = "Copy target instant or sorcery spell that targets you. You may choose new targets for the copy"; - } - - public MirrorSheenEffect(final MirrorSheenEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - Spell spell = game.getStack().getSpell(source.getFirstTarget()); - if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - Player player = game.getPlayer(source.getControllerId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - game.informPlayers(player.getLogName() + " copies " + activateMessage); - return true; - } - return false; - } - - @Override - public MirrorSheenEffect copy() { - return new MirrorSheenEffect(this); - } -} - class TargetYouPredicate implements ObjectPlayerPredicate> { @Override diff --git a/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java b/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java index 8486db2102..3b610ad491 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java +++ b/Mage.Sets/src/mage/sets/gatecrash/IllusionistsBracers.java @@ -102,7 +102,7 @@ class AbilityActivatedTriggeredAbility extends TriggeredAbilityImpl { StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); if (!(stackAbility.getStackAbility() instanceof ManaAbility)) { Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility.getStackAbility()); + effect.setValue("stackAbility", stackAbility); return true; } } @@ -133,21 +133,11 @@ class CopyActivatedAbilityEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Ability ability = (Ability) getValue("stackAbility"); + StackAbility ability = (StackAbility) getValue("stackAbility"); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (ability != null && controller != null && sourcePermanent != null) { - Ability newAbility = ability.copy(); - newAbility.newId(); - game.getStack().push(new StackAbility(newAbility, source.getControllerId())); - if (newAbility.getTargets().size() > 0) { - if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { - newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { - return false; - } - } - } + ability.createCopyOnStack(game, source, source.getControllerId(), true); game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString()); return true; } diff --git a/Mage.Sets/src/mage/sets/legends/ChainLightning.java b/Mage.Sets/src/mage/sets/legends/ChainLightning.java index 9773ee7d57..a6c47c8f8f 100644 --- a/Mage.Sets/src/mage/sets/legends/ChainLightning.java +++ b/Mage.Sets/src/mage/sets/legends/ChainLightning.java @@ -106,10 +106,8 @@ class ChainLightningEffect extends OneShotEffect { if (cost.pay(source, game, source.getSourceId(), affectedPlayer.getId(), false, null)) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - Spell copy = spell.copySpell(affectedPlayer.getId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, affectedPlayer.getId()); - game.informPlayers(affectedPlayer.getLogName() + " copies " + copy.getName() + "."); + spell.createCopyOnStack(game, source, affectedPlayer.getId(), true); + game.informPlayers(affectedPlayer.getLogName() + " copies " + spell.getName() + "."); } } } diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java index 3f7297fe11..3d1812276e 100644 --- a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java +++ b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java @@ -39,6 +39,7 @@ import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; +import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.TargetSpell; @@ -96,6 +97,7 @@ class ForkEffect extends OneShotEffect { copy.getColor(game).setRed(true); game.getStack().push(copy); copy.chooseNewTargets(game, controller.getId()); + game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, copy.getId(), spell.getId(), source.getControllerId())); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java b/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java index b3bca8a727..18ea9e59d6 100644 --- a/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java +++ b/Mage.Sets/src/mage/sets/lorwyn/RingsOfBrighthearth.java @@ -96,7 +96,7 @@ class RingsOfBrighthearthTriggeredAbility extends TriggeredAbilityImpl { StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); if (stackAbility != null && !(stackAbility.getStackAbility() instanceof ManaAbility)) { Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility.getStackAbility()); + effect.setValue("stackAbility", stackAbility); return true; } } @@ -132,21 +132,11 @@ class RingsOfBrighthearthEffect extends OneShotEffect { if (player != null) { if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? If you do, copy that ability. You may choose new targets for the copy.", source, game)) { if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { - Ability ability = (Ability) getValue("stackAbility"); + StackAbility ability = (StackAbility) getValue("stackAbility"); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (ability != null && controller != null) { - Ability newAbility = ability.copy(); - newAbility.newId(); - game.getStack().push(new StackAbility(newAbility, source.getControllerId())); - if (newAbility.getTargets().size() > 0) { - if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { - newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { - return false; - } - } - } + ability.createCopyOnStack(game, source, source.getControllerId(), true); game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString()); return true; } diff --git a/Mage.Sets/src/mage/sets/magic2010/HiveMind.java b/Mage.Sets/src/mage/sets/magic2010/HiveMind.java index 62e2d2de28..47b2c195e2 100644 --- a/Mage.Sets/src/mage/sets/magic2010/HiveMind.java +++ b/Mage.Sets/src/mage/sets/magic2010/HiveMind.java @@ -135,9 +135,7 @@ class HiveMindEffect extends OneShotEffect { if (spell != null && player != null) { for (UUID playerId : game.getState().getPlayersInRange(player.getId(), game)) { if (!playerId.equals(spell.getControllerId())) { - Spell copy = spell.copySpell(playerId); - game.getStack().push(copy); - copy.chooseNewTargets(game, playerId); + spell.createCopyOnStack(game, source, playerId, true); } } return true; diff --git a/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java b/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java index 9b1cedfdbd..03f256ba0d 100644 --- a/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java +++ b/Mage.Sets/src/mage/sets/magic2014/StrionicResonator.java @@ -92,21 +92,10 @@ class StrionicResonatorEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(targetPointer.getFirst(game, source)); if (stackAbility != null) { - Ability ability = (Ability) stackAbility.getStackAbility(); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (ability != null && controller != null && sourcePermanent != null) { - Ability newAbility = ability.copy(); - newAbility.newId(); - game.getStack().push(new StackAbility(newAbility, source.getControllerId())); - if (newAbility.getTargets().size() > 0) { - if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { - newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { - return false; - } - } - } + if (controller != null && sourcePermanent != null) { + stackAbility.createCopyOnStack(game, source, source.getControllerId(), true); game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString()); return true; } diff --git a/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java b/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java index 5212945984..eb666894c9 100644 --- a/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java +++ b/Mage.Sets/src/mage/sets/magic2015/KurkeshOnakkeAncient.java @@ -107,7 +107,7 @@ class KurkeshOnakkeAncientTriggeredAbility extends TriggeredAbilityImpl { StackAbility stackAbility = (StackAbility) game.getStack().getStackObject(event.getSourceId()); if (!(stackAbility.getStackAbility() instanceof ManaAbility)) { Effect effect = this.getEffects().get(0); - effect.setValue("stackAbility", stackAbility.getStackAbility()); + effect.setValue("stackAbility", stackAbility); return true; } } @@ -144,21 +144,11 @@ class KurkeshOnakkeAncientEffect extends OneShotEffect { if (player != null) { if (player.chooseUse(Outcome.Benefit, "Pay " + cost.getText() + "? If you do, copy that ability. You may choose new targets for the copy.", source, game)) { if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { - Ability ability = (Ability) getValue("stackAbility"); + StackAbility ability = (StackAbility) getValue("stackAbility"); Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId()); if (ability != null && controller != null) { - Ability newAbility = ability.copy(); - newAbility.newId(); - game.getStack().push(new StackAbility(newAbility, source.getControllerId())); - if (newAbility.getTargets().size() > 0) { - if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { - newAbility.getTargets().clearChosen(); - if (newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), source.getControllerId(), newAbility, false, game) == false) { - return false; - } - } - } + ability.createCopyOnStack(game, source, source.getControllerId(), true); game.informPlayers(new StringBuilder(sourcePermanent.getName()).append(": ").append(controller.getLogName()).append(" copied activated ability").toString()); return true; } diff --git a/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java b/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java index 82d88aa7db..9aa89e0b2f 100644 --- a/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java +++ b/Mage.Sets/src/mage/sets/magicorigins/PsychicRebuttal.java @@ -104,22 +104,21 @@ class PsychicRebuttalEffect extends OneShotEffect { if (controller == null) { return false; } - StackObject stackObject = game.getState().getStack().getStackObject(getTargetPointer().getFirst(game, source)); - if (stackObject != null) { - game.getStack().counter(stackObject.getId(), source.getSourceId(), game); + Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source)); + if (spell != null) { + game.getStack().counter(spell.getId(), source.getSourceId(), game); if (SpellMasteryCondition.getInstance().apply(game, source) - && controller.chooseUse(Outcome.PlayForFree, "Copy " + stackObject.getName() + " (you may choose new targets for the copy)?", source, game)) { + && controller.chooseUse(Outcome.PlayForFree, "Copy " + spell.getName() + " (you may choose new targets for the copy)?", source, game)) { - Spell copy = ((Spell) stackObject).copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - Player player = game.getPlayer(source.getControllerId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); + StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true); + if (newStackObject != null && newStackObject instanceof Spell) { + String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); + if (activateMessage.startsWith(" casts ")) { + activateMessage = activateMessage.substring(6); + } + game.informPlayers(controller.getLogName() + activateMessage); } - game.informPlayers(player.getLogName() + activateMessage); } return true; diff --git a/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java b/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java index 933d01e8f1..0cca30561d 100644 --- a/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java +++ b/Mage.Sets/src/mage/sets/onslaught/ChainOfVapor.java @@ -41,6 +41,7 @@ import mage.filter.common.FilterControlledLandPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetNonlandPermanent; @@ -103,14 +104,14 @@ class ChainOfVaporEffect extends OneShotEffect { if (player.chooseUse(outcome, "Copy the spell?", source, game)) { Spell spell = game.getStack().getSpell(source.getSourceId()); if (spell != null) { - Spell copy = spell.copySpell(player.getId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, player.getId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); + StackObject newStackObject = spell.createCopyOnStack(game, source, player.getId(), true); + if (newStackObject != null && newStackObject instanceof Spell) { + String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); + if (activateMessage.startsWith(" casts ")) { + activateMessage = activateMessage.substring(6); + } + game.informPlayers(player.getLogName() + " " + activateMessage); } - game.informPlayers(player.getLogName() + " " + activateMessage); } } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EchoMage.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EchoMage.java index 347c705252..bd9de184e4 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EchoMage.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EchoMage.java @@ -130,11 +130,8 @@ class EchoMageEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { - for (int i = 0; i < 2; i++) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - } + spell.createCopyOnStack(game, source, source.getControllerId(), true); + spell.createCopyOnStack(game, source, source.getControllerId(), true); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/theros/MeletisCharlatan.java b/Mage.Sets/src/mage/sets/theros/MeletisCharlatan.java index 061d1587a5..0ce38ddc84 100644 --- a/Mage.Sets/src/mage/sets/theros/MeletisCharlatan.java +++ b/Mage.Sets/src/mage/sets/theros/MeletisCharlatan.java @@ -44,6 +44,7 @@ import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; @@ -104,15 +105,15 @@ class MeletisCharlatanCopyTargetSpellEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, spell.getControllerId()); + StackObject newStackObject = spell.createCopyOnStack(game, source, spell.getControllerId(), true); Player player = game.getPlayer(spell.getControllerId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); + if (player != null && newStackObject != null && newStackObject instanceof Spell) { + String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); + if (activateMessage.startsWith(" casts ")) { + activateMessage = activateMessage.substring(6); + } + game.informPlayers(player.getLogName() + " copies " + activateMessage); } - game.informPlayers(player.getLogName() + " copies " + activateMessage);; return true; } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 71d14673b8..b611c66f88 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -48,6 +48,7 @@ import mage.constants.Outcome; import mage.constants.RangeOfInfluence; import mage.game.Game; import mage.game.combat.CombatGroup; +import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; import mage.player.ai.ComputerPlayer; @@ -173,6 +174,7 @@ public class RandomPlayer extends ComputerPlayer { if (ability.isUsesStack()) { game.getStack().push(new StackAbility(ability, playerId)); if (ability.activate(game, false)) { + game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); actionCount++; return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java index bf21fba8f8..567dac9b35 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CopyTargetSpellEffect.java @@ -34,6 +34,7 @@ import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; /** @@ -57,16 +58,16 @@ public class CopyTargetSpellEffect extends OneShotEffect { spell = (Spell) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.STACK); } if (spell != null) { - Spell copy = spell.copySpell(source.getControllerId());; - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); + StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true); Player player = game.getPlayer(source.getControllerId()); - String activateMessage = copy.getActivatedMessage(game); - if (activateMessage.startsWith(" casts ")) { - activateMessage = activateMessage.substring(6); - } - if (!game.isSimulation()) { - game.informPlayers(player.getLogName() + activateMessage); + if (player != null && newStackObject != null && newStackObject instanceof Spell) { + String activateMessage = ((Spell) newStackObject).getActivatedMessage(game); + if (activateMessage.startsWith(" casts ")) { + activateMessage = activateMessage.substring(6); + } + if (!game.isSimulation()) { + game.informPlayers(player.getLogName() + activateMessage); + } } return true; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java b/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java index b724e73ebd..a695f7a936 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/EpicEffect.java @@ -140,10 +140,7 @@ class EpicPushEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { if (spell != null) { - // don't change the targets of the in the origin copied spell - Spell copySpell = spell.copy(); - game.getStack().push(copySpell); - copySpell.chooseNewTargets(game, source.getControllerId()); + spell.createCopyOnStack(game, source, source.getControllerId(), true); return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java index 0908f42787..26b23a062c 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java @@ -52,6 +52,7 @@ 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; @@ -292,11 +293,9 @@ class ConspireEffect extends OneShotEffect { if (controller != null && conspiredSpell != null) { Card card = game.getCard(conspiredSpell.getSourceId()); if (card != null) { - Spell copy = conspiredSpell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game)); + StackObject newStackObject = conspiredSpell.createCopyOnStack(game, source, source.getControllerId(), true); + if (newStackObject != null && newStackObject instanceof Spell && !game.isSimulation()) { + game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game)); } return true; } diff --git a/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java b/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java index e95f1c7225..eb1601830e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/GravestormAbility.java @@ -109,9 +109,7 @@ class GravestormEffect extends OneShotEffect { game.informPlayers("Gravestorm: " + spell.getName() + " will be copied " + gravestormCount + " time" + (gravestormCount > 1 ? "s" : "")); } for (int i = 0; i < gravestormCount; i++) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); + spell.createCopyOnStack(game, source, source.getControllerId(), true); } } } diff --git a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java index ee0887b085..0499ba0d9e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ReplicateAbility.java @@ -247,11 +247,9 @@ class ReplicateCopyEffect extends OneShotEffect { } // create the copies for (int i = 0; i < replicateCount; i++) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); - if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + copy.getActivatedMessage(game)); + StackObject newStackObject = spell.createCopyOnStack(game, source, source.getControllerId(), true); + if (newStackObject != null && newStackObject instanceof Spell && !game.isSimulation()) { + game.informPlayers(controller.getLogName() + ((Spell) newStackObject).getActivatedMessage(game)); } } return true; diff --git a/Mage/src/main/java/mage/abilities/keyword/StormAbility.java b/Mage/src/main/java/mage/abilities/keyword/StormAbility.java index 27ff6fba57..8b5071e590 100644 --- a/Mage/src/main/java/mage/abilities/keyword/StormAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/StormAbility.java @@ -109,9 +109,7 @@ class StormEffect extends OneShotEffect { game.informPlayers("Storm: " + spell.getLogName() + " will be copied " + stormCount + " time" + (stormCount > 1 ? "s" : "")); } for (int i = 0; i < stormCount; i++) { - Spell copy = spell.copySpell(source.getControllerId()); - game.getStack().push(copy); - copy.chooseNewTargets(game, source.getControllerId()); + spell.createCopyOnStack(game, source, source.getControllerId(), true); } } } diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index d09bf6b59d..9497c485d4 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -125,6 +125,8 @@ public class GameEvent implements Serializable { */ SPELL_CAST, ACTIVATE_ABILITY, ACTIVATED_ABILITY, + TRIGGERED_ABILITY, + COPIED_STACKOBJECT, /* ADD_MANA targetId id of the ability that added the mana sourceId sourceId of the ability that added the mana diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 9a2eb2609c..16c0b19978 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -56,6 +56,8 @@ import mage.constants.ZoneDetail; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; @@ -873,4 +875,14 @@ public class Spell extends StackObjImpl implements Card { throw new UnsupportedOperationException("Not supported for Spell"); } + @Override + public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { + Spell copy = this.copySpell(newControllerId); + game.getStack().push(copy); + if (chooseNewTargets) { + copy.chooseNewTargets(game, newControllerId); + } + game.fireEvent(new GameEvent(EventType.COPIED_STACKOBJECT, copy.getId(), this.getId(), newControllerId)); + return copy; + } } diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index b9663190f3..ee04b6eeb2 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -580,4 +580,21 @@ public class StackAbility extends StackObjImpl implements Ability { public void setCanFizzle(boolean canFizzle) { throw new UnsupportedOperationException("Not supported."); } + + @Override + public StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets) { + Ability newAbility = this.copy(); + newAbility.newId(); + StackAbility newStackAbility = new StackAbility(newAbility, newControllerId); + game.getStack().push(newStackAbility); + if (chooseNewTargets && newAbility.getTargets().size() > 0) { + Player controller = game.getPlayer(newControllerId); + if (controller.chooseUse(newAbility.getEffects().get(0).getOutcome(), "Choose new targets?", source, game)) { + newAbility.getTargets().clearChosen(); + newAbility.getTargets().chooseTargets(newAbility.getEffects().get(0).getOutcome(), newControllerId, newAbility, false, game); + } + } + game.fireEvent(new GameEvent(GameEvent.EventType.COPIED_STACKOBJECT, newStackAbility.getId(), this.getId(), newControllerId)); + return newStackAbility; + } } diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index 0d94ac6f99..b90d5a7a2f 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -50,6 +50,8 @@ public interface StackObject extends MageObject, Controllable { // int getConvertedManaCost(); boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget); + + StackObject createCopyOnStack(Game game, Ability source, UUID newControllerId, boolean chooseNewTargets); @Override StackObject copy(); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 69d7ec91c2..3043c94eef 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1239,6 +1239,9 @@ public abstract class PlayerImpl implements Player, Serializable { if (!ability.isUsesStack()) { ability.resolve(game); } + else { + game.fireEvent(new GameEvent(EventType.TRIGGERED_ABILITY, ability.getId(), ability.getSourceId(), ability.getControllerId())); + } game.removeBookmark(bookmark); return true; }