From 2b78388eab885cec0b10a940e1a1f1846c8e9524 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 28 Nov 2020 19:49:16 +0400 Subject: [PATCH] [CMR] fixed Akroma's Will - missing copy of new condition in modes (#7210); Improved compatibility of new modes condition with choose dialogs and test framework; --- .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../src/mage/player/human/HumanPlayer.java | 2 +- .../cards/single/cmr/AkromasWillTest.java | 81 +++++++++++++++++++ .../java/org/mage/test/player/TestPlayer.java | 2 +- Mage/src/main/java/mage/abilities/Modes.java | 76 ++++++++++------- 5 files changed, 131 insertions(+), 32 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AkromasWillTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 06ca962697..48376cafda 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1924,7 +1924,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { @Override public Mode chooseMode(Modes modes, Ability source, Game game) { log.debug("chooseMode"); - if (modes.getMode() != null && modes.getMaxModes() == modes.getSelectedModes().size()) { + if (modes.getMode() != null && modes.getMaxModes(game, source) == modes.getSelectedModes().size()) { // mode was already set by the AI return modes.getMode(); } 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 11d8a59507..a0ca6be9b5 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 @@ -2085,7 +2085,7 @@ public class HumanPlayer extends PlayerImpl { boolean done = false; while (!done && canRespond()) { - String message = "Choose mode (selected " + modes.getSelectedModes().size() + " of " + modes.getMaxModes() + String message = "Choose mode (selected " + modes.getSelectedModes().size() + " of " + modes.getMaxModes(game, source) + ", min " + modes.getMinModes() + ")"; if (obj != null) { message = message + "
" + obj.getLogName(); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AkromasWillTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AkromasWillTest.java new file mode 100644 index 0000000000..82d5c18602 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/cmr/AkromasWillTest.java @@ -0,0 +1,81 @@ +package org.mage.test.cards.single.cmr; + +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * @author JayDi85 + */ + +public class AkromasWillTest extends CardTestCommanderDuelBase { + + @Test + public void test_OneMode() { + // https://github.com/magefree/mage/issues/7210 + + // Choose one. If you control a commander as you cast this spell, you may choose both. + // * Creatures you control gain flying, vigilance, and double strike until end of turn. + // * Creatures you control gain lifelink, indestructible, and protection from all colors until end of turn. + addCard(Zone.HAND, playerA, "Akroma's Will", 1); // {3}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + // + addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair", 1); + + checkAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", FlyingAbility.class, false); + checkAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", LifelinkAbility.class, false); + + // cast and use ONE mode only + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akroma's Will"); + setModeChoice(playerA, "1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", FlyingAbility.class, true); + checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", LifelinkAbility.class, false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultiModesOnCommander() { + // https://github.com/magefree/mage/issues/7210 + + // Choose one. If you control a commander as you cast this spell, you may choose both. + // * Creatures you control gain flying, vigilance, and double strike until end of turn. + // * Creatures you control gain lifelink, indestructible, and protection from all colors until end of turn. + addCard(Zone.HAND, playerA, "Akroma's Will", 1); // {3}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + // + addCard(Zone.COMMAND, playerA, "Balduvian Bears", 1); // {1}{G} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + // + addCard(Zone.BATTLEFIELD, playerA, "Kitesail Corsair", 1); + + checkAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", FlyingAbility.class, false); + checkAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", LifelinkAbility.class, false); + + // prepare commander + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}", 2); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("commander on battle", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 1); + + // cast and use two modes instead one + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akroma's Will"); + setModeChoice(playerA, "2"); + setModeChoice(playerA, "1"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", FlyingAbility.class, true); + checkAbility("after", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitesail Corsair", LifelinkAbility.class, true); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } +} 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 9ccf4d56ad..ffc0b2541b 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 @@ -1869,7 +1869,7 @@ public class TestPlayer implements Player { @Override public Mode chooseMode(Modes modes, Ability source, Game game) { - if (!modesSet.isEmpty() && modes.getMaxModes() > modes.getSelectedModes().size()) { + if (!modesSet.isEmpty() && modes.getMaxModes(game, source) > modes.getSelectedModes().size()) { // set mode to null to select less than maximum modes if multiple modes are allowed if (modesSet.get(0) == null) { modesSet.remove(0); diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index 10a7a87aa7..d64ca6c132 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -76,6 +76,7 @@ public class Modes extends LinkedHashMap { } else { this.currentMode = get(modes.getMode().getId()); // need fix? } + this.moreCondition = modes.moreCondition; } public Modes copy() { @@ -190,8 +191,41 @@ public class Modes extends LinkedHashMap { this.maxModesFilter = maxModesFilter; } - public int getMaxModes() { - return this.maxModes; + /** + * Return real affected max modes in current game. Use null params for default max modes value. + * + * @param game + * @param source + * @return + */ + public int getMaxModes(Game game, Ability source) { + int realMaxModes = this.maxModes; + if (game == null || source == null) { + return realMaxModes; + } + + // use case: make all modes chooseable + if (moreCondition != null && moreCondition.apply(game, source)) { + realMaxModes = Integer.MAX_VALUE; + } + + // use case: limit max modes by opponents (wtf?!) + if (getMaxModesFilter() != null) { + if (this.maxModesFilter instanceof FilterPlayer) { + realMaxModes = 0; + for (UUID targetPlayerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player targetPlayer = game.getPlayer(targetPlayerId); + if (((FilterPlayer) this.maxModesFilter).match(targetPlayer, source.getSourceId(), source.getControllerId(), game)) { + realMaxModes++; + } + } + if (realMaxModes > this.maxModes) { + realMaxModes = this.maxModes; + } + } + } + + return realMaxModes; } public void setModeChooser(TargetController modeChooser) { @@ -222,9 +256,9 @@ public class Modes extends LinkedHashMap { public boolean choose(Game game, Ability source) { if (this.isResetEachTurn()) { - if (this.getTurnNum(game, source) != game.getTurnNum()) { + if (getTurnNum(game, source) != game.getTurnNum()) { this.clearAlreadySelectedModes(source, game); - this.setTurnNum(game, source); + setTurnNum(game, source); } } if (this.size() > 1) { @@ -284,24 +318,8 @@ public class Modes extends LinkedHashMap { // player chooses modes manually this.currentMode = null; - int currentMaxModes = this.getMaxModes(); - if (moreCondition != null && moreCondition.apply(game, source)) { - currentMaxModes = Integer.MAX_VALUE; - } - if (getMaxModesFilter() != null) { - if (maxModesFilter instanceof FilterPlayer) { - currentMaxModes = 0; - for (UUID targetPlayerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { - Player targetPlayer = game.getPlayer(targetPlayerId); - if (((FilterPlayer) maxModesFilter).match(targetPlayer, source.getSourceId(), source.getControllerId(), game)) { - currentMaxModes++; - } - } - if (currentMaxModes > this.getMaxModes()) { - currentMaxModes = this.getMaxModes(); - } - } - } + int currentMaxModes = this.getMaxModes(game, source); + while (this.selectedModes.size() < currentMaxModes) { Mode choice = player.chooseMode(this, source, game); if (choice == null) { @@ -446,19 +464,19 @@ public class Modes extends LinkedHashMap { sb.append(chooseText); } else if (this.getMaxModesFilter() != null) { sb.append("choose one or more. Each mode must target ").append(getMaxModesFilter().getMessage()); - } else if (this.getMinModes() == 0 && this.getMaxModes() == 1) { + } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) == 1) { sb.append("choose up to one"); - } else if (this.getMinModes() == 0 && this.getMaxModes() == 3) { + } else if (this.getMinModes() == 0 && this.getMaxModes(null, null) == 3) { sb.append("choose any number"); - } else if (this.getMinModes() == 1 && this.getMaxModes() > 2) { + } else if (this.getMinModes() == 1 && this.getMaxModes(null, null) > 2) { sb.append("choose one or more"); - } else if (this.getMinModes() == 1 && this.getMaxModes() == 2) { + } else if (this.getMinModes() == 1 && this.getMaxModes(null, null) == 2) { sb.append("choose one or both"); - } else if (this.getMinModes() == 2 && this.getMaxModes() == 2) { + } else if (this.getMinModes() == 2 && this.getMaxModes(null, null) == 2) { sb.append("choose two"); - } else if (this.getMinModes() == 3 && this.getMaxModes() == 3) { + } else if (this.getMinModes() == 3 && this.getMaxModes(null, null) == 3) { sb.append("choose three"); - } else if (this.getMinModes() == 4 && this.getMaxModes() == 4) { + } else if (this.getMinModes() == 4 && this.getMaxModes(null, null) == 4) { sb.append("choose four"); } else { sb.append("choose one");