diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java index a49c7ca7a0..c1ff29cb74 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,12 +20,11 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.player.ai; import java.util.LinkedList; @@ -53,9 +52,6 @@ import mage.game.turn.PostCombatMainStep; import mage.game.turn.Step; import org.apache.log4j.Logger; - - - /** * * @author ayratn @@ -91,10 +87,10 @@ public class ComputerPlayer7 extends ComputerPlayer6 { private boolean priorityPlay(Game game) { if (lastLoggedTurn != game.getTurnNum()) { lastLoggedTurn = game.getTurnNum(); - logger.info("======================= Turn: "+ game.getTurnNum() + " ["+ game.getPlayer(game.getActivePlayerId()).getName() +"] ========================================="); + logger.info("======================= Turn: " + game.getTurnNum() + " [" + game.getPlayer(game.getActivePlayerId()).getName() + "] ========================================="); } logState(game); - logger.debug("Priority -- Step: " + (game.getTurn().getStepType() + " ").substring(0,25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name); + logger.debug("Priority -- Step: " + (game.getTurn().getStepType() + " ").substring(0, 25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name); game.getState().setPriorityPlayerId(playerId); game.firePriorityEvent(playerId); switch (game.getTurn().getStepType()) { @@ -111,8 +107,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } act(game); return true; - } - else { + } else { pass(game); } return false; @@ -128,8 +123,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } act(game); return true; - } - else { + } else { pass(game); } return false; @@ -141,12 +135,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 { return false; case POSTCOMBAT_MAIN: // if (game.getActivePlayerId().equals(playerId)) { - printOutState(game); - if (actions.isEmpty()) { - calculatePostCombatActions(game); - } - act(game); - return true; + printOutState(game); + if (actions.isEmpty()) { + calculatePostCombatActions(game); + } + act(game); + return true; // } // else { // pass(game); @@ -161,24 +155,24 @@ public class ComputerPlayer7 extends ComputerPlayer6 { return false; } - protected void calculatePreCombatActions(Game game) { + protected void calculatePreCombatActions(Game game) { if (!getNextAction(game)) { currentScore = GameStateEvaluator2.evaluate(playerId, game); Game sim = createSimulation(game); SimulationNode2.resetCount(); root = new SimulationNode2(null, sim, maxDepth, playerId); - + addActionsTimed(); logger.trace("After add actions timed: root.children.size = " + root.children.size()); if (root.children.size() > 0) { root = root.children.get(0); // int bestScore = root.getScore(); // if (bestScore > currentScore || allowBadMoves) { - + // prevent repeating always the same action with no cost boolean doThis = true; if (root.abilities.size() == 1) { - for (Ability ability:root.abilities) { + for (Ability ability : root.abilities) { if (ability.getManaCosts().convertedManaCost() == 0 && ability.getCosts().isEmpty()) { if (actionCache.contains(ability.getRule() + "_" + ability.getSourceId())) { doThis = false; // don't do it again @@ -217,7 +211,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { actions = new LinkedList<>(root.abilities); combat = root.combat; } else { - logger.debug("[" + game.getPlayer(playerId).getName() + "] no better score current: " + currentScore + " bestScore: " + bestScore ); + logger.debug("[" + game.getPlayer(playerId).getName() + "] no better score current: " + currentScore + " bestScore: " + bestScore); } } else { logger.debug("[" + game.getPlayer(playerId).getName() + "][post] Action: skip"); @@ -229,7 +223,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { protected int addActions(SimulationNode2 node, int depth, int alpha, int beta) { boolean stepFinished = false; int val; - if (logger.isTraceEnabled() && node !=null && node.getAbilities() != null && !node.getAbilities().toString().equals("[Pass]")){ + if (logger.isTraceEnabled() && node != null && node.getAbilities() != null && !node.getAbilities().toString().equals("[Pass]")) { logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta); } Game game = node.getGame(); @@ -243,42 +237,39 @@ public class ComputerPlayer7 extends ComputerPlayer6 { val = GameStateEvaluator2.evaluate(playerId, game); if (logger.isTraceEnabled()) { StringBuilder sb = new StringBuilder("Add Actions -- reached end state <").append(val).append(">"); - SimulationNode2 logNode = node; + SimulationNode2 logNode = node; do { - sb.append(new StringBuilder(" <- ["+logNode.getDepth()+"]" + (logNode.getAbilities() != null ? logNode.getAbilities().toString():"[empty]"))); + sb.append(new StringBuilder(" <- [" + logNode.getDepth() + "]" + (logNode.getAbilities() != null ? logNode.getAbilities().toString() : "[empty]"))); logNode = logNode.getParent(); - } while((logNode.getParent() != null)); + } while ((logNode.getParent() != null)); logger.trace(sb); } } else if (node.getChildren().size() > 0) { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder("Add Action [").append(depth) .append("] -- something added children ") - .append(node.getAbilities() != null ? node.getAbilities().toString():"null") + .append(node.getAbilities() != null ? node.getAbilities().toString() : "null") .append(" added children: ").append(node.getChildren().size()).append(" ("); - for (SimulationNode2 logNode: node.getChildren()) { - sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString():"null").append(", "); + for (SimulationNode2 logNode : node.getChildren()) { + sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString() : "null").append(", "); } sb.append(")"); logger.debug(sb); } - val = minimaxAB(node, depth-1, alpha, beta); - } - else { + val = minimaxAB(node, depth - 1, alpha, beta); + } else { logger.trace("Add Action -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName()); if (allPassed(game)) { if (!game.getStack().isEmpty()) { resolve(node, depth, game); - } - else { + } else { stepFinished = true; } } if (game.gameOver(null)) { val = GameStateEvaluator2.evaluate(playerId, game); - } - else if (stepFinished) { + } else if (stepFinished) { logger.debug("Step finished"); int testScore = GameStateEvaluator2.evaluate(playerId, game); if (game.getActivePlayerId().equals(playerId)) { @@ -286,8 +277,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { // if score at end of step is worse than original score don't check further //logger.debug("Add Action -- abandoning check, no immediate benefit"); val = testScore; - } - else { + } else { /*switch (game.getTurn().getStepType()) { case PRECOMBAT_MAIN: val = simulateCombat(game, node, depth-1, alpha, beta, false); @@ -301,32 +291,29 @@ public class ComputerPlayer7 extends ComputerPlayer6 { }*/ val = GameStateEvaluator2.evaluate(playerId, game); } - } - else { + } else { val = GameStateEvaluator2.evaluate(playerId, game); /*if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS) val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true); else val = GameStateEvaluator2.evaluate(playerId, game); - */ + */ } - } - else if (node.getChildren().size() > 0) { + } else if (node.getChildren().size() > 0) { if (logger.isDebugEnabled()) { StringBuilder sb = new StringBuilder("Add Action [").append(depth) .append("] -- trigger ") - .append(node.getAbilities() != null ? node.getAbilities().toString():"null") + .append(node.getAbilities() != null ? node.getAbilities().toString() : "null") .append(" added children: ").append(node.getChildren().size()).append(" ("); - for (SimulationNode2 logNode: node.getChildren()) { - sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString():"null").append(", "); + for (SimulationNode2 logNode : node.getChildren()) { + sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString() : "null").append(", "); } sb.append(")"); logger.debug(sb); } val = minimaxAB(node, depth, alpha, beta); - } - else { + } else { val = simulatePriority(node, game, depth, alpha, beta); } } @@ -353,25 +340,20 @@ public class ComputerPlayer7 extends ComputerPlayer6 { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { val = simulateAttackers(game, node, game.getActivePlayerId(), depth, alpha, beta, counter); } - } - else if (!counter) { + } else if (!counter) { simulateToEnd(game); val = simulatePostCombatMain(game, node, depth, alpha, beta); } } - } - else { - if (!game.getStep().skipStep(game, game.getActivePlayerId())) { - game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId())); - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { - //only suitable for two player games - only simulates blocks for 1st defender - val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter); - } - } - else if (!counter) { - finishCombat(game); - ///val = simulateCounterAttack(game, node, depth, alpha, beta); + } else if (!game.getStep().skipStep(game, game.getActivePlayerId())) { + game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId())); + if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) { + //only suitable for two player games - only simulates blocks for 1st defender + val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter); } + } else if (!counter) { + finishCombat(game); + ///val = simulateCounterAttack(game, node, depth, alpha, beta); } if (val == null) { val = GameStateEvaluator2.evaluate(playerId, game); @@ -382,7 +364,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 { return val; } - protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) { if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); @@ -396,7 +377,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { if (logger.isDebugEnabled()) { logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(defenderId, game)); } - for (Combat engagement: attacker.addAttackers(game)) { + for (Combat engagement : attacker.addAttackers(game)) { if (logger.isDebugEnabled()) { logger.debug("Sim Attackers: " + engagement.getAttackers() + ", blockers: " + engagement.getBlockers()); } @@ -404,9 +385,9 @@ public class ComputerPlayer7 extends ComputerPlayer6 { logger.debug("Sim Attackers -- pruning attackers"); break; } - Game sim = game.copy(); - for (CombatGroup group: engagement.getGroups()) { - for (UUID attackId: group.getAttackers()) { + Game sim = game.copy(); + for (CombatGroup group : engagement.getGroups()) { + for (UUID attackId : group.getAttackers()) { sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false); } } @@ -424,7 +405,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId())); Combat simCombat = sim.getCombat().copy(); sim.getPhase().setStep(new DeclareBlockersStep()); - val = simulateCombat(sim, newNode, depth-1, alpha, beta, counter); + val = simulateCombat(sim, newNode, depth - 1, alpha, beta, counter); if (!attackerId.equals(playerId)) { if (val < beta) { beta = val; @@ -434,15 +415,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 { bestNode.setCombat(newNode.getChildren().get(0).getCombat()); } } - } - else { - if (val > alpha) { - alpha = val; - bestNode = newNode; - bestNode.setScore(val); - if (newNode.getChildren().size() > 0) { - bestNode.setCombat(newNode.getChildren().get(0).getCombat()); - } + } else if (val > alpha) { + alpha = val; + bestNode = newNode; + bestNode.setScore(val); + if (newNode.getChildren().size() > 0) { + bestNode.setCombat(newNode.getChildren().get(0).getCombat()); } } } @@ -475,16 +453,16 @@ public class ComputerPlayer7 extends ComputerPlayer6 { logger.debug(defender.getName() + "'s possible blockers: " + defender.getAvailableBlockers(game)); } List<Combat> combats = defender.addBlockers(game); - for (Combat engagement: combats) { + for (Combat engagement : combats) { if (alpha >= beta) { logger.debug("Sim blockers -- pruning blockers"); break; } Game sim = game.copy(); - for (CombatGroup group: engagement.getGroups()) { + for (CombatGroup group : engagement.getGroups()) { if (group.getAttackers().size() > 0) { UUID attackerId = group.getAttackers().get(0); - for (UUID blockerId: group.getBlockers()) { + for (UUID blockerId : group.getBlockers()) { sim.getPlayer(defenderId).declareBlocker(defenderId, blockerId, attackerId, sim); } } @@ -505,11 +483,9 @@ public class ComputerPlayer7 extends ComputerPlayer6 { finishCombat(sim); if (sim.gameOver(null)) { val = GameStateEvaluator2.evaluate(playerId, sim); - } - else if (!counter) { - val = simulatePostCombatMain(sim, newNode, depth-1, alpha, beta); - } - else { + } else if (!counter) { + val = simulatePostCombatMain(sim, newNode, depth - 1, alpha, beta); + } else { val = GameStateEvaluator2.evaluate(playerId, sim); } if (!defenderId.equals(playerId)) { @@ -519,14 +495,11 @@ public class ComputerPlayer7 extends ComputerPlayer6 { bestNode.setScore(val); bestNode.setCombat(simCombat); } - } - else { - if (val > alpha) { - alpha = val; - bestNode = newNode; - bestNode.setScore(val); - bestNode.setCombat(simCombat); - } + } else if (val > alpha) { + alpha = val; + bestNode = newNode; + bestNode.setScore(val); + bestNode.setCombat(simCombat); } } } @@ -570,7 +543,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 { val = GameStateEvaluator2.evaluate(playerId, game); return val; }*/ - protected void simulateStep(Game game, Step step) { if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 03ca681e47..c25a5c3845 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -65,9 +65,14 @@ import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.PlayerAction; +import static mage.constants.PlayerAction.HOLD_PRIORITY; +import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_ID_NO; import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL; +import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_LAST; import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL; import mage.constants.RangeOfInfluence; +import static mage.constants.SpellAbilityType.SPLIT; +import static mage.constants.SpellAbilityType.SPLIT_FUSED; import mage.constants.Zone; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterBlockingCreature; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BloodthirstTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BloodthirstTest.java index dcafcdaf8b..7b64a63894 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BloodthirstTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BloodthirstTest.java @@ -67,7 +67,7 @@ public class BloodthirstTest extends CardTestPlayerBase { public void testBloodlord() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); - // Bloodthirst 3 + // Bloodthirst 3 (If an opponent was dealt damage this turn, this creature enters the battlefield with 3 +1/+1 counters) // Whenever you cast a Vampire creature spell, it gains bloodthirst 3 addCard(Zone.HAND, playerA, "Bloodlord of Vaasgoth"); // {3}{B}{B} addCard(Zone.HAND, playerA, "Barony Vampire"); // 3/2 {2}{B} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java index 5b1f330acc..3f0982b20c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/ErsatzGnomesTest.java @@ -90,10 +90,73 @@ public class ErsatzGnomesTest extends CardTestPlayerBase { execute(); assertGraveyardCount(playerB, "Unsummon", 1); + assertHandCount(playerA, "Silvercoat Lion", 0); assertPermanentCount(playerA, "Silvercoat Lion", 1); assertTapped("Ersatz Gnomes", true); Permanent lion = getPermanent("Silvercoat Lion", playerA); Assert.assertTrue("Silvercoat lion has to be white", lion.getColor(currentGame).isWhite()); } + @Test + public void testChangeColorOfBestowSpell() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + // Bestow {3}{W} + // Lifelink + // Echanted creature gets +1/+1 and has lifelink. + addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W} + + // {T}: Target spell becomes colorless. + // {T}: Target permanent becomes colorless until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + addCard(Zone.HAND, playerB, "Unsummon"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon using bestow", "Silvercoat Lion"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertHandCount(playerA, "Hopeful Eidolon", 0); + assertPermanentCount(playerA, "Hopeful Eidolon", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 3, 3); + assertTapped("Ersatz Gnomes", true); + Permanent eidolon = getPermanent("Hopeful Eidolon", playerA); + Assert.assertTrue("Hopeful Eidolon Enchantment has to be colorless", eidolon.getColor(currentGame).isColorless()); + } + + @Test + public void testChangeColorOfBestowSpellUnsummon() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Bestow {3}{W} + // Lifelink + // Echanted creature gets +1/+1 and has lifelink. + addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W} + + // {T}: Target spell becomes colorless. + // {T}: Target permanent becomes colorless until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes"); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + addCard(Zone.HAND, playerB, "Unsummon"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon"); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon"); + + castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Hopeful Eidolon"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Hopeful Eidolon"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Unsummon", 1); + assertHandCount(playerA, "Hopeful Eidolon", 0); + assertPermanentCount(playerA, "Hopeful Eidolon", 1); + assertTapped("Ersatz Gnomes", true); + Permanent lion = getPermanent("Hopeful Eidolon", playerA); + Assert.assertTrue("Hopeful Eidolon has to be white", lion.getColor(currentGame).isWhite()); + } } diff --git a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java index 0fe8f7b741..d2fd8a0d57 100644 --- a/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java +++ b/Mage.Verify/src/test/java/mage/verify/VerifyCardDataTest.java @@ -1,5 +1,13 @@ package mage.verify; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import mage.ObjectColor; import mage.cards.Card; import mage.cards.CardImpl; @@ -12,15 +20,6 @@ import mage.constants.CardType; import org.junit.Assert; import org.junit.Test; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Paths; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public class VerifyCardDataTest { // right now this is very noisy, and not useful enough to make any assertions on @@ -105,8 +104,7 @@ public class VerifyCardDataTest { if (!(token.equals(card.getName()) || containsInTypesOrText(ref, token) || containsInTypesOrText(ref, token.toLowerCase()) - || (ref2 != null && (containsInTypesOrText(ref2, token) || containsInTypesOrText(ref2, token.toLowerCase()))) - )) { + || (ref2 != null && (containsInTypesOrText(ref2, token) || containsInTypesOrText(ref2, token.toLowerCase()))))) { System.out.println("unexpected token " + token + " in " + card); } } @@ -143,12 +141,12 @@ public class VerifyCardDataTest { if (expected == null) { expected = Collections.emptyList(); } - if (expected.size() != color.getColorCount() || - (color.isBlack() && !expected.contains("Black")) || - (color.isBlue() && !expected.contains("Blue")) || - (color.isGreen() && !expected.contains("Green")) || - (color.isRed() && !expected.contains("Red")) || - (color.isWhite() && !expected.contains("White"))) { + if (expected.size() != color.getColorCount() + || (color.isBlack() && !expected.contains("Black")) + || (color.isBlue() && !expected.contains("Blue")) + || (color.isGreen() && !expected.contains("Green")) + || (color.isRed() && !expected.contains("Red")) + || (color.isWhite() && !expected.contains("White"))) { fail(card, "colors", color + " != " + expected); } } diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index a57df07fac..f78a74e9ee 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -208,6 +208,8 @@ public class ZonesHandler { // If we can't find the card we can't remove it. return false; } + // If needed take attributes from the spell (e.g. color of spell was changed) + card = takeAttributesFromSpell(card, event, game); boolean success = false; if (info.faceDown) { card.setFaceDown(true, game); @@ -282,4 +284,23 @@ public class ZonesHandler { order.add(cards.getCards(game).iterator().next()); return order; } + + private static Card takeAttributesFromSpell(Card card, ZoneChangeEvent event, Game game) { + if (Zone.STACK.equals(event.getFromZone())) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell != null) { + boolean doCopy = false; + if (!card.getColor(game).equals(spell.getColor(game))) { + doCopy = true; + } + if (doCopy) { + // the card that is referenced to in the permanent is copied and the spell attributes are set to this copied card + card = card.copy(); + card.getColor(game).setColor(spell.getColor(game)); + } + } + } + return card; + } + } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentCard.java b/Mage/src/main/java/mage/game/permanent/PermanentCard.java index 017a509386..9ff7eb7578 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentCard.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentCard.java @@ -132,7 +132,7 @@ public class PermanentCard extends PermanentImpl { transformable = card.isTransformable(); if (transformable) { this.nightCard = card.isNightCard(); - if (! this.nightCard) { + if (!this.nightCard) { this.secondSideCard = card.getSecondCardFace(); this.secondSideCardClazz = this.secondSideCard.getClass(); } diff --git a/Mage/src/main/java/mage/game/permanent/PermanentMeld.java b/Mage/src/main/java/mage/game/permanent/PermanentMeld.java index dd28172322..6cba02c680 100644 --- a/Mage/src/main/java/mage/game/permanent/PermanentMeld.java +++ b/Mage/src/main/java/mage/game/permanent/PermanentMeld.java @@ -27,15 +27,9 @@ */ package mage.game.permanent; -import java.util.ArrayList; import java.util.UUID; import mage.cards.Card; -import mage.cards.CardsImpl; -import mage.cards.MeldCard; -import mage.constants.Zone; import mage.game.Game; -import mage.game.events.ZoneChangeEvent; -import mage.players.Player; /** * diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index ed562d6b6c..cc9b47a99e 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -277,9 +277,6 @@ public class Spell extends StackObjImpl implements Card { } } else { updateOptionalCosts(0); - if (!getColor(game).equals(card.getColor(game))) { // if spell color was changed, the created permanent needs to be of that color - game.getState().getCreateCardAttribute(card).getColor().setColor(getColor(game)); - } return controller.moveCards(card, Zone.BATTLEFIELD, ability, game, false, faceDown, false, null); } } @@ -902,7 +899,7 @@ public class Spell extends StackObjImpl implements Card { @Override public void checkForCountersToAdd(Permanent permanent, Game game) { - throw new UnsupportedOperationException("Not supported for Spell"); + card.checkForCountersToAdd(permanent, game); } @Override diff --git a/Mage/src/main/java/mage/game/turn/TurnMods.java b/Mage/src/main/java/mage/game/turn/TurnMods.java index 932b6f2c36..ee9baa5b38 100644 --- a/Mage/src/main/java/mage/game/turn/TurnMods.java +++ b/Mage/src/main/java/mage/game/turn/TurnMods.java @@ -1,16 +1,16 @@ /* * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,20 +20,18 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.game.turn; -import mage.constants.PhaseStep; -import mage.constants.TurnPhase; - import java.util.ArrayList; import java.util.ListIterator; import java.util.UUID; +import mage.constants.PhaseStep; +import mage.constants.TurnPhase; /** * @@ -41,10 +39,11 @@ import java.util.UUID; */ public class TurnMods extends ArrayList<TurnMod> { - public TurnMods() {} + public TurnMods() { + } public TurnMods(final TurnMods mods) { - for (TurnMod mod: mods) { + for (TurnMod mod : mods) { this.add(mod.copy()); } } @@ -95,7 +94,7 @@ public class TurnMods extends ArrayList<TurnMod> { it.remove(); } } - // now delete all other - control next turn effect is not cumulative + // now delete all other effects that control current active player - control next turn of player effects are not cumulative it = this.listIterator(this.size()); while (it.hasPrevious()) { TurnMod turnMod = it.previous();