forked from External/mage
Turn under control reworked:
- game: added support when a human is take control over a computer player (related to #12878); - game: fixed game freezes while controlling player leaves/disconnect on active priority/choose of another player;
This commit is contained in:
parent
49b90820e0
commit
0505f5159e
12 changed files with 534 additions and 32 deletions
|
|
@ -0,0 +1,401 @@
|
||||||
|
package mage.player.ai;
|
||||||
|
|
||||||
|
import mage.MageObject;
|
||||||
|
import mage.abilities.*;
|
||||||
|
import mage.abilities.costs.VariableCost;
|
||||||
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.Cards;
|
||||||
|
import mage.cards.decks.Deck;
|
||||||
|
import mage.choices.Choice;
|
||||||
|
import mage.constants.ManaType;
|
||||||
|
import mage.constants.MultiAmountType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.RangeOfInfluence;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.combat.CombatGroup;
|
||||||
|
import mage.game.draft.Draft;
|
||||||
|
import mage.game.match.Match;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.game.tournament.Tournament;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
|
import mage.target.TargetAmount;
|
||||||
|
import mage.target.TargetCard;
|
||||||
|
import mage.util.MultiAmountMessage;
|
||||||
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI player that can be taken under control by another player (AI or human).
|
||||||
|
* <p>
|
||||||
|
* Under control logic on choose dialog (under human):
|
||||||
|
* - create fake human player and assign it to real human data transfer object (for income answers);
|
||||||
|
* - call choose dialog from fake human (e.g. send choose data to real player);
|
||||||
|
* - game will process all sending and answering logic as "human under human" logic;
|
||||||
|
* - return choose dialog result without AI code processing;
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class ComputerPlayerControllableProxy extends ComputerPlayer7 {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ComputerPlayerControllableProxy.class);
|
||||||
|
|
||||||
|
Player lastControllingPlayer = null;
|
||||||
|
|
||||||
|
public ComputerPlayerControllableProxy(String name, RangeOfInfluence range, int skill) {
|
||||||
|
super(name, range, skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComputerPlayerControllableProxy(final ComputerPlayerControllableProxy player) {
|
||||||
|
super(player);
|
||||||
|
this.lastControllingPlayer = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ComputerPlayerControllableProxy copy() {
|
||||||
|
return new ComputerPlayerControllableProxy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isUnderMe(Game game) {
|
||||||
|
return game.isSimulation() || this.isGameUnderControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Player getControllingPlayer(Game game) {
|
||||||
|
Player player = game.getPlayer(this.getTurnControlledBy());
|
||||||
|
this.lastControllingPlayer = player.prepareControllableProxy(this);
|
||||||
|
return this.lastControllingPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResponseString(String responseString) {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.setResponseString(responseString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResponseManaType(UUID manaTypePlayerId, ManaType responseManaType) {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.setResponseManaType(manaTypePlayerId, responseManaType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResponseUUID(UUID responseUUID) {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.setResponseUUID(responseUUID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResponseBoolean(Boolean responseBoolean) {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.setResponseBoolean(responseBoolean);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setResponseInteger(Integer responseInteger) {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.setResponseInteger(responseInteger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void signalPlayerCheat() {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.signalPlayerCheat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void signalPlayerConcede(boolean stopCurrentChooseDialog) {
|
||||||
|
if (this.lastControllingPlayer != null) {
|
||||||
|
this.lastControllingPlayer.signalPlayerConcede(stopCurrentChooseDialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean priority(Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.priority(game);
|
||||||
|
} else {
|
||||||
|
Player player = getControllingPlayer(game);
|
||||||
|
try {
|
||||||
|
return player.priority(game);
|
||||||
|
} finally {
|
||||||
|
this.passed = player.isPassed(); // TODO: wtf, no needs?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chooseMulligan(Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseMulligan(game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseMulligan(game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chooseUse(Outcome outcome, String message, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseUse(outcome, message, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseUse(outcome, message, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseUse(outcome, message, secondMessage, trueText, falseText, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseUse(outcome, message, secondMessage, trueText, falseText, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int chooseReplacementEffect(Map<String, String> effectsMap, Map<String, MageObject> objectsMap, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseReplacementEffect(effectsMap, objectsMap, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseReplacementEffect(effectsMap, objectsMap, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean choose(Outcome outcome, Choice choice, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.choose(outcome, choice, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).choose(outcome, choice, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.choose(outcome, target, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).choose(outcome, target, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.choose(outcome, target, source, game, options);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).choose(outcome, target, source, game, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseTarget(outcome, target, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseTarget(outcome, target, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.choose(outcome, cards, target, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).choose(outcome, cards, target, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseTarget(outcome, cards, target, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseTarget(outcome, cards, target, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseTargetAmount(outcome, target, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseTargetAmount(outcome, target, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TriggeredAbility chooseTriggeredAbility(java.util.List<TriggeredAbility> abilities, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseTriggeredAbility(abilities, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseTriggeredAbility(abilities, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean playMana(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.playMana(abilityToCast, unpaid, promptText, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).playMana(abilityToCast, unpaid, promptText, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.announceXMana(min, max, message, game, ability);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).announceXMana(min, max, message, game, ability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.announceXCost(min, max, message, game, ability, variableCost);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).announceXCost(min, max, message, game, ability, variableCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectAttackers(Game game, UUID attackingPlayerId) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
super.selectAttackers(game, attackingPlayerId);
|
||||||
|
} else {
|
||||||
|
getControllingPlayer(game).selectAttackers(game, attackingPlayerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectBlockers(Ability source, Game game, UUID defendingPlayerId) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
super.selectBlockers(source, game, defendingPlayerId);
|
||||||
|
} else {
|
||||||
|
getControllingPlayer(game).selectBlockers(source, game, defendingPlayerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID chooseAttackerOrder(java.util.List<Permanent> attackers, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseAttackerOrder(attackers, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseAttackerOrder(attackers, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID chooseBlockerOrder(java.util.List<Permanent> blockers, CombatGroup combatGroup, java.util.List<UUID> blockerOrder, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseBlockerOrder(blockers, combatGroup, blockerOrder, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAmount(int min, int max, String message, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.getAmount(min, max, message, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).getAmount(min, max, message, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Integer> getMultiAmountWithIndividualConstraints(
|
||||||
|
Outcome outcome,
|
||||||
|
List<MultiAmountMessage> messages,
|
||||||
|
int totalMin,
|
||||||
|
int totalMax,
|
||||||
|
MultiAmountType type,
|
||||||
|
Game game
|
||||||
|
) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.getMultiAmountWithIndividualConstraints(outcome, messages, totalMin, totalMax, type, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).getMultiAmountWithIndividualConstraints(outcome, messages, totalMin, totalMax, type, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sideboard(Match match, Deck deck) {
|
||||||
|
super.sideboard(match, deck);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void construct(Tournament tournament, Deck deck) {
|
||||||
|
super.construct(tournament, deck);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pickCard(java.util.List<Card> cards, Deck deck, Draft draft) {
|
||||||
|
super.pickCard(cards, deck, draft);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean activateAbility(ActivatedAbility ability, Game game) {
|
||||||
|
// TODO: need research, see HumanPlayer's code
|
||||||
|
return super.activateAbility(ability, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseAbilityForCast(card, game, noMana);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseAbilityForCast(card, game, noMana);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActivatedAbility chooseLandOrSpellAbility(Card card, Game game, boolean noMana) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseLandOrSpellAbility(card, game, noMana);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseLandOrSpellAbility(card, game, noMana);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mode chooseMode(Modes modes, Ability source, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.chooseMode(modes, source, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).chooseMode(modes, source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean choosePile(Outcome outcome, String message, java.util.List<? extends Card> pile1, java.util.List<? extends Card> pile2, Game game) {
|
||||||
|
if (isUnderMe(game)) {
|
||||||
|
return super.choosePile(outcome, message, pile1, pile2, game);
|
||||||
|
} else {
|
||||||
|
return getControllingPlayer(game).choosePile(outcome, message, pile1, pile2, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void abort() {
|
||||||
|
// TODO: need research, is it require real player call? Concede/leave/timeout works by default
|
||||||
|
super.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skip() {
|
||||||
|
// TODO: see abort comments above
|
||||||
|
super.skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// * - GAME thread: on notify from response - check new answer value and process it (if it bad then repeat and wait the next one);
|
// * - GAME thread: on notify from response - check new answer value and process it (if it bad then repeat and wait the next one);
|
||||||
private transient Boolean responseOpenedForAnswer = false; // GAME thread waiting new answer
|
private transient Boolean responseOpenedForAnswer = false; // GAME thread waiting new answer
|
||||||
private transient long responseLastWaitingThreadId = 0;
|
private transient long responseLastWaitingThreadId = 0;
|
||||||
private final transient PlayerResponse response = new PlayerResponse();
|
private final transient PlayerResponse response; // data receiver from a client side (must be shared for one player between multiple clients)
|
||||||
private final int RESPONSE_WAITING_TIME_SECS = 30; // waiting time before cancel current response
|
private final int RESPONSE_WAITING_TIME_SECS = 30; // waiting time before cancel current response
|
||||||
private final int RESPONSE_WAITING_CHECK_MS = 100; // timeout for open status check
|
private final int RESPONSE_WAITING_CHECK_MS = 100; // timeout for open status check
|
||||||
|
|
||||||
|
|
@ -105,7 +105,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
protected static FilterCreatureForCombat filterCreatureForCombat = new FilterCreatureForCombat();
|
protected static FilterCreatureForCombat filterCreatureForCombat = new FilterCreatureForCombat();
|
||||||
protected static FilterAttackingCreature filterAttack = new FilterAttackingCreature();
|
protected static FilterAttackingCreature filterAttack = new FilterAttackingCreature();
|
||||||
protected static FilterBlockingCreature filterBlock = new FilterBlockingCreature();
|
protected static FilterBlockingCreature filterBlock = new FilterBlockingCreature();
|
||||||
protected final Choice replacementEffectChoice;
|
protected Choice replacementEffectChoice = null;
|
||||||
private static final Logger logger = Logger.getLogger(HumanPlayer.class);
|
private static final Logger logger = Logger.getLogger(HumanPlayer.class);
|
||||||
|
|
||||||
protected HashSet<String> autoSelectReplacementEffects = new LinkedHashSet<>(); // must be sorted
|
protected HashSet<String> autoSelectReplacementEffects = new LinkedHashSet<>(); // must be sorted
|
||||||
|
|
@ -131,8 +131,12 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
public HumanPlayer(String name, RangeOfInfluence range, int skill) {
|
public HumanPlayer(String name, RangeOfInfluence range, int skill) {
|
||||||
super(name, range);
|
super(name, range);
|
||||||
human = true;
|
this.human = true;
|
||||||
|
this.response = new PlayerResponse();
|
||||||
|
initReplacementDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initReplacementDialog() {
|
||||||
replacementEffectChoice = new ChoiceImpl(true);
|
replacementEffectChoice = new ChoiceImpl(true);
|
||||||
replacementEffectChoice.setMessage("Choose replacement effect to resolve first");
|
replacementEffectChoice.setMessage("Choose replacement effect to resolve first");
|
||||||
replacementEffectChoice.setSpecial(
|
replacementEffectChoice.setSpecial(
|
||||||
|
|
@ -143,8 +147,20 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make fake player from any other
|
||||||
|
*/
|
||||||
|
public HumanPlayer(final PlayerImpl sourcePlayer, final PlayerResponse sourceResponse) {
|
||||||
|
super(sourcePlayer);
|
||||||
|
this.human = true;
|
||||||
|
this.response = sourceResponse; // need for sync and wait user's response from a network
|
||||||
|
initReplacementDialog();
|
||||||
|
}
|
||||||
|
|
||||||
public HumanPlayer(final HumanPlayer player) {
|
public HumanPlayer(final HumanPlayer player) {
|
||||||
super(player);
|
super(player);
|
||||||
|
this.response = player.response;
|
||||||
|
|
||||||
this.replacementEffectChoice = player.replacementEffectChoice;
|
this.replacementEffectChoice = player.replacementEffectChoice;
|
||||||
this.autoSelectReplacementEffects.addAll(player.autoSelectReplacementEffects);
|
this.autoSelectReplacementEffects.addAll(player.autoSelectReplacementEffects);
|
||||||
this.currentlyUnpaidMana = player.currentlyUnpaidMana;
|
this.currentlyUnpaidMana = player.currentlyUnpaidMana;
|
||||||
|
|
@ -2940,11 +2956,6 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHistory() {
|
|
||||||
return "no available";
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean gameInCheckPlayableState(Game game) {
|
private boolean gameInCheckPlayableState(Game game) {
|
||||||
return gameInCheckPlayableState(game, false);
|
return gameInCheckPlayableState(game, false);
|
||||||
}
|
}
|
||||||
|
|
@ -2962,4 +2973,14 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Player prepareControllableProxy(Player playerUnderControl) {
|
||||||
|
// make fake player, e.g. transform computer player to human player for choose dialogs under control
|
||||||
|
HumanPlayer fakePlayer = new HumanPlayer((PlayerImpl) playerUnderControl, this.response);
|
||||||
|
if (!fakePlayer.getTurnControlledBy().equals(this.getId())) {
|
||||||
|
throw new IllegalArgumentException("Wrong code usage: controllable proxy must be controlled by " + this.getName());
|
||||||
|
}
|
||||||
|
return fakePlayer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
/>
|
/>
|
||||||
<playerTypes>
|
<playerTypes>
|
||||||
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
|
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
|
||||||
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayer7"/>
|
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayerControllableProxy"/>
|
||||||
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
|
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
|
||||||
<playerType name="Computer - draftbot" jar="mage-player-ai-draft-bot.jar" className="mage.player.ai.ComputerDraftPlayer"/>
|
<playerType name="Computer - draftbot" jar="mage-player-ai-draft-bot.jar" className="mage.player.ai.ComputerDraftPlayer"/>
|
||||||
</playerTypes>
|
</playerTypes>
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
/>
|
/>
|
||||||
<playerTypes>
|
<playerTypes>
|
||||||
<playerType name="Human" jar="mage-player-human-${project.version}.jar" className="mage.player.human.HumanPlayer"/>
|
<playerType name="Human" jar="mage-player-human-${project.version}.jar" className="mage.player.human.HumanPlayer"/>
|
||||||
<playerType name="Computer - mad" jar="mage-player-ai-ma-${project.version}.jar" className="mage.player.ai.ComputerPlayer7"/>
|
<playerType name="Computer - mad" jar="mage-player-ai-ma-${project.version}.jar" className="mage.player.ai.ComputerPlayerControllableProxy"/>
|
||||||
<playerType name="Computer - draftbot" jar="mage-player-ai-draftbot-${project.version}.jar" className="mage.player.ai.ComputerDraftPlayer"/>
|
<playerType name="Computer - draftbot" jar="mage-player-ai-draftbot-${project.version}.jar" className="mage.player.ai.ComputerDraftPlayer"/>
|
||||||
</playerTypes>
|
</playerTypes>
|
||||||
<gameTypes>
|
<gameTypes>
|
||||||
|
|
|
||||||
|
|
@ -786,23 +786,23 @@ public class GameController implements GameCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayerUUID(UUID userId, final UUID data) {
|
public void sendPlayerUUID(UUID userId, final UUID data) {
|
||||||
sendMessage(userId, playerId -> getGameSession(playerId).sendPlayerUUID(data));
|
sendMessage(userId, playerId -> sendDirectPlayerUUID(playerId, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayerString(UUID userId, final String data) {
|
public void sendPlayerString(UUID userId, final String data) {
|
||||||
sendMessage(userId, playerId -> getGameSession(playerId).sendPlayerString(data));
|
sendMessage(userId, playerId -> sendDirectPlayerString(playerId, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayerManaType(UUID userId, final UUID manaTypePlayerId, final ManaType data) {
|
public void sendPlayerManaType(UUID userId, final UUID manaTypePlayerId, final ManaType data) {
|
||||||
sendMessage(userId, playerId -> getGameSession(playerId).sendPlayerManaType(data, manaTypePlayerId));
|
sendMessage(userId, playerId -> sendDirectPlayerManaType(playerId, manaTypePlayerId, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayerBoolean(UUID userId, final Boolean data) {
|
public void sendPlayerBoolean(UUID userId, final Boolean data) {
|
||||||
sendMessage(userId, playerId -> getGameSession(playerId).sendPlayerBoolean(data));
|
sendMessage(userId, playerId -> sendDirectPlayerBoolean(playerId, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayerInteger(UUID userId, final Integer data) {
|
public void sendPlayerInteger(UUID userId, final Integer data) {
|
||||||
sendMessage(userId, playerId -> getGameSession(playerId).sendPlayerInteger(data));
|
sendMessage(userId, playerId -> sendDirectPlayerInteger(playerId, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePriorityTimers() {
|
private void updatePriorityTimers() {
|
||||||
|
|
@ -1055,7 +1055,8 @@ public class GameController implements GameCallback {
|
||||||
} else {
|
} else {
|
||||||
// otherwise execute the action under other player's control
|
// otherwise execute the action under other player's control
|
||||||
for (UUID controlled : player.getPlayersUnderYourControl()) {
|
for (UUID controlled : player.getPlayersUnderYourControl()) {
|
||||||
if (gameSessions.containsKey(controlled) && game.getPriorityPlayerId().equals(controlled)) {
|
Player controlledPlayer = game.getPlayer(controlled);
|
||||||
|
if ((gameSessions.containsKey(controlled) || controlledPlayer.isComputer()) && game.getPriorityPlayerId().equals(controlled)) {
|
||||||
stopResponseIdleTimeout();
|
stopResponseIdleTimeout();
|
||||||
command.execute(controlled);
|
command.execute(controlled);
|
||||||
}
|
}
|
||||||
|
|
@ -1098,7 +1099,6 @@ public class GameController implements GameCallback {
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
interface Command {
|
interface Command {
|
||||||
|
|
||||||
void execute(UUID player);
|
void execute(UUID player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1138,6 +1138,81 @@ public class GameController implements GameCallback {
|
||||||
return newGameSessionWatchers;
|
return newGameSessionWatchers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendDirectPlayerUUID(UUID playerId, UUID data) {
|
||||||
|
// real player
|
||||||
|
GameSessionPlayer session = getGameSession(playerId);
|
||||||
|
if (session != null) {
|
||||||
|
session.sendPlayerUUID(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// computer under control
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
if (player != null && player.isComputer()) {
|
||||||
|
player.setResponseUUID(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDirectPlayerString(UUID playerId, String data) {
|
||||||
|
// real player
|
||||||
|
GameSessionPlayer session = getGameSession(playerId);
|
||||||
|
if (session != null) {
|
||||||
|
session.sendPlayerString(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// computer under control
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
if (player != null && player.isComputer()) {
|
||||||
|
player.setResponseString(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDirectPlayerManaType(UUID playerId, UUID manaTypePlayerId, ManaType manaType) {
|
||||||
|
// real player
|
||||||
|
GameSessionPlayer session = getGameSession(playerId);
|
||||||
|
if (session != null) {
|
||||||
|
session.sendPlayerManaType(manaTypePlayerId, manaType);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// computer under control
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
if (player != null && player.isComputer()) {
|
||||||
|
player.setResponseManaType(manaTypePlayerId, manaType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDirectPlayerBoolean(UUID playerId, Boolean data) {
|
||||||
|
// real player
|
||||||
|
GameSessionPlayer session = getGameSession(playerId);
|
||||||
|
if (session != null) {
|
||||||
|
session.sendPlayerBoolean(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// computer under control
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
if (player != null && player.isComputer()) {
|
||||||
|
player.setResponseBoolean(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendDirectPlayerInteger(UUID playerId, Integer data) {
|
||||||
|
// real player
|
||||||
|
GameSessionPlayer session = getGameSession(playerId);
|
||||||
|
if (session != null) {
|
||||||
|
session.sendPlayerInteger(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// computer under control
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
if (player != null && player.isComputer()) {
|
||||||
|
player.setResponseInteger(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private GameSessionPlayer getGameSession(UUID playerId) {
|
private GameSessionPlayer getGameSession(UUID playerId) {
|
||||||
// TODO: check parent callers - there are possible problems with sync, can be related to broken "fix" logs too
|
// TODO: check parent callers - there are possible problems with sync, can be related to broken "fix" logs too
|
||||||
// It modify players data, but:
|
// It modify players data, but:
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
||||||
game.getPlayer(playerId).setResponseString(data);
|
game.getPlayer(playerId).setResponseString(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPlayerManaType(ManaType manaType, UUID manaTypePlayerId) {
|
public void sendPlayerManaType(UUID manaTypePlayerId, ManaType manaType) {
|
||||||
game.getPlayer(playerId).setResponseManaType(manaTypePlayerId, manaType);
|
game.getPlayer(playerId).setResponseManaType(manaTypePlayerId, manaType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
/>
|
/>
|
||||||
<playerTypes>
|
<playerTypes>
|
||||||
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
|
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>
|
||||||
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayer7"/>
|
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayerControllableProxy"/>
|
||||||
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
|
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
|
||||||
<playerType name="Computer - draftbot" jar="mage-player-ai-draft-bot.jar" className="mage.player.ai.ComputerDraftPlayer"/>
|
<playerType name="Computer - draftbot" jar="mage-player-ai-draft-bot.jar" className="mage.player.ai.ComputerDraftPlayer"/>
|
||||||
</playerTypes>
|
</playerTypes>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd">
|
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd">
|
||||||
<server serverAddress="0.0.0.0" serverName="mage-server" port="17171" maxGameThreads="10" maxSecondsIdle="600"/>
|
<server serverAddress="0.0.0.0" serverName="mage-server" port="17171" maxGameThreads="10" maxSecondsIdle="600"/>
|
||||||
<playerTypes>
|
<playerTypes>
|
||||||
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayer7"/>
|
<playerType name="Computer - mad" jar="mage-player-ai-ma.jar" className="mage.player.ai.ComputerPlayerControllableProxy"/>
|
||||||
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
|
<playerType name="Computer - monte carlo" jar="mage-player-aimcts.jar" className="mage.player.ai.ComputerPlayerMCTS"/>
|
||||||
</playerTypes>
|
</playerTypes>
|
||||||
<gameTypes>
|
<gameTypes>
|
||||||
|
|
|
||||||
|
|
@ -4544,10 +4544,6 @@ public class TestPlayer implements Player {
|
||||||
return AIPlayer;
|
return AIPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getHistory() {
|
|
||||||
return computerPlayer.getHistory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PlanarDieRollResult rollPlanarDie(Outcome outcome, Ability source, Game game, int numberChaosSides, int numberPlanarSides) {
|
public PlanarDieRollResult rollPlanarDie(Outcome outcome, Ability source, Game game, int numberChaosSides, int numberPlanarSides) {
|
||||||
return computerPlayer.rollPlanarDie(outcome, source, game, numberChaosSides, numberPlanarSides);
|
return computerPlayer.rollPlanarDie(outcome, source, game, numberChaosSides, numberPlanarSides);
|
||||||
|
|
|
||||||
|
|
@ -845,6 +845,10 @@ public abstract class GameImpl implements Game {
|
||||||
// concede for itself
|
// concede for itself
|
||||||
// stop current player dialog and execute concede
|
// stop current player dialog and execute concede
|
||||||
currentPriorityPlayer.signalPlayerConcede(true);
|
currentPriorityPlayer.signalPlayerConcede(true);
|
||||||
|
} else if (currentPriorityPlayer.getTurnControlledBy().equals(playerId)) {
|
||||||
|
// concede for itself while controlling another player
|
||||||
|
// stop current player dialog and execute concede
|
||||||
|
currentPriorityPlayer.signalPlayerConcede(true);
|
||||||
} else {
|
} else {
|
||||||
// concede for another player
|
// concede for another player
|
||||||
// allow current player to continue and check concede on any next priority
|
// allow current player to continue and check concede on any next priority
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,15 @@ import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author BetaSteward_at_googlemail.com
|
* Warning, if you add new choose dialogs then must implement it for:
|
||||||
|
* - PlayerImpl (only if it use another default dialogs inside)
|
||||||
|
* - HumanPlayer (support client-server in human games)
|
||||||
|
* - ComputerPlayer (support AI in computer games)
|
||||||
|
* - StubPlayer (temp)
|
||||||
|
* - ComputerPlayerControllableProxy (support control of one player type over another player type)
|
||||||
|
* - TestPlayer (support unit tests)
|
||||||
|
*
|
||||||
|
* @author BetaSteward_at_googlemail.com, JayDi85
|
||||||
*/
|
*/
|
||||||
public interface Player extends MageItem, Copyable<Player> {
|
public interface Player extends MageItem, Copyable<Player> {
|
||||||
|
|
||||||
|
|
@ -1212,8 +1220,6 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
*/
|
*/
|
||||||
boolean addTargets(Ability ability, Game game);
|
boolean addTargets(Ability ability, Game game);
|
||||||
|
|
||||||
String getHistory();
|
|
||||||
|
|
||||||
boolean hasDesignation(DesignationType designationName);
|
boolean hasDesignation(DesignationType designationName);
|
||||||
|
|
||||||
void addDesignation(Designation designation);
|
void addDesignation(Designation designation);
|
||||||
|
|
@ -1253,4 +1259,8 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
* so that's method helps to find real player that used by a game (in most use cases it's a PlayerImpl)
|
* so that's method helps to find real player that used by a game (in most use cases it's a PlayerImpl)
|
||||||
*/
|
*/
|
||||||
Player getRealPlayer();
|
Player getRealPlayer();
|
||||||
|
|
||||||
|
default Player prepareControllableProxy(Player playerUnderControl) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5372,11 +5372,6 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHistory() {
|
|
||||||
return "no available";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasDesignation(DesignationType designationName) {
|
public boolean hasDesignation(DesignationType designationName) {
|
||||||
for (Designation designation : designations) {
|
for (Designation designation : designations) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue