Turn under control reworked:

- game: fixed game freezes when computer try to take control over another computer or human (added game logs, related to #12878);
 - cheats: improved take and give control commands, now you can give control under yourself to another player;
 - cheats: improved take and give control commands, now you can return control to computer in the same priority;
 - cheats: deleted useless and unused command to activate opponent's ability;
This commit is contained in:
Oleg Agafonov 2025-01-11 22:21:58 +04:00
parent a5c354f960
commit 1f1d1088a1
6 changed files with 50 additions and 73 deletions

View file

@ -1,8 +1,6 @@
package mage.utils;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
@ -26,6 +24,7 @@ import mage.game.permanent.token.Token;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.target.common.TargetOpponent;
import mage.util.CardUtil;
import mage.util.MultiAmountMessage;
import mage.util.RandomUtil;
@ -68,15 +67,14 @@ public final class 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_UNDER_CONTROL_TAKE = "@under control take";
private static final String COMMAND_UNDER_CONTROL_GIVE = "@under control give";
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";
private static final String COMMAND_SHOW_OPPONENT_LIBRARY = "@show opponent library";
private static final String COMMAND_SHOW_MY_HAND = "@show my hand";
private static final String COMMAND_SHOW_MY_LIBRARY = "@show my library";
private static final String COMMAND_ACTIVATE_OPPONENT_ABILITY = "@activate opponent ability";
private static final Map<String, String> supportedCommands = new HashMap<>();
static {
@ -84,14 +82,13 @@ public final class 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_UNDER_CONTROL_TAKE, "UNDER CONTROL: TAKE");
supportedCommands.put(COMMAND_UNDER_CONTROL_GIVE, "UNDER CONTROL: GIVE");
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");
supportedCommands.put(COMMAND_SHOW_MY_HAND, "SHOW MY HAND");
supportedCommands.put(COMMAND_SHOW_MY_LIBRARY, "SHOW MY LIBRARY");
supportedCommands.put(COMMAND_ACTIVATE_OPPONENT_ABILITY, "ACTIVATE OPPONENT ABILITY");
}
private static final Pattern patternGroup = Pattern.compile("\\[(.+)\\]"); // [test new card]
@ -307,8 +304,8 @@ public final class SystemUtil {
// 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));
initLines.add(2, String.format("[%s]", COMMAND_UNDER_CONTROL_TAKE));
initLines.add(3, String.format("[%s]", COMMAND_UNDER_CONTROL_GIVE));
// collect all commands
CommandGroup currentGroup = null;
@ -427,53 +424,6 @@ public final class SystemUtil {
break;
}
case COMMAND_ACTIVATE_OPPONENT_ABILITY: {
// WARNING, maybe very bugged if called in wrong priority
// uses choose triggered ability dialog to select it
UUID savedPriorityPlayer = null;
if (game.getActivePlayerId() != opponent.getId()) {
savedPriorityPlayer = game.getActivePlayerId();
}
// change active player to find and play selected abilities (it's danger and buggy code)
if (savedPriorityPlayer != null) {
game.getState().setPriorityPlayerId(opponent.getId());
game.firePriorityEvent(opponent.getId());
}
List<ActivatedAbility> abilities = opponent.getPlayable(game, true);
Map<String, String> choices = new HashMap<>();
abilities.forEach(ability -> {
MageObject object = ability.getSourceObject(game);
choices.put(ability.getId().toString(), object.getName() + ": " + ability.toString());
});
// TODO: set priority for us?
Choice choice = new ChoiceImpl(false);
choice.setMessage("Choose playable ability to activate by opponent " + opponent.getName());
choice.setKeyChoices(choices);
if (feedbackPlayer.choose(Outcome.Detriment, choice, game) && choice.getChoiceKey() != null) {
String needId = choice.getChoiceKey();
Optional<ActivatedAbility> ability = abilities.stream().filter(a -> a.getId().toString().equals(needId)).findFirst();
if (ability.isPresent()) {
// TODO: set priority for player?
ActivatedAbility activatedAbility = ability.get();
game.informPlayers(feedbackPlayer.getLogName() + " as another player " + opponent.getLogName()
+ " trying to force an activate ability: " + activatedAbility.getGameLogMessage(game));
if (opponent.activateAbility(activatedAbility, game)) {
game.informPlayers("Force to activate ability: DONE");
} else {
game.informPlayers("Force to activate ability: FAIL");
}
}
}
// restore original priority player
if (savedPriorityPlayer != null) {
game.getState().setPriorityPlayerId(savedPriorityPlayer);
game.firePriorityEvent(savedPriorityPlayer);
}
break;
}
case COMMAND_CARDS_ADD_TO_HAND: {
// card
@ -552,12 +502,12 @@ public final class SystemUtil {
break;
}
case COMMAND_OPPONENT_UNDER_CONTROL_START: {
Target target = new TargetPlayer().withNotTarget(true).withChooseHint("to take under your control");
case COMMAND_UNDER_CONTROL_TAKE: {
Target target = new TargetOpponent().withNotTarget(true).withChooseHint("to take under your control");
Ability fakeSourceAbility = fakeSourceAbilityTemplate.copy();
if (feedbackPlayer.chooseTarget(Outcome.GainControl, target, fakeSourceAbility, game)) {
Player targetPlayer = game.getPlayer(target.getFirstTarget());
if (targetPlayer != null && targetPlayer != feedbackPlayer) {
if (!targetPlayer.getId().equals(feedbackPlayer.getId())) {
CardUtil.takeControlUnderPlayerStart(game, fakeSourceAbility, feedbackPlayer, targetPlayer, false);
// allow priority play again in same step (for better cheat UX)
targetPlayer.resetPassed();
@ -568,13 +518,19 @@ public final class SystemUtil {
break;
}
case COMMAND_OPPONENT_UNDER_CONTROL_END: {
Target target = new TargetPlayer().withNotTarget(true).withChooseHint("to free from your control");
case COMMAND_UNDER_CONTROL_GIVE: {
Target target = new TargetPlayer().withNotTarget(true).withChooseHint("to give control of your player");
Ability fakeSourceAbility = fakeSourceAbilityTemplate.copy();
if (feedbackPlayer.chooseTarget(Outcome.GainControl, target, fakeSourceAbility, game)) {
Player targetPlayer = game.getPlayer(target.getFirstTarget());
if (targetPlayer != null && targetPlayer != feedbackPlayer && !targetPlayer.isGameUnderControl()) {
CardUtil.takeControlUnderPlayerEnd(game, fakeSourceAbility, feedbackPlayer, targetPlayer);
if (targetPlayer != null) {
if (!targetPlayer.getId().equals(feedbackPlayer.getId())) {
// give control to another player
CardUtil.takeControlUnderPlayerStart(game, fakeSourceAbility, targetPlayer, feedbackPlayer, false);
} else {
// return control to itself
CardUtil.takeControlUnderPlayerEnd(game, fakeSourceAbility, feedbackPlayer, targetPlayer);
}
}
// workaround for refresh priority dialog like avatar click (cheats called from priority in 99%)
game.firePriorityEvent(feedbackPlayer.getId());