From 5626c5f932da749c8a7b2a290d929400c51e4d1f Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 7 Jan 2025 19:47:25 +0400 Subject: [PATCH] cheats: added default commands to take and remove control over another player (related to #12878) --- .../src/main/java/mage/utils/SystemUtil.java | 39 ++++++++++++++++++- Mage/src/main/java/mage/game/turn/Turn.java | 1 - .../main/java/mage/game/turn/TurnMods.java | 8 ++++ Mage/src/main/java/mage/util/CardUtil.java | 2 +- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Mage.Common/src/main/java/mage/utils/SystemUtil.java b/Mage.Common/src/main/java/mage/utils/SystemUtil.java index 9d5f3f3593b6..604ae7f361a6 100644 --- a/Mage.Common/src/main/java/mage/utils/SystemUtil.java +++ b/Mage.Common/src/main/java/mage/utils/SystemUtil.java @@ -24,6 +24,8 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.Token; import mage.players.Player; +import mage.target.Target; +import mage.target.TargetPlayer; import mage.util.CardUtil; import mage.util.MultiAmountMessage; import mage.util.RandomUtil; @@ -66,6 +68,8 @@ private SystemUtil() { // [@mana add] -> MANA ADD private static final String COMMAND_CARDS_ADD_TO_HAND = "@card add to hand"; private static final String COMMAND_LANDS_ADD_TO_BATTLEFIELD = "@lands add"; + private static final String COMMAND_OPPONENT_UNDER_CONTROL_START = "@opponent under control start"; + private static final String COMMAND_OPPONENT_UNDER_CONTROL_END = "@opponent under control end"; private static final String COMMAND_MANA_ADD = "@mana add"; // TODO: not implemented private static final String COMMAND_RUN_CUSTOM_CODE = "@run custom code"; // TODO: not implemented private static final String COMMAND_SHOW_OPPONENT_HAND = "@show opponent hand"; @@ -80,6 +84,8 @@ private SystemUtil() { supportedCommands.put(COMMAND_CARDS_ADD_TO_HAND, "CARDS: ADD TO HAND"); supportedCommands.put(COMMAND_MANA_ADD, "MANA ADD"); supportedCommands.put(COMMAND_LANDS_ADD_TO_BATTLEFIELD, "LANDS: ADD TO BATTLEFIELD"); + supportedCommands.put(COMMAND_OPPONENT_UNDER_CONTROL_START, "OPPONENT CONTROL: ENABLE"); + supportedCommands.put(COMMAND_OPPONENT_UNDER_CONTROL_END, "OPPONENT CONTROL: DISABLE"); supportedCommands.put(COMMAND_RUN_CUSTOM_CODE, "RUN CUSTOM CODE"); supportedCommands.put(COMMAND_SHOW_OPPONENT_HAND, "SHOW OPPONENT HAND"); supportedCommands.put(COMMAND_SHOW_OPPONENT_LIBRARY, "SHOW OPPONENT LIBRARY"); @@ -255,7 +261,7 @@ public static CardCommandData parseCardCommand(String commandLine) { * * @param game * @param commandsFilePath file path with commands in init.txt format - * @param feedbackPlayer player to execute that cheats (will see choose dialogs) + * @param feedbackPlayer player to execute that cheats (will see choose dialogs) */ public static void executeCheatCommands(Game game, String commandsFilePath, Player feedbackPlayer) { @@ -301,6 +307,8 @@ public static void executeCheatCommands(Game game, String commandsFilePath, Play // add default commands initLines.add(0, String.format("[%s]", COMMAND_LANDS_ADD_TO_BATTLEFIELD)); initLines.add(1, String.format("[%s]", COMMAND_CARDS_ADD_TO_HAND)); + initLines.add(2, String.format("[%s]", COMMAND_OPPONENT_UNDER_CONTROL_START)); + initLines.add(3, String.format("[%s]", COMMAND_OPPONENT_UNDER_CONTROL_END)); // collect all commands CommandGroup currentGroup = null; @@ -544,6 +552,34 @@ public static void executeCheatCommands(Game game, String commandsFilePath, Play break; } + case COMMAND_OPPONENT_UNDER_CONTROL_START: { + Target target = new TargetPlayer().withNotTarget(true).withChooseHint("to take under your control"); + if (feedbackPlayer.chooseTarget(Outcome.GainControl, target, fakeSourceAbilityTemplate, game)) { + Player targetPlayer = game.getPlayer(target.getFirstTarget()); + if (targetPlayer != null && targetPlayer != feedbackPlayer) { + CardUtil.takeControlUnderPlayerStart(game, fakeSourceAbilityTemplate, feedbackPlayer, targetPlayer, false); + // allow priority play again in same step (for better cheat UX) + targetPlayer.resetPassed(); + } + // workaround for refresh priority dialog like avatar click (cheats called from priority in 99%) + game.firePriorityEvent(feedbackPlayer.getId()); + } + break; + } + + case COMMAND_OPPONENT_UNDER_CONTROL_END: { + Target target = new TargetPlayer().withNotTarget(true).withChooseHint("to free from your control"); + if (feedbackPlayer.chooseTarget(Outcome.GainControl, target, fakeSourceAbilityTemplate, game)) { + Player targetPlayer = game.getPlayer(target.getFirstTarget()); + if (targetPlayer != null && targetPlayer != feedbackPlayer && !targetPlayer.isGameUnderControl()) { + CardUtil.takeControlUnderPlayerEnd(game, fakeSourceAbilityTemplate, feedbackPlayer, targetPlayer); + } + // workaround for refresh priority dialog like avatar click (cheats called from priority in 99%) + game.firePriorityEvent(feedbackPlayer.getId()); + } + break; + } + default: { String mes = String.format("Unknown system command: %s", runGroup.name); errorsList.add(mes); @@ -551,7 +587,6 @@ public static void executeCheatCommands(Game game, String commandsFilePath, Play break; } } - sendCheatCommandsFeedback(game, feedbackPlayer, errorsList); return; } diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java index 1ada5011092f..f163c2e7e9cd 100644 --- a/Mage/src/main/java/mage/game/turn/Turn.java +++ b/Mage/src/main/java/mage/game/turn/Turn.java @@ -248,7 +248,6 @@ private void checkTurnIsControlledByOtherPlayer(Game game, UUID activePlayerId) // add new under control TurnMod newControllerMod = game.getState().getTurnMods().useNextNewController(activePlayerId); if (newControllerMod != null && !newControllerMod.getNewControllerId().equals(activePlayerId)) { - // set player under new control // game logs added in child's call (controlPlayersTurn) game.getPlayer(newControllerMod.getNewControllerId()).controlPlayersTurn(game, activePlayerId, newControllerMod.getInfo()); } diff --git a/Mage/src/main/java/mage/game/turn/TurnMods.java b/Mage/src/main/java/mage/game/turn/TurnMods.java index c127110ced23..6019143fe814 100644 --- a/Mage/src/main/java/mage/game/turn/TurnMods.java +++ b/Mage/src/main/java/mage/game/turn/TurnMods.java @@ -62,6 +62,14 @@ public TurnMod useNextSkipTurn(UUID playerId) { } public TurnMod useNextNewController(UUID playerId) { + // 720.1a + // Multiple player-controlling effects that affect the same player overwrite each other. + // The last one to be created is the one that works. + // + // 720.1b + // If a turn is skipped, any pending player-controlling effects wait until the player + // who would be affected actually takes a turn. + TurnMod lastNewControllerMod = null; // find last/actual mod diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index d77bdca4a6ad..3f85a6fa5919 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1380,7 +1380,7 @@ public static void takeControlUnderPlayerStart(Game game, Ability source, Player public static void takeControlUnderPlayerEnd(Game game, Ability source, Player controller, Player playerUnderControl) { playerUnderControl.setGameUnderYourControl(true, false); if (!playerUnderControl.getTurnControlledBy().equals(controller.getId())) { - game.informPlayers(controller + " return control of the turn to " + playerUnderControl.getLogName() + CardUtil.getSourceLogName(game, source)); + game.informPlayers(controller.getLogName() + " return control of the turn to " + playerUnderControl.getLogName() + CardUtil.getSourceLogName(game, source)); controller.getPlayersUnderYourControl().remove(playerUnderControl.getId()); } }