forked from External/mage
GUI: Autochoose targets if choice can be made (#9206)
This commit is contained in:
parent
1e01efd49d
commit
96f6fbefc8
29 changed files with 492 additions and 213 deletions
|
|
@ -788,7 +788,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
@Override
|
||||
public String getRule(boolean all) {
|
||||
StringBuilder sbRule = threadLocalBuilder.get();
|
||||
if (all || this.abilityType != AbilityType.SPELL) {
|
||||
if (all || this.abilityType != AbilityType.SPELL) { // TODO: Why the override for non-spells?
|
||||
if (!manaCosts.isEmpty()) {
|
||||
sbRule.append(manaCosts.getText());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1054,4 +1054,16 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @return
|
||||
*/
|
||||
FilterMana getPhyrexianColors();
|
||||
|
||||
/**
|
||||
* Function to query if the player has strictChooseMode enabled. Only the test player can have it.
|
||||
* Function is added here so that the test suite project does not have to be imported into the client/server project.
|
||||
*
|
||||
* @return whether the player has strictChooseMode enabled
|
||||
*/
|
||||
public default boolean getStrictChooseMode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public UserData getControllingPlayersUserData(Game game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4268,6 +4268,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return this.userData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserData getControllingPlayersUserData(Game game) {
|
||||
if (!isGameUnderControl()) {
|
||||
Player player = game.getPlayer(getTurnControlledBy());
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public class UserData implements Serializable {
|
|||
protected boolean passPriorityCast;
|
||||
protected boolean passPriorityActivation;
|
||||
protected boolean autoOrderTrigger;
|
||||
protected int autoTargetLevel;
|
||||
protected boolean useSameSettingsForReplacementEffects;
|
||||
protected boolean useFirstManaAbility = false;
|
||||
private String userIdStr;
|
||||
|
|
@ -50,6 +51,7 @@ public class UserData implements Serializable {
|
|||
boolean passPriorityCast,
|
||||
boolean passPriorityActivation,
|
||||
boolean autoOrderTrigger,
|
||||
int autoTargetLevel,
|
||||
boolean useSameSettingsForReplacementEffects,
|
||||
boolean useFirstManaAbility,
|
||||
String userIdStr) {
|
||||
|
|
@ -66,6 +68,7 @@ public class UserData implements Serializable {
|
|||
this.passPriorityCast = passPriorityCast;
|
||||
this.passPriorityActivation = passPriorityActivation;
|
||||
this.autoOrderTrigger = autoOrderTrigger;
|
||||
this.autoTargetLevel = autoTargetLevel;
|
||||
this.useSameSettingsForReplacementEffects = useSameSettingsForReplacementEffects;
|
||||
this.useFirstManaAbility = useFirstManaAbility;
|
||||
this.matchHistory = "";
|
||||
|
|
@ -90,6 +93,7 @@ public class UserData implements Serializable {
|
|||
this.passPriorityCast = userData.passPriorityCast;
|
||||
this.passPriorityActivation = userData.passPriorityActivation;
|
||||
this.autoOrderTrigger = userData.autoOrderTrigger;
|
||||
this.autoTargetLevel = userData.autoTargetLevel;
|
||||
this.useSameSettingsForReplacementEffects = userData.useSameSettingsForReplacementEffects;
|
||||
this.useFirstManaAbility = userData.useFirstManaAbility;
|
||||
this.userIdStr = userData.userIdStr;
|
||||
|
|
@ -111,6 +115,7 @@ public class UserData implements Serializable {
|
|||
false,
|
||||
false,
|
||||
true,
|
||||
1,
|
||||
true,
|
||||
false,
|
||||
""
|
||||
|
|
@ -235,14 +240,22 @@ public class UserData implements Serializable {
|
|||
return autoOrderTrigger;
|
||||
}
|
||||
|
||||
public boolean isUseSameSettingsForReplacementEffects() {
|
||||
return useSameSettingsForReplacementEffects;
|
||||
}
|
||||
|
||||
public void setAutoOrderTrigger(boolean autoOrderTrigger) {
|
||||
this.autoOrderTrigger = autoOrderTrigger;
|
||||
}
|
||||
|
||||
public int getAutoTargetLevel() {
|
||||
return autoTargetLevel;
|
||||
}
|
||||
|
||||
public void setAutoTargetLevel(int autoTargetLevel) {
|
||||
this.autoTargetLevel = autoTargetLevel;
|
||||
}
|
||||
|
||||
public boolean isUseSameSettingsForReplacementEffects() {
|
||||
return useSameSettingsForReplacementEffects;
|
||||
}
|
||||
|
||||
public boolean isUseFirstManaAbility() {
|
||||
return useFirstManaAbility;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mage.target;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.cards.Cards;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.Filter;
|
||||
|
|
@ -8,6 +9,7 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
|
@ -159,4 +161,35 @@ public interface Target extends Serializable {
|
|||
int getSize();
|
||||
|
||||
boolean contains(UUID targetId);
|
||||
|
||||
/**
|
||||
* This function tries to auto-choose the next target.
|
||||
* <p>
|
||||
* It will NOT add it to the list of targets, it will ony choose the next target
|
||||
* <p>
|
||||
* Use this version when the targets is selected from targets.getTargets.
|
||||
* <p>
|
||||
* It will auto-choosen if all of the following criteria are met:
|
||||
* - The minimum and maximum number of targets is the same (i.e. effect does not have "up to" in its name)
|
||||
* - The number of valid targets is equal to the number of targets still left to be specified
|
||||
*
|
||||
*
|
||||
* @param abilityControllerId
|
||||
* @param source
|
||||
* @param game
|
||||
* @return The UUID of the chosen option, or null if one could not be chosen
|
||||
*/
|
||||
UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game);
|
||||
|
||||
/**
|
||||
* Use this version when the target is chosen from a specified collection.
|
||||
* E.g. {@link Player#chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game)}
|
||||
*
|
||||
* @param abilityControllerId
|
||||
* @param source
|
||||
* @param game
|
||||
* @param possibleTargets
|
||||
* @return
|
||||
*/
|
||||
UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game, Collection<UUID> possibleTargets);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mage.game.Game;
|
|||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.TargetEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.RandomUtil;
|
||||
|
|
@ -317,8 +318,14 @@ public abstract class TargetImpl implements Target {
|
|||
possibleTargets.remove(index);
|
||||
}
|
||||
}
|
||||
} else if (!targetController.chooseTarget(outcome, this, source, game)) {
|
||||
return chosen;
|
||||
} else {
|
||||
// Try to autochoosen
|
||||
UUID autoChosenId = tryToAutoChoose(playerId, source, game);
|
||||
if (autoChosenId != null) {
|
||||
addTarget(autoChosenId, source, game);
|
||||
} else if (!targetController.chooseTarget(outcome, this, source, game)) { // If couldn't autochoose ask player
|
||||
return chosen;
|
||||
}
|
||||
}
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
} while (!isChosen() && !doneChoosing());
|
||||
|
|
@ -595,4 +602,82 @@ public abstract class TargetImpl implements Target {
|
|||
public boolean contains(UUID targetId) {
|
||||
return targets.containsKey(targetId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game) {
|
||||
Set<UUID> possibleTargets = possibleTargets(abilityControllerId, source, game);
|
||||
possibleTargets.removeAll(this.targets.keySet());
|
||||
return tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game, Collection<UUID> possibleTargets) {
|
||||
Player player = game.getPlayer(abilityControllerId);
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
int playerAutoTargetLevel;
|
||||
if (player.isHuman() && player.getControllingPlayersUserData(game) != null) { // Ensure that non-strictChooseMode ComputerPlayer will still use this ability
|
||||
playerAutoTargetLevel = player.getControllingPlayersUserData(game).getAutoTargetLevel();
|
||||
} else {
|
||||
playerAutoTargetLevel = 2;
|
||||
}
|
||||
boolean strictModeEnabled = player.getStrictChooseMode();
|
||||
boolean canAutoChoose = this.getMinNumberOfTargets() == this.getMaxNumberOfTargets() && // Targets must be picked
|
||||
possibleTargets.size() == this.getNumberOfTargets() - this.getSize() && // Available targets are equal to the number that must be picked
|
||||
!strictModeEnabled && // Test AI is not set to strictChooseMode(true)
|
||||
playerAutoTargetLevel > 0; // Human player has enabled auto-choose in settings
|
||||
|
||||
if (canAutoChoose) {
|
||||
boolean autoTargetAll = playerAutoTargetLevel == 2;
|
||||
for (UUID possibleChooseId : possibleTargets) {
|
||||
// Don't pick a target that's already been chosen, this will lead to an infinite loop of
|
||||
// choosen and unchoosing the same target.
|
||||
if (this.targets.containsKey(possibleChooseId)) {
|
||||
continue;
|
||||
}
|
||||
if (autoTargetAll) { // No need for further checks since all targeting is to be automated
|
||||
return possibleChooseId;
|
||||
}
|
||||
|
||||
// Check if you control the target (or own the card)
|
||||
boolean targetingOwnThing;
|
||||
if (possibleChooseId == abilityControllerId) {
|
||||
targetingOwnThing = true;
|
||||
} else {
|
||||
Permanent targetPermanent = game.getPermanent(possibleChooseId);
|
||||
Card targetCard = game.getCard(possibleChooseId);
|
||||
Spell targetSpell = game.getSpell(possibleChooseId);
|
||||
if (targetPermanent != null) {
|
||||
targetingOwnThing = abilityControllerId == targetPermanent.getControllerId();
|
||||
} else if (targetCard != null) {
|
||||
targetingOwnThing = abilityControllerId == targetCard.getOwnerId();
|
||||
} else if (targetSpell != null) {
|
||||
targetingOwnThing = abilityControllerId == targetSpell.getControllerId();
|
||||
} else {
|
||||
// No point further checking
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If you control (or own the card) the target, check if it's one of the feel-bad effects.
|
||||
if (targetingOwnThing) {
|
||||
String abilityText = source.getRule(true).toLowerCase();
|
||||
|
||||
if (abilityText.contains("discard")
|
||||
|| abilityText.contains("sacrifice")
|
||||
|| abilityText.contains("destroy")
|
||||
|| abilityText.contains("exile")) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise return the target with the return statement below.
|
||||
}
|
||||
|
||||
// If we get here then it means that the target UUID passes the checks.
|
||||
return possibleChooseId;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue