From ecaa5a5b42335bc9a58ef1c09b44b0de76af4de8 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 7 Jul 2020 06:32:46 +0400 Subject: [PATCH] Eon Frolicker - fixed rollback error on play (#6780); --- .../src/mage/cards/c/CulturalExchange.java | 5 +- Mage.Sets/src/mage/cards/e/EonFrolicker.java | 8 ++-- .../test/cards/single/EonFrolickerTest.java | 46 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 24 ++++++---- .../abilities/keyword/ProtectionAbility.java | 28 +++++++---- .../main/java/mage/players/PlayerImpl.java | 9 ++-- 6 files changed, 91 insertions(+), 29 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/EonFrolickerTest.java diff --git a/Mage.Sets/src/mage/cards/c/CulturalExchange.java b/Mage.Sets/src/mage/cards/c/CulturalExchange.java index 892f003901..c7b9c98b8f 100644 --- a/Mage.Sets/src/mage/cards/c/CulturalExchange.java +++ b/Mage.Sets/src/mage/cards/c/CulturalExchange.java @@ -1,7 +1,5 @@ - package mage.cards.c; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.OneShotEffect; @@ -20,8 +18,9 @@ import mage.target.TargetPlayer; import mage.target.common.TargetCreaturePermanent; import mage.target.targetpointer.FixedTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class CulturalExchange extends CardImpl { diff --git a/Mage.Sets/src/mage/cards/e/EonFrolicker.java b/Mage.Sets/src/mage/cards/e/EonFrolicker.java index a46ca9f89b..09815576b6 100644 --- a/Mage.Sets/src/mage/cards/e/EonFrolicker.java +++ b/Mage.Sets/src/mage/cards/e/EonFrolicker.java @@ -16,9 +16,9 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.FilterObject; +import mage.filter.FilterPlayer; import mage.filter.StaticFilters; -import mage.filter.predicate.permanent.ControllerIdPredicate; +import mage.filter.predicate.other.PlayerIdPredicate; import mage.game.Game; import mage.game.turn.TurnMod; import mage.players.Player; @@ -85,8 +85,8 @@ class EonFrolickerEffect extends OneShotEffect { return false; } game.getState().getTurnMods().add(new TurnMod(player.getId(), false)); - FilterObject filter = new FilterObject(player.getName()); - filter.add(new ControllerIdPredicate(player.getId())); + FilterPlayer filter = new FilterPlayer(player.getName()); + filter.add(new PlayerIdPredicate(player.getId())); Ability ability = new ProtectionAbility(filter); game.addEffect(new GainAbilityControlledEffect( ability, Duration.UntilYourNextTurn, diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/EonFrolickerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/EonFrolickerTest.java new file mode 100644 index 0000000000..fab0fbfff1 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/EonFrolickerTest.java @@ -0,0 +1,46 @@ +package org.mage.test.cards.single; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ + +public class EonFrolickerTest extends CardTestPlayerBase { + + @Test + public void test_EonFrolicker_NormalPlay() { + // https://github.com/magefree/mage/issues/6780 + + // {2}{U}{U} + // When Eon Frolicker enters the battlefield, if you cast it, target opponent takes an extra turn after this one. + // Until your next turn, you and planeswalkers you control gain protection from that player. + // (You and planeswalkers you control can’t be targeted, dealt damage, or enchanted by anything controlled by that player.) + addCard(Zone.HAND, playerA, "Eon Frolicker", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // + // Chandra’s Fury deals 4 damage to target player or planeswalker and 1 damage to each creature that player or that planeswalker’s controller controls. + addCard(Zone.HAND, playerB, "Chandra's Fury", 1); // {4}{R} + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eon Frolicker"); + addTarget(playerA, playerB); + + // AI can targets only Eon Frolicker (cause A protected from B) + checkPlayableAbility("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cast Chandra's Fury", true); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Chandra's Fury"); + //addTarget(playerB, playerB); + + //setStrictChooseMode(true); // AI must choose target for fury (itself) + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Chandra's Fury", 1); + assertLife(playerA, 20); + assertLife(playerB, 20 - 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 090884d812..fc85af74c4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2124,16 +2124,17 @@ public class TestPlayer implements Player { || target.getOriginalTarget() instanceof TargetPermanentOrPlayer || target.getOriginalTarget() instanceof TargetDefender) { for (String targetDefinition : targets) { - if (targetDefinition.startsWith("targetPlayer=")) { - checkTargetDefinitionMarksSupport(target, targetDefinition, "="); - String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); - for (Player player : game.getPlayers().values()) { - if (player.getName().equals(playerName) - && target.canTarget(computerPlayer.getId(), player.getId(), source, game)) { - target.addTarget(player.getId(), source, game); - targets.remove(targetDefinition); - return true; - } + if (!targetDefinition.startsWith("targetPlayer=")) { + continue; + } + checkTargetDefinitionMarksSupport(target, targetDefinition, "="); + String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); + for (Player player : game.getPlayers().values()) { + if (player.getName().equals(playerName) + && target.canTarget(computerPlayer.getId(), player.getId(), source, game)) { + target.addTarget(player.getId(), source, game); + targets.remove(targetDefinition); + return true; } } } @@ -2146,6 +2147,9 @@ public class TestPlayer implements Player { || (target.getOriginalTarget() instanceof TargetCreatureOrPlayer) || (target.getOriginalTarget() instanceof TargetDefender)) { for (String targetDefinition : targets) { + if (targetDefinition.startsWith("targetPlayer=")) { + continue; + } checkTargetDefinitionMarksSupport(target, targetDefinition, "^[]"); String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index 35da5caded..b6af39c002 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -1,27 +1,24 @@ package mage.abilities.keyword; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; import mage.MageObject; import mage.ObjectColor; import mage.abilities.StaticAbility; import mage.cards.Card; import mage.constants.Zone; -import mage.filter.Filter; -import mage.filter.FilterCard; -import mage.filter.FilterObject; -import mage.filter.FilterPermanent; -import mage.filter.FilterSpell; +import mage.filter.*; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; import mage.game.stack.StackObject; +import mage.players.Player; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; /** - * * @author BetaSteward_at_googlemail.com */ public class ProtectionAbility extends StaticAbility { @@ -87,6 +84,7 @@ public class ProtectionAbility extends StaticAbility { } return true; } + if (filter instanceof FilterSpell) { if (source instanceof Spell) { return !filter.match(source, game); @@ -99,9 +97,21 @@ public class ProtectionAbility extends StaticAbility { return true; } } + if (filter instanceof FilterObject) { return !filter.match(source, game); } + + if (filter instanceof FilterPlayer) { + Player player = null; + if (source instanceof Permanent) { + player = game.getPlayer(((Permanent) source).getControllerId()); + } else if (source instanceof Card) { + player = game.getPlayer(((Card) source).getOwnerId()); + } + return !((FilterPlayer) filter).match(player, getSourceId(), this.getControllerId(), game); + } + return true; } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 2076e20c3a..078bcb4bb2 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -345,9 +345,12 @@ public abstract class PlayerImpl implements Player, Serializable { this.clearCastSourceIdManaCosts(); this.castSourceIdWithAlternateMana.addAll(player.getCastSourceIdWithAlternateMana()); - this.castSourceIdManaCosts.putAll(player.getCastSourceIdManaCosts()); - this.castSourceIdCosts.putAll(player.getCastSourceIdCosts()); - + for (Entry> entry : player.getCastSourceIdManaCosts().entrySet()) { + this.castSourceIdManaCosts.put(entry.getKey(), entry.getValue().copy()); + } + for (Entry> entry : player.getCastSourceIdCosts().entrySet()) { + this.castSourceIdCosts.put(entry.getKey(), entry.getValue().copy()); + } this.phyrexianColors = player.getPhyrexianColors().copy(); this.designations.clear();