diff --git a/Mage.Sets/src/mage/cards/g/GildedDrake.java b/Mage.Sets/src/mage/cards/g/GildedDrake.java index 10938bacf2..ba9ed15f10 100644 --- a/Mage.Sets/src/mage/cards/g/GildedDrake.java +++ b/Mage.Sets/src/mage/cards/g/GildedDrake.java @@ -72,24 +72,23 @@ class GildedDrakeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Permanent sourceObject = game.getPermanent(source.getSourceId()); - if (sourceObject != null) { - if (targetPointer.getFirst(game, source) != null) { - Permanent targetPermanent = game.getPermanent(targetPointer.getFirst(game, source)); - if (targetPermanent != null) { - ContinuousEffect effect = new ExchangeControlTargetEffect(Duration.EndOfGame, "", true); - effect.setTargetPointer(targetPointer); - game.addEffect(effect, source); - return true; - } - } - sourceObject.sacrifice(source, game); - } + if (controller == null) { + return false; + } + Permanent sourceObject = game.getPermanent(source.getSourceId()); + if (sourceObject == null) { + return false; + } + + if (targetPointer.getFirst(game, source) == null || game.getPermanent(targetPointer.getFirst(game, source)) == null) { + sourceObject.sacrifice(source, game); return true; } - return false; + + ContinuousEffect effect = new ExchangeControlTargetEffect(Duration.EndOfGame, "", true); + effect.setTargetPointer(targetPointer); + game.addEffect(effect, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java b/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java index ba97b5e26e..b83bddb849 100644 --- a/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java +++ b/Mage.Sets/src/mage/cards/k/KikiJikiMirrorBreaker.java @@ -84,18 +84,14 @@ class KikiJikiMirrorBreakerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); - if (permanent != null) { - CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); - effect.setTargetPointer(new FixedTarget(permanent, game)); - effect.apply(game, source); - for (Permanent addedToken : effect.getAddedPermanents()) { - SacrificeTargetEffect sacrificeEffect = new SacrificeTargetEffect("Sacrifice the token at the beginning of the next end step", source.getControllerId()); - sacrificeEffect.setTargetPointer(new FixedTarget(addedToken.getId())); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(sacrificeEffect), source); - } - return true; + if (permanent == null) { + return false; } - return false; + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true); + effect.setTargetPointer(new FixedTarget(permanent, game)); + effect.apply(game, source); + effect.sacrificeTokensCreatedAtNextEndStep(game, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java b/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java index 2a4dd54719..51abc490f5 100644 --- a/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java +++ b/Mage.Sets/src/mage/cards/r/ReflectionOfKikiJiki.java @@ -18,6 +18,7 @@ import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.AnotherPredicate; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.target.TargetPermanent; import mage.target.targetpointer.FixedTarget; @@ -82,6 +83,11 @@ class ReflectionOfKikiJikiEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + Permanent permanent = getTargetPointer().getFirstTargetPermanentOrLKI(game, source); + if (permanent == null) { + return false; + } + CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(null, null, true); effect.setTargetPointer(new FixedTarget(source.getFirstTarget(), game)); effect.apply(game, source); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/ExchangeControlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/ExchangeControlTest.java index f7c64be294..b95b18e43f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/ExchangeControlTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/ExchangeControlTest.java @@ -5,6 +5,7 @@ import mage.constants.PhaseStep; import mage.constants.Zone; import mage.game.permanent.Permanent; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -277,6 +278,56 @@ public class ExchangeControlTest extends CardTestPlayerBase { assertGraveyardCount(playerB, "Lightning Bolt", 1); assertGraveyardCount(playerB, "Silvercoat Lion", 1); assertGraveyardCount(playerA, "Gilded Drake", 1); + } + /** + * 1. Kiki-Jiki copied Gilded Drake. + * 2. Gilded Drake copy is exchanged for another creature. + * 3. After the exchange occurs, Gilded Drake is killed by any means. + * 4. Exchange creature is returned to previous controller (possible owner) during the next phase. + *

+ * See https://github.com/magefree/mage/issues/8742 + */ + @Ignore + @Test + public void testGildedDrakeCopyExchange() { + addCard(Zone.BATTLEFIELD, playerA, "Kiki-Jiki, Mirror Breaker"); + addCard(Zone.HAND, playerA, "Gilded Drake"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + + addCard(Zone.BATTLEFIELD, playerB, "Dwarven Trader"); // Exchange target + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // Exchange target + + setStrictChooseMode(true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Gilded Drake"); + addTarget(playerA, "Dwarven Trader"); // Target for Gilded Drake + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true); // Skip the cast and grab the ETB + + // Copy the drake + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}", "Gilded Drake", "When {this}"); + addTarget(playerA, "Silvercoat Lion"); // Target for Gilded Drake + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // Destroy both of the Drakes + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt"); + addTarget(playerA, "Gilded Drake"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt"); + addTarget(playerA, "Gilded Drake"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Kiki-Jiki, Mirror Breaker", 1); + assertPermanentCount(playerA, "Dwarven Trader", 1); // Original's exhange target + assertPermanentCount(playerA, "Silvercoat Lion", 1); // Copy's exchange target + + assertPermanentCount(playerA, "Gilded Drake", 0); + assertPermanentCount(playerB, "Gilded Drake", 0); } } diff --git a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java index 2394f9dab5..4abd6ee491 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/continuous/ExchangeControlTargetEffect.java @@ -28,7 +28,7 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl { private boolean withSecondTarget; private boolean destroyAttachedAuras; private Map zoneChangeCounter = new HashMap<>(); - private Map lockedControllers = new HashMap<>(); + private Map lockedControllers = new HashMap<>(); // Controllers for each permanent that is enforced by this effect public ExchangeControlTargetEffect(Duration duration, String rule) { this(duration, rule, false); @@ -102,9 +102,11 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl { discard(); return; } + // Meant to be swapped since this enforced the this.lockedControllers.put(permanent1.getId(), permanent2.getControllerId()); - this.zoneChangeCounter.put(permanent1.getId(), permanent1.getZoneChangeCounter(game)); this.lockedControllers.put(permanent2.getId(), permanent1.getControllerId()); + + this.zoneChangeCounter.put(permanent1.getId(), permanent1.getZoneChangeCounter(game)); this.zoneChangeCounter.put(permanent2.getId(), permanent2.getZoneChangeCounter(game)); } else { // discard if there are less than 2 permanents @@ -118,7 +120,7 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl { for (Map.Entry entry : zoneChangeCounter.entrySet()) { Permanent permanent = game.getPermanent(entry.getKey()); if (permanent == null || permanent.getZoneChangeCounter(game) != entry.getValue()) { - // controll effect cease if the same permanent is no longer on the battlefield + // Control effect cease if the same permanent is no longer on the battlefield toDelete.add(entry.getKey()); continue; } @@ -138,6 +140,7 @@ public class ExchangeControlTargetEffect extends ContinuousEffectImpl { if (!toDelete.isEmpty()) { for (UUID uuid : toDelete) { zoneChangeCounter.remove(uuid); + lockedControllers.remove(uuid); } if (zoneChangeCounter.isEmpty()) { discard();