diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java index bd953f1161e..c0afea01a25 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java @@ -214,4 +214,35 @@ public class FlashbackTest extends CardTestPlayerBase { assertHandCount(playerA, 0); } + + /** + * I cast Runic Repetition targeting a Silent Departure in exile, and + * afterwards I cast the Silent Departure from my hand. When it resolves, it + * goes back to exile instead of ending up in my graveyard. Looks like a + * problem with Runic Repetition? + */ + @Test + public void testFlashbackReturnToHandAndCastAgain() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 9); + // Return target creature to its owner's hand. + // Flashback {4}{U} + addCard(Zone.GRAVEYARD, playerA, "Silent Departure", 1); // {U} + addCard(Zone.HAND, playerA, "Runic Repetition", 1);// {2}{U} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback"); + addTarget(playerA, "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Runic Repetition"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silent Departure", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, "Silvercoat Lion", 2); + assertExileCount("Silent Departure", 0); + assertGraveyardCount(playerA, "Silent Departure", 1); + assertGraveyardCount(playerA, "Runic Repetition", 1); + + } } diff --git a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java index 8dc0b496655..51a6c378ef5 100644 --- a/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.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 mage.abilities.common; import mage.abilities.TriggeredAbilityImpl; @@ -41,7 +40,6 @@ import mage.target.targetpointer.FixedTarget; * * @author Loki */ - public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { private TargetController targetController; @@ -90,7 +88,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { case YOU: boolean yours = event.getPlayerId().equals(this.controllerId); if (yours && setTargetPointer) { - if (getTargets().size() == 0) { + if (getTargets().isEmpty()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } @@ -99,7 +97,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { return yours; case OPPONENT: if (game.getPlayer(this.controllerId).hasOpponent(event.getPlayerId(), game)) { - if (setTargetPointer && getTargets().size() == 0) { + if (setTargetPointer && getTargets().isEmpty()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } @@ -108,7 +106,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { } break; case ANY: - if (setTargetPointer && getTargets().size() == 0) { + if (setTargetPointer && getTargets().isEmpty()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } @@ -119,7 +117,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { if (attachment != null && attachment.getAttachedTo() != null) { Permanent attachedTo = game.getPermanent(attachment.getAttachedTo()); if (attachedTo != null && attachedTo.getControllerId().equals(event.getPlayerId())) { - if (setTargetPointer && getTargets().size() == 0) { + if (setTargetPointer && getTargets().isEmpty()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getPlayerId())); } @@ -143,9 +141,9 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { switch (targetController) { case YOU: if (this.optional) { - if (sb.substring(0, 6).toLowerCase().equals("target")){ + if (sb.substring(0, 6).toLowerCase().equals("target")) { sb.insert(0, "you may have "); - } else if (!sb.substring(0, 4).toLowerCase().equals("you ")){ + } else if (!sb.substring(0, 4).toLowerCase().equals("you ")) { sb.insert(0, "you may "); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java index 9c661c6ec1d..5e2f7095bdb 100644 --- a/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/FlashbackAbility.java @@ -32,6 +32,7 @@ import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.VariableCost; +import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; @@ -45,6 +46,7 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.players.Player; +import mage.target.targetpointer.FixedTarget; /** * 702.32. Flashback @@ -206,7 +208,9 @@ class FlashbackEffect extends OneShotEffect { } spellAbility.setCostModificationActive(false); // prevents to apply cost modification twice for flashbacked spells if (controller.cast(spellAbility, game, false)) { - game.addEffect(new FlashbackReplacementEffect(), source); + ContinuousEffect effect = new FlashbackReplacementEffect(); + effect.setTargetPointer(new FixedTarget(source.getSourceId(), game.getState().getZoneChangeCounter(source.getSourceId()))); + game.addEffect(effect, source); return true; } return false; @@ -256,8 +260,16 @@ class FlashbackReplacementEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - return event.getTargetId().equals(source.getSourceId()) + if (event.getTargetId().equals(source.getSourceId()) && ((ZoneChangeEvent) event).getFromZone() == Zone.STACK - && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED; + && ((ZoneChangeEvent) event).getToZone() != Zone.EXILED) { + discard(); + int zcc = game.getState().getZoneChangeCounter(source.getSourceId()); + if (((FixedTarget) getTargetPointer()).getZoneChangeCounter() == zcc) { + return true; + } + + } + return false; } } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java index 02e07555776..5d681562862 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentImpl.java @@ -625,7 +625,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { this.removeFromCombat(game); this.controlledFromStartOfControllerTurn = false; - this.abilities.setControllerId(controllerId); + this.getAbilities(game).setControllerId(controllerId); game.getContinuousEffects().setController(objectId, controllerId); // the controller of triggered abilites is always set/checked before the abilities triggers so not needed here game.fireEvent(new GameEvent(EventType.LOST_CONTROL, objectId, objectId, beforeResetControllerId));