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 ace8620e6b2..561326d5c22 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 @@ -692,7 +692,7 @@ public class ComputerPlayer extends PlayerImpl { || target.getOriginalTarget() instanceof TargetCardInHand) { isAddedSomething = false; if (outcome.isGood()) { - // good + // good - choose max possible Cards cards = new CardsImpl(possibleTargets); List cardsInHand = new ArrayList<>(cards.getCards(game)); while (!target.isChosen(game) @@ -703,36 +703,42 @@ public class ComputerPlayer extends PlayerImpl { if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) { target.addTarget(card.getId(), source, game); isAddedSomething = true; - cardsInHand.remove(card); if (target.isChoiceCompleted(game)) { return true; } } + cardsInHand.remove(card); } } } else { - // bad + // bad - choose the lowest possible findPlayables(game); for (Card card : unplayable.values()) { + if (target.isChosen(game)) { + return isAddedSomething; + } if (possibleTargets.contains(card.getId()) && target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) { target.addTarget(card.getId(), source, game); isAddedSomething = true; - if (target.isChoiceCompleted(game)) { - return true; + if (target.isChosen(game)) { + return isAddedSomething; } } } if (!hand.isEmpty()) { for (Card card : hand.getCards(game)) { + if (target.isChosen(game)) { + return isAddedSomething; + } if (possibleTargets.contains(card.getId()) && target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) { target.addTarget(card.getId(), source, game); isAddedSomething = true; - if (target.isChoiceCompleted(game)) { - return true; + if (target.isChosen(game)) { + return isAddedSomething; } } } @@ -854,39 +860,6 @@ public class ComputerPlayer extends PlayerImpl { if (target.getOriginalTarget() instanceof TargetPermanentOrPlayer) { List targets; TargetPermanentOrPlayer origTarget = ((TargetPermanentOrPlayer) target.getOriginalTarget()); - if (outcome.isGood()) { - targets = threats(abilityControllerId, source, ((FilterPermanentOrPlayer) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets()); - } else { - targets = threats(randomOpponentId, source, ((FilterPermanentOrPlayer) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets()); - } - - if (targets.isEmpty()) { - if (outcome.isGood()) { - if (target.canTarget(abilityControllerId, getId(), source, game) && !target.contains(getId())) { - return tryAddTarget(target, getId(), source, game); - } - } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game) && !target.contains(randomOpponentId)) { - return tryAddTarget(target, randomOpponentId, source, game); - } - } - - if (targets.isEmpty() && target.isRequired(source)) { - targets = game.getBattlefield().getActivePermanents(((FilterPermanentOrPlayer) origTarget.getFilter()).getPermanentFilter(), playerId, game); - } - for (Permanent permanent : targets) { - List alreadyTargeted = target.getTargets(); - if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) { - if (alreadyTargeted != null && !alreadyTargeted.contains(permanent.getId())) { - return tryAddTarget(target, permanent.getId(), source, game); - } - } - } - } - - if (target.getOriginalTarget() instanceof TargetPlayerOrPlaneswalker - || target.getOriginalTarget() instanceof TargetOpponentOrPlaneswalker) { - List targets; - TargetPermanentOrPlayer origTarget = ((TargetPermanentOrPlayer) target.getOriginalTarget()); // TODO: in multiplayer game there many opponents - if random opponents don't have targets then AI must use next opponent, but it skips // (e.g. you randomOpponentId must be replaced by List randomOpponents) @@ -1239,6 +1212,7 @@ public class ComputerPlayer extends PlayerImpl { throw new IllegalStateException("Target wasn't handled in computer's chooseTarget method: " + target.getClass().getCanonicalName()); } //end of chooseTarget method + @Deprecated // TODO: replace by source only version protected Card selectCard(UUID abilityControllerId, List cards, Outcome outcome, Target target, Game game) { return selectCardInner(abilityControllerId, cards, outcome, target, null, game); } @@ -1279,6 +1253,10 @@ public class ComputerPlayer extends PlayerImpl { log.debug("chooseTarget: " + outcome.toString() + ':' + target.toString()); } + if (target.getAmountRemaining() <= 0) { + return false; + } + UUID sourceId = source != null ? source.getSourceId() : null; // sometimes a target selection can be made from a player that does not control the ability @@ -2576,6 +2554,7 @@ public class ComputerPlayer extends PlayerImpl { tournament.submitDeck(playerId, deck); } + @Deprecated // TODO: replace by source only version public Card selectBestCard(List cards, List chosenColors) { return selectBestCardInner(cards, chosenColors, null, null, null); } @@ -2623,14 +2602,6 @@ public class ComputerPlayer extends PlayerImpl { return bestCard; } - public Card selectWorstCard(List cards, List chosenColors) { - return selectWorstCardInner(cards, chosenColors, null, null, null); - } - - public Card selectWorstCardTarget(List cards, List chosenColors, Target target, Ability targetingSource, Game game) { - return selectWorstCardInner(cards, chosenColors, target, targetingSource, game); - } - /** * @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget */ @@ -3097,6 +3068,7 @@ public class ComputerPlayer extends PlayerImpl { return before != after; } + @Deprecated // TODO: replace by source only version private boolean selectPlayer(Outcome outcome, Target target, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) { return selectPlayerInner(outcome, target, null, abilityControllerId, randomOpponentId, game, required); } diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java index 944d9aa5135..9e2b68cc055 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/src/mage/player/ai/SimulatedPlayerMCTS.java @@ -288,6 +288,10 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer { @Override public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + if (target.getAmountRemaining() <= 0) { + return false; + } + Set possibleTargets = target.possibleTargets(playerId, source, game); if (possibleTargets.isEmpty()) { return !target.isRequired(source); 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 c0ab0e728db..cd07bd48889 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 @@ -1042,6 +1042,10 @@ public class HumanPlayer extends PlayerImpl { return false; } + if (target.getAmountRemaining() <= 0) { + return false; + } + if (source == null) { return false; } 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 80ce3115065..b172cabb621 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 @@ -2328,7 +2328,7 @@ public class TestPlayer implements Player { continue; } if (hasObjectTargetNameOrAlias(permanent, targetName)) { - if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { + if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); isAddedSomething = true; @@ -2336,7 +2336,7 @@ public class TestPlayer implements Player { } } } else if ((permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove search by exp code? - if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) { + if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); isAddedSomething = true; @@ -2371,7 +2371,7 @@ public class TestPlayer implements Player { isAddedSomething = false; for (Player player : game.getPlayers().values()) { if (player.getName().equals(choiceRecord)) { - if (target.canTarget(abilityControllerId, player.getId(), null, game) && !target.contains(player.getId())) { + if (target.canTarget(abilityControllerId, player.getId(), source, game) && !target.contains(player.getId())) { target.add(player.getId(), game); isAddedSomething = true; } @@ -2538,7 +2538,8 @@ public class TestPlayer implements Player { String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); for (Player player : game.getPlayers().values()) { if (player.getName().equals(playerName) - && target.canTarget(abilityControllerId, player.getId(), source, game)) { + && target.canTarget(abilityControllerId, player.getId(), source, game) + && !target.contains(player.getId())) { target.addTarget(player.getId(), source, game); targets.remove(targetDefinition); return true; @@ -2887,7 +2888,7 @@ public class TestPlayer implements Player { @Override public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) { - if (message.equals("Scry 1?")) { + if (message != null && message.equals("Scry 1?")) { return false; } assertAliasSupportInChoices(false); @@ -4326,7 +4327,9 @@ public class TestPlayer implements Player { // chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount) // if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx) - Assert.assertNotEquals("chooseTargetAmount needs non zero amount remaining", 0, target.getAmountRemaining()); + if (target.getAmountRemaining() <= 0) { + return false; + } assertAliasSupportInTargets(true); if (!targets.isEmpty()) { diff --git a/Mage/src/main/java/mage/game/command/dungeons/TombOfAnnihilationDungeon.java b/Mage/src/main/java/mage/game/command/dungeons/TombOfAnnihilationDungeon.java index be2c988301b..af928e4acfb 100644 --- a/Mage/src/main/java/mage/game/command/dungeons/TombOfAnnihilationDungeon.java +++ b/Mage/src/main/java/mage/game/command/dungeons/TombOfAnnihilationDungeon.java @@ -209,7 +209,7 @@ class OublietteTarget extends TargetSacrifice { @Override public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { Set possibleTargets = super.possibleTargets(sourceControllerId, source, game); - possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, null, game)); + possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, source, game)); return possibleTargets; }