1418 lines
43 KiB
Java
1418 lines
43 KiB
Java
package magic.model;
|
|
|
|
import java.time.Instant;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.SortedSet;
|
|
import java.util.TreeSet;
|
|
|
|
import magic.data.GeneralConfig;
|
|
import magic.data.settings.BooleanSetting;
|
|
import magic.exception.GameException;
|
|
import magic.model.action.AddEventAction;
|
|
import magic.model.action.AddFirstEventAction;
|
|
import magic.model.action.DequeueTriggerAction;
|
|
import magic.model.action.EnqueueTriggerAction;
|
|
import magic.model.action.ExecuteFirstEventAction;
|
|
import magic.model.action.LogMarkerAction;
|
|
import magic.model.action.MagicAction;
|
|
import magic.model.action.MagicActionList;
|
|
import magic.model.action.MarkerAction;
|
|
import magic.model.action.PutItemOnStackAction;
|
|
import magic.model.action.RemoveFromPlayAction;
|
|
import magic.model.choice.MagicCombatCreature;
|
|
import magic.model.choice.MagicDeclareAttackersResult;
|
|
import magic.model.choice.MagicDeclareBlockersResult;
|
|
import magic.model.choice.MagicTargetChoice;
|
|
import magic.model.event.MagicEvent;
|
|
import magic.model.event.MagicEventQueue;
|
|
import magic.model.event.MagicUniquenessEvent;
|
|
import magic.model.mstatic.MagicLayer;
|
|
import magic.model.mstatic.MagicPermanentStatic;
|
|
import magic.model.mstatic.MagicPermanentStaticMap;
|
|
import magic.model.mstatic.MagicStatic;
|
|
import magic.model.phase.MagicGameplay;
|
|
import magic.model.phase.MagicPhase;
|
|
import magic.model.phase.MagicPhaseType;
|
|
import magic.model.phase.MagicStep;
|
|
import magic.model.stack.MagicStack;
|
|
import magic.model.target.MagicLegendaryCopiesFilter;
|
|
import magic.model.target.MagicOtherPermanentTargetFilter;
|
|
import magic.model.target.MagicTarget;
|
|
import magic.model.target.MagicTargetFilter;
|
|
import magic.model.target.MagicTargetFilterFactory;
|
|
import magic.model.target.MagicTargetHint;
|
|
import magic.model.target.MagicTargetNone;
|
|
import magic.model.trigger.AtEndOfTurnTrigger;
|
|
import magic.model.trigger.AtUpkeepTrigger;
|
|
import magic.model.trigger.DamageIsDealtTrigger;
|
|
import magic.model.trigger.MagicPermanentTrigger;
|
|
import magic.model.trigger.MagicPermanentTriggerMap;
|
|
import magic.model.trigger.MagicTrigger;
|
|
import magic.model.trigger.MagicTriggerType;
|
|
import magic.model.trigger.OtherEntersBattlefieldTrigger;
|
|
import magic.model.trigger.PreventDamageTrigger;
|
|
import magic.ui.MagicSound;
|
|
|
|
public class MagicGame {
|
|
|
|
public static final boolean LOSE_DRAW_EMPTY_LIBRARY=true;
|
|
private static final long ID_FACTOR=31;
|
|
|
|
private static int COUNT;
|
|
private static MagicGame INSTANCE;
|
|
|
|
private final MagicDuel duel;
|
|
private final MagicPlayer[] players;
|
|
private MagicPermanentTriggerMap triggers;
|
|
private final MagicPermanentTriggerMap additionalTriggers;
|
|
private final MagicPermanentStaticMap statics;
|
|
private final MagicCardList exiledUntilEndOfTurn;
|
|
private final MagicEventQueue events;
|
|
private final MagicStack stack;
|
|
private final MagicStack pendingStack;
|
|
private final MagicPlayer scorePlayer;
|
|
private final MagicGameplay gameplay;
|
|
private final MagicActionList actions;
|
|
private final MagicActionList delayedActions;
|
|
private int score;
|
|
private int turn=1;
|
|
private int startTurn;
|
|
private int mainPhaseCount=100000000;
|
|
private int landsPlayed;
|
|
private int maxLands;
|
|
private int priorityPassedCount;
|
|
private boolean creatureDiedThisTurn;
|
|
private boolean priorityPassed;
|
|
private MagicPhaseType skipTurnTill = MagicPhaseType.Mulligan;
|
|
private boolean stateCheckRequired;
|
|
private boolean artificial;
|
|
private boolean immediate;
|
|
private boolean disableLog;
|
|
private MagicPlayer turnPlayer;
|
|
private MagicPlayer losingPlayer = MagicPlayer.NONE;
|
|
private MagicPhase phase;
|
|
private MagicStep step;
|
|
private MagicPayedCost payedCost;
|
|
private MagicActionList undoPoints;
|
|
private MagicLogBook logBook;
|
|
private MagicLogMessageBuilder logMessageBuilder;
|
|
private MagicSource activeSource = MagicSource.NONE;
|
|
private long[] keys;
|
|
private long stateId;
|
|
private long time = 1000000;
|
|
private boolean isConceded = false;
|
|
|
|
// affects options available to AI for each choice
|
|
private boolean fastMana = false;
|
|
private boolean fastTarget = false;
|
|
private boolean fastBlocker = false;
|
|
private boolean fastGameplay = false;
|
|
private boolean hintTiming = true;
|
|
private boolean hintPriority = true;
|
|
private boolean hintTarget = true;
|
|
|
|
private final long startTimeMilli = Instant.now().toEpochMilli();
|
|
|
|
public static MagicGame getInstance() {
|
|
return INSTANCE;
|
|
}
|
|
|
|
static int getCount() {
|
|
return COUNT;
|
|
}
|
|
|
|
static MagicGame create(final MagicDuel duel, final MagicGameplay gameplay, final MagicPlayer[] players, final MagicPlayer startPlayer) {
|
|
COUNT++;
|
|
INSTANCE = new MagicGame(duel, gameplay, players, startPlayer);
|
|
return INSTANCE;
|
|
}
|
|
|
|
private MagicGame(final MagicDuel aDuel, final MagicGameplay aGameplay, final MagicPlayer[] aPlayers, final MagicPlayer startPlayer) {
|
|
|
|
artificial=false;
|
|
duel = aDuel;
|
|
gameplay = aGameplay;
|
|
players = aPlayers;
|
|
for (final MagicPlayer player : players) {
|
|
player.setGame(this);
|
|
}
|
|
|
|
triggers=new MagicPermanentTriggerMap();
|
|
additionalTriggers=new MagicPermanentTriggerMap();
|
|
statics = new MagicPermanentStaticMap();
|
|
exiledUntilEndOfTurn=new MagicCardList();
|
|
events=new MagicEventQueue();
|
|
stack=new MagicStack();
|
|
pendingStack=new MagicStack();
|
|
scorePlayer=players[0];
|
|
turnPlayer=startPlayer;
|
|
actions=new MagicActionList();
|
|
delayedActions=new MagicActionList();
|
|
undoPoints=new MagicActionList();
|
|
logBook=new MagicLogBook();
|
|
logMessageBuilder=new MagicLogMessageBuilder(this);
|
|
payedCost=new MagicPayedCost();
|
|
changePhase(gameplay.getStartPhase(this));
|
|
}
|
|
|
|
public MagicGame(final MagicGame game,final MagicPlayer aScorePlayer) {
|
|
|
|
artificial=true;
|
|
|
|
//copy the reference, these are singletons
|
|
duel=game.duel;
|
|
gameplay=game.gameplay;
|
|
phase=game.phase;
|
|
step=game.step;
|
|
|
|
//copying primitives, array of primitive
|
|
time = game.time;
|
|
turn = game.turn;
|
|
startTurn = game.startTurn;
|
|
landsPlayed = game.landsPlayed;
|
|
maxLands = game.maxLands;
|
|
creatureDiedThisTurn = game.creatureDiedThisTurn;
|
|
priorityPassed = game.priorityPassed;
|
|
priorityPassedCount = game.priorityPassedCount;
|
|
stateCheckRequired = game.stateCheckRequired;
|
|
|
|
//copied and stored in copyMap
|
|
final MagicCopyMap copyMap=new MagicCopyMap();
|
|
players=copyMap.copyObjects(game.players,MagicPlayer.class);
|
|
for (final MagicPlayer player : players) {
|
|
player.setGame(this);
|
|
}
|
|
scorePlayer=copyMap.copy(aScorePlayer);
|
|
turnPlayer=copyMap.copy(game.turnPlayer);
|
|
losingPlayer=copyMap.copy(game.losingPlayer);
|
|
payedCost=copyMap.copy(game.payedCost);
|
|
activeSource=copyMap.copy(game.activeSource);
|
|
|
|
//construct a new object using copyMap to copy internals
|
|
events=new MagicEventQueue(copyMap, game.events);
|
|
stack=new MagicStack(copyMap, game.stack);
|
|
pendingStack=new MagicStack(copyMap, game.pendingStack);
|
|
triggers=new MagicPermanentTriggerMap(copyMap, game.triggers);
|
|
additionalTriggers=new MagicPermanentTriggerMap(copyMap, game.additionalTriggers);
|
|
statics=new MagicPermanentStaticMap(copyMap, game.statics);
|
|
exiledUntilEndOfTurn=new MagicCardList(copyMap, game.exiledUntilEndOfTurn);
|
|
|
|
//the following are NOT copied when game state is cloned
|
|
//fastMana
|
|
//fastTarget
|
|
//fastBlocker
|
|
//immediate
|
|
//hintTiming
|
|
//hintPriority
|
|
//hintTarget
|
|
//skipTurnTill
|
|
//mainPhaseCount
|
|
|
|
//score is RESET to zero
|
|
score=0;
|
|
|
|
//historical actions are not carried over
|
|
actions=new MagicActionList();
|
|
|
|
//there should be no pending actions
|
|
assert game.delayedActions.isEmpty() : "delayedActions: " + game.delayedActions;
|
|
delayedActions=new MagicActionList();
|
|
|
|
//no logging
|
|
disableLog = true;
|
|
undoPoints=null;
|
|
logBook=null;
|
|
logMessageBuilder=null;
|
|
}
|
|
|
|
public void skipTurnTill(final MagicPhaseType skip) {
|
|
skipTurnTill = skip;
|
|
}
|
|
|
|
public void clearSkipTurnTill() {
|
|
skipTurnTill = MagicPhaseType.Mulligan;
|
|
}
|
|
|
|
public boolean shouldSkip() {
|
|
return phase.getType().ordinal() < skipTurnTill.ordinal();
|
|
}
|
|
|
|
public void setScore(final int aScore) {
|
|
score = aScore;
|
|
}
|
|
|
|
public void changeScore(final int amount) {
|
|
score+=amount;
|
|
}
|
|
|
|
public int getScore() {
|
|
return score;
|
|
}
|
|
|
|
public long getUniqueId() {
|
|
time++;
|
|
return time;
|
|
}
|
|
|
|
//follow factors in MagicMarkerAction
|
|
public long getStateId() {
|
|
keys = new long[] {
|
|
turn,
|
|
phase.hashCode(),
|
|
step.hashCode(),
|
|
turnPlayer.getIndex(),
|
|
landsPlayed,
|
|
maxLands,
|
|
priorityPassedCount,
|
|
creatureDiedThisTurn ? 1L : -1L,
|
|
priorityPassed ? 1L : -1L,
|
|
stateCheckRequired ? 1L : -1L,
|
|
payedCost.getStateId(),
|
|
stack.getStateId(),
|
|
pendingStack.getStateId(),
|
|
events.getStateId(),
|
|
players[0].getStateId(),
|
|
players[1].getStateId(),
|
|
activeSource.getStateId(),
|
|
triggers.getStateId(),
|
|
statics.getStateId(),
|
|
exiledUntilEndOfTurn.getStateId(),
|
|
};
|
|
stateId = MurmurHash3.hash(keys);
|
|
return stateId;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "GAME: " +
|
|
"id=" + stateId + " " +
|
|
"t=" + turn + " " +
|
|
"p=" + phase.getType() + " " +
|
|
"s=" + step + " " +
|
|
"tp=" + turnPlayer.getIndex() + " " +
|
|
"lp=" + landsPlayed + " " +
|
|
"ppc=" + priorityPassedCount + " " +
|
|
"pp=" + priorityPassed + " " +
|
|
"sc=" + stateCheckRequired + " " +
|
|
"x=" + getPayedCost().getX() + " " +
|
|
"e=" + events.size() + " " +
|
|
"s=" + stack.size();
|
|
}
|
|
|
|
public String getIdString() {
|
|
final StringBuilder sb = new StringBuilder(toString());
|
|
sb.append('\n');
|
|
sb.append(keys[0]);
|
|
for (int i = 1; i < keys.length; i++) {
|
|
sb.append(' ').append(keys[i]);
|
|
}
|
|
for (MagicPlayer player : players) {
|
|
sb.append('\n').append(player.getIdString());
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
public long getGameId(final int pruneScore) {
|
|
long id=0;
|
|
id = id*ID_FACTOR + turn;
|
|
id = id*ID_FACTOR + phase.getType().ordinal();
|
|
id = id*ID_FACTOR + score + pruneScore;
|
|
for (MagicPlayer player : players) {
|
|
id = player.getPlayerId(id);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
public static boolean canSkipSingleChoice() {
|
|
return GeneralConfig.getInstance().getSkipSingle();
|
|
}
|
|
|
|
public static boolean canSkipSingleManaChoice() {
|
|
return GeneralConfig.getInstance().getSkipSingle();
|
|
}
|
|
|
|
//human is declaring blockers, skip if AI is not attacking
|
|
public boolean canSkipDeclareBlockersSingleChoice() {
|
|
return GeneralConfig.getInstance().getSkipSingle() && turnPlayer.getNrOfAttackers() == 0;
|
|
}
|
|
|
|
public boolean canAlwaysPass() {
|
|
if (GeneralConfig.get(BooleanSetting.ALWAYS_PASS)) {
|
|
return phase.getType() == MagicPhaseType.Draw ||
|
|
phase.getType() == MagicPhaseType.BeginOfCombat;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public boolean isArtificial() {
|
|
return artificial;
|
|
}
|
|
|
|
public boolean isReal() {
|
|
return !artificial;
|
|
}
|
|
|
|
public void setArtificial(final boolean art) {
|
|
artificial = art;
|
|
}
|
|
|
|
public boolean getFastMana() {
|
|
return fastMana;
|
|
}
|
|
|
|
public void setFastMana(final boolean v) {
|
|
fastMana = v;
|
|
}
|
|
|
|
public boolean getFastTarget() {
|
|
return fastTarget;
|
|
}
|
|
|
|
public void setFastTarget(final boolean v) {
|
|
fastTarget = v;
|
|
}
|
|
|
|
public boolean getFastBlocker() {
|
|
return fastBlocker;
|
|
}
|
|
|
|
public void setFastBlocker(final boolean v) {
|
|
fastBlocker = v;
|
|
}
|
|
|
|
public boolean getFastGameplay() {
|
|
return fastGameplay;
|
|
}
|
|
|
|
public void setFastGameplay(final boolean v) {
|
|
fastGameplay = v;
|
|
}
|
|
|
|
public void setFastChoices(final boolean v) {
|
|
fastMana = v;
|
|
fastTarget = v;
|
|
fastBlocker = v;
|
|
fastGameplay = v;
|
|
}
|
|
|
|
public boolean getHintTiming() {
|
|
return hintTiming;
|
|
}
|
|
|
|
public void setHintTiming(final boolean v) {
|
|
hintTiming = v;
|
|
}
|
|
|
|
public boolean getHintPriority() {
|
|
return hintPriority;
|
|
}
|
|
|
|
public void setHintPriority(final boolean v) {
|
|
hintPriority = v;
|
|
}
|
|
|
|
public boolean getHintTarget() {
|
|
return hintTarget;
|
|
}
|
|
|
|
public void setHintTarget(final boolean v) {
|
|
hintTarget = v;
|
|
}
|
|
|
|
public void setTurn(final int aTurn) {
|
|
turn = aTurn;
|
|
}
|
|
|
|
public int getTurn() {
|
|
return turn;
|
|
}
|
|
|
|
public void setMainPhases(final int count) {
|
|
startTurn=turn;
|
|
mainPhaseCount=count;
|
|
}
|
|
|
|
public int getRelativeTurn() {
|
|
return startTurn>0?turn-startTurn:0;
|
|
}
|
|
|
|
public void decreaseMainPhaseCount() {
|
|
mainPhaseCount--;
|
|
}
|
|
|
|
public void setMainPhaseCount(final int count) {
|
|
mainPhaseCount=count;
|
|
}
|
|
|
|
public int getMainPhaseCount() {
|
|
return mainPhaseCount;
|
|
}
|
|
|
|
public void setPhase(final MagicPhase aPhase) {
|
|
phase = aPhase;
|
|
}
|
|
|
|
public void nextPhase() {
|
|
changePhase(gameplay.getNextPhase(this));
|
|
}
|
|
|
|
private void changePhase(final MagicPhase aPhase) {
|
|
phase = aPhase;
|
|
step = MagicStep.Begin;
|
|
priorityPassedCount = 0;
|
|
for (MagicPlayer player : players) {
|
|
player.getActivationPriority().clear();
|
|
}
|
|
}
|
|
|
|
public MagicPhase getPhase() {
|
|
return phase;
|
|
}
|
|
|
|
public void executePhase() {
|
|
phase.executePhase(this);
|
|
}
|
|
|
|
public boolean isPhase(final MagicPhaseType type) {
|
|
return phase.getType()==type;
|
|
}
|
|
|
|
public boolean isMainPhase() {
|
|
return phase.getType().isMain();
|
|
}
|
|
|
|
public boolean isCombatPhase() {
|
|
return phase.getType().isCombat();
|
|
}
|
|
|
|
public void setStep(final MagicStep aStep) {
|
|
step = aStep;
|
|
}
|
|
|
|
public MagicStep getStep() {
|
|
return step;
|
|
}
|
|
|
|
public void resolve() {
|
|
if (stack.isEmpty()) {
|
|
step=MagicStep.NextPhase;
|
|
} else {
|
|
step=MagicStep.Resolve;
|
|
}
|
|
}
|
|
|
|
public void resetPayedCost() {
|
|
payedCost = new MagicPayedCost();
|
|
}
|
|
|
|
public void setPayedCost(final MagicPayedCost aPayedCost) {
|
|
payedCost = aPayedCost;
|
|
}
|
|
|
|
public MagicPayedCost getPayedCost() {
|
|
return new MagicPayedCost(payedCost);
|
|
}
|
|
|
|
/** Tells gameplay that it can skip certain parts during AI processing. */
|
|
public boolean canSkip() {
|
|
return stack.isEmpty() && artificial && fastGameplay;
|
|
}
|
|
|
|
public boolean isFinished() {
|
|
return losingPlayer.isValid() || mainPhaseCount <= 0;
|
|
}
|
|
|
|
public MagicLogBook getLogBook() {
|
|
return logBook;
|
|
}
|
|
|
|
public void hideHiddenCards() {
|
|
getOpponent(scorePlayer).setHandToUnknown();
|
|
for (final MagicPlayer player : players) {
|
|
player.getLibrary().setAIKnown(false);
|
|
}
|
|
}
|
|
|
|
public void showRandomizedHiddenCards() {
|
|
getOpponent(scorePlayer).showRandomizedHandAndLibrary();
|
|
scorePlayer.getLibrary().shuffle(MagicRandom.nextRNGInt());
|
|
scorePlayer.getLibrary().setAIKnown(true);
|
|
}
|
|
|
|
public Collection<MagicAction> getActions() {
|
|
return actions;
|
|
}
|
|
|
|
public int getNumActions() {
|
|
return actions.size();
|
|
}
|
|
|
|
public void addDelayedAction(final MagicAction action) {
|
|
delayedActions.add(action);
|
|
}
|
|
|
|
public void doAction(final MagicAction action) {
|
|
if (!action.isLegal(this)) {
|
|
return;
|
|
}
|
|
actions.add(action);
|
|
try {
|
|
action.doAction(this);
|
|
} catch (Throwable ex) {
|
|
throw new GameException(ex, this);
|
|
}
|
|
//performing actions update the score
|
|
score += action.getScore(scorePlayer);
|
|
}
|
|
|
|
public void update() {
|
|
doDelayedActions();
|
|
MagicPermanent.update(this);
|
|
|
|
triggers = new MagicPermanentTriggerMap(additionalTriggers);
|
|
|
|
// add Soulbond trigger
|
|
triggers.add(new MagicPermanentTrigger(0, MagicPermanent.NONE, OtherEntersBattlefieldTrigger.Soulbond));
|
|
|
|
// add Monarch triggers
|
|
triggers.add(new MagicPermanentTrigger(1, MagicPermanent.NONE, AtEndOfTurnTrigger.Monarch));
|
|
triggers.add(new MagicPermanentTrigger(2, MagicPermanent.NONE, DamageIsDealtTrigger.Monarch));
|
|
|
|
// add Suspend trigger
|
|
triggers.add(new MagicPermanentTrigger(3, MagicPermanent.NONE, AtUpkeepTrigger.Suspend));
|
|
|
|
// prevent damage replacement
|
|
triggers.add(new MagicPermanentTrigger(4, MagicPermanent.NONE, PreventDamageTrigger.ProtectionShield));
|
|
triggers.add(new MagicPermanentTrigger(Long.MAX_VALUE, MagicPermanent.NONE, PreventDamageTrigger.PreventDamageShield));
|
|
|
|
for (final MagicPlayer player : players) {
|
|
for (final MagicPermanent perm : player.getPermanents()) {
|
|
for (final MagicTrigger<?> trigger : perm.getTriggers()) {
|
|
triggers.add(new MagicPermanentTrigger(getUniqueId(), perm, trigger));
|
|
}}}
|
|
|
|
MagicPlayer.update(this);
|
|
MagicGame.update(this);
|
|
doDelayedActions();
|
|
}
|
|
|
|
public MagicManaCost modCost(final MagicCard card, final MagicManaCost cost) {
|
|
MagicManaCost currCost = cost;
|
|
for (final MagicPermanentStatic mps : getStatics(MagicLayer.CostIncrease)) {
|
|
currCost = mps.getStatic().increaseCost(mps.getPermanent(), card, currCost);
|
|
}
|
|
for (final MagicPermanentStatic mps : getStatics(MagicLayer.CostReduction)) {
|
|
currCost = mps.getStatic().reduceCost(mps.getPermanent(), card, currCost);
|
|
}
|
|
return currCost;
|
|
}
|
|
|
|
public void apply(final MagicLayer layer) {
|
|
switch (layer) {
|
|
case Game:
|
|
maxLands = 1;
|
|
break;
|
|
default:
|
|
throw new RuntimeException("No case for " + layer + " in MagicGame.apply");
|
|
}
|
|
}
|
|
|
|
private void apply(final MagicPermanent source, final MagicStatic mstatic) {
|
|
final MagicLayer layer = mstatic.getLayer();
|
|
switch (layer) {
|
|
case Game:
|
|
mstatic.modGame(source, this);
|
|
break;
|
|
default:
|
|
throw new RuntimeException("No case for " + layer + " in MagicGame.apply");
|
|
}
|
|
}
|
|
|
|
public static void update(final MagicGame game) {
|
|
game.apply(MagicLayer.Game);
|
|
for (final MagicPermanentStatic mpstatic : game.getStatics(MagicLayer.Game)) {
|
|
final MagicStatic mstatic = mpstatic.getStatic();
|
|
final MagicPermanent source = mpstatic.getPermanent();
|
|
if (mstatic.accept(game, source, source)) {
|
|
game.apply(source, mstatic);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void doDelayedActions() {
|
|
while (!delayedActions.isEmpty()) {
|
|
final MagicAction action = delayedActions.removeFirst();
|
|
doAction(action);
|
|
}
|
|
}
|
|
|
|
public void snapshot() {
|
|
final MagicAction markerAction=new MarkerAction();
|
|
doAction(markerAction);
|
|
if (!artificial) {
|
|
doAction(new LogMarkerAction());
|
|
undoPoints.addLast(markerAction);
|
|
}
|
|
}
|
|
|
|
public void restore() {
|
|
if (!artificial) {
|
|
undoPoints.removeLast();
|
|
}
|
|
//undo each action up to and including the first MagicMarkerAction
|
|
MagicAction action;
|
|
do {
|
|
action = actions.removeLast();
|
|
try {
|
|
action.undoAction(this);
|
|
} catch (Throwable ex) {
|
|
//put action back so that it shows up in report
|
|
actions.addLast(action);
|
|
throw new GameException(ex, this);
|
|
}
|
|
} while (!(action instanceof MarkerAction));
|
|
}
|
|
|
|
public void undoAllActions() {
|
|
assert actions.isEmpty() : "actions: " + actions;
|
|
}
|
|
|
|
public int getNrOfUndoPoints() {
|
|
return undoPoints.size();
|
|
}
|
|
|
|
public boolean hasUndoPoints() {
|
|
return !undoPoints.isEmpty();
|
|
}
|
|
|
|
public void clearUndoPoints() {
|
|
undoPoints.clear();
|
|
}
|
|
|
|
public void clearMessages() {
|
|
logMessageBuilder.clearMessages();
|
|
}
|
|
|
|
public void logMessages() {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
logMessageBuilder.logMessages();
|
|
}
|
|
|
|
private void logAppendEvent(final MagicEvent event,final Object[] choiceResults) {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
final String message=event.getDescription(choiceResults);
|
|
if (message.length() == 0) {
|
|
return;
|
|
}
|
|
logMessageBuilder.appendMessage(event.getPlayer(),message);
|
|
}
|
|
|
|
public void logAppendMessage(final MagicPlayer player,final String message) {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
logMessageBuilder.appendMessage(player,message);
|
|
}
|
|
|
|
public void logAppendValue(final MagicPlayer player, final int amount) {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
logMessageBuilder.appendMessage(player, "(" + amount + ")");
|
|
}
|
|
|
|
public void logAppendX(final MagicPlayer player, final int X) {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
logMessageBuilder.appendMessage(player, "(X="+X+")");
|
|
}
|
|
|
|
public void logMessage(final MagicPlayer player,final String message) {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
logBook.add(new MagicMessage(this,player,message));
|
|
}
|
|
|
|
public void logAttackers(final MagicPlayer player,final MagicDeclareAttackersResult result) {
|
|
if (disableLog || result.isEmpty()) {
|
|
return;
|
|
}
|
|
final SortedSet<String> names= new TreeSet<>();
|
|
for (final MagicPermanent attacker : result) {
|
|
names.add(MagicMessage.getCardToken(attacker));
|
|
}
|
|
final StringBuilder builder = new StringBuilder(player + " attacks with ");
|
|
MagicMessage.addNames(builder,names);
|
|
builder.append('.');
|
|
logBook.add(new MagicMessage(this,player,builder.toString()));
|
|
}
|
|
|
|
public void logBlockers(final MagicPlayer player, final MagicDeclareBlockersResult result) {
|
|
if (disableLog) {
|
|
return;
|
|
}
|
|
final SortedSet<String> names = new TreeSet<>();
|
|
for (final MagicCombatCreature[] creatures : result) {
|
|
for (int index = 1; index < creatures.length; index++) {
|
|
names.add(MagicMessage.getCardToken(creatures[index].permanent));
|
|
}
|
|
}
|
|
if (names.isEmpty()) {
|
|
return;
|
|
}
|
|
final StringBuilder builder = new StringBuilder(player + " blocks with ");
|
|
MagicMessage.addNames(builder, names);
|
|
builder.append('.');
|
|
logBook.add(new MagicMessage(this, player, builder.toString()));
|
|
}
|
|
|
|
public void executeEvent(final MagicEvent event,final Object[] choiceResults) {
|
|
if (choiceResults == null) {
|
|
throw new RuntimeException("choiceResults is null");
|
|
}
|
|
|
|
logAppendEvent(event,choiceResults);
|
|
|
|
// Payed cost
|
|
if (event.getManaChoiceResultIndex() >= 0) {
|
|
payedCost.set(choiceResults[event.getManaChoiceResultIndex()]);
|
|
}
|
|
|
|
// Target in cost
|
|
if (event.getTargetChoiceResultIndex() >= 0) {
|
|
payedCost.set(choiceResults[event.getTargetChoiceResultIndex()]);
|
|
}
|
|
|
|
activeSource = event.getSource();
|
|
event.executeEvent(this,choiceResults);
|
|
if (hasNextEvent() && getNextEvent().hasChoice()) {
|
|
update();
|
|
}
|
|
}
|
|
|
|
public MagicEventQueue getEvents() {
|
|
return events;
|
|
}
|
|
|
|
public boolean hasNextEvent() {
|
|
return !events.isEmpty();
|
|
}
|
|
|
|
public MagicEvent getNextEvent() {
|
|
return events.getFirst();
|
|
}
|
|
|
|
public boolean advanceToNextEventWithChoice() {
|
|
while (!isFinished()) {
|
|
if (!hasNextEvent()) {
|
|
executePhase();
|
|
} else if (!getNextEvent().hasChoice()) {
|
|
executeNextEvent();
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public List<Object[]> advanceToNextEventWithChoices() {
|
|
while (!isFinished()) {
|
|
if (!hasNextEvent()) {
|
|
executePhase();
|
|
} else if (!getNextEvent().hasChoice()) {
|
|
executeNextEvent();
|
|
} else {
|
|
final MagicEvent event = getNextEvent();
|
|
final List<Object[]> choices = event.getArtificialChoiceResults(this);
|
|
if (choices.size() == 1) {
|
|
executeNextEvent(choices.get(0));
|
|
} else {
|
|
return choices;
|
|
}
|
|
}
|
|
}
|
|
return Collections.emptyList();
|
|
}
|
|
|
|
public void addNextCostEvent(final MagicEvent event) {
|
|
event.setCost();
|
|
doAction(new AddFirstEventAction(event));
|
|
}
|
|
|
|
public void addCostEvent(final MagicEvent event) {
|
|
event.setCost();
|
|
doAction(new AddEventAction(event));
|
|
}
|
|
|
|
public void addEvent(final MagicEvent event) {
|
|
doAction(new AddEventAction(event));
|
|
}
|
|
|
|
public void addFirstEvent(final MagicEvent event) {
|
|
doAction(new AddFirstEventAction(event));
|
|
}
|
|
|
|
public void executeNextEvent(final Object[] choiceResults) {
|
|
doAction(new ExecuteFirstEventAction(choiceResults));
|
|
}
|
|
|
|
public void executeNextEvent() {
|
|
doAction(new ExecuteFirstEventAction(MagicEvent.NO_CHOICE_RESULTS));
|
|
}
|
|
|
|
public MagicDuel getDuel() {
|
|
return duel;
|
|
}
|
|
|
|
public void advanceDuel() {
|
|
duel.advance(losingPlayer != players[0], this);
|
|
}
|
|
|
|
public MagicPlayer[] getPlayers() {
|
|
return players;
|
|
}
|
|
|
|
/**
|
|
* Gets players ordered as Active Player then Non-Active Player.
|
|
* <p>
|
|
* @see <a href="http://www.slightlymagic.net/forum/viewtopic.php?f=115&p=155684">APNAP forum topic</a>
|
|
*/
|
|
public List<MagicPlayer> getAPNAP() {
|
|
return Arrays.asList(turnPlayer, turnPlayer.getOpponent());
|
|
}
|
|
|
|
public MagicPlayer getPlayer(final int index) {
|
|
return players[index];
|
|
}
|
|
|
|
MagicPlayer getOpponent(final MagicPlayer player) {
|
|
return players[1-player.getIndex()];
|
|
}
|
|
|
|
public void setTurnPlayer(final MagicPlayer aTurnPlayer) {
|
|
turnPlayer = aTurnPlayer;
|
|
}
|
|
|
|
public MagicPlayer getTurnPlayer() {
|
|
return turnPlayer;
|
|
}
|
|
|
|
public MagicPlayer getAttackingPlayer() {
|
|
return turnPlayer;
|
|
}
|
|
|
|
public MagicPlayer getDefendingPlayer() {
|
|
return turnPlayer.getOpponent();
|
|
}
|
|
|
|
public MagicPlayer getPriorityPlayer() {
|
|
return step == MagicStep.ActivePlayer ? turnPlayer : getOpponent(turnPlayer);
|
|
}
|
|
|
|
public MagicPlayer getScorePlayer() {
|
|
return scorePlayer;
|
|
}
|
|
|
|
public void setLosingPlayer(final MagicPlayer player) {
|
|
losingPlayer = player;
|
|
}
|
|
|
|
public MagicPlayer getLosingPlayer() {
|
|
return losingPlayer;
|
|
}
|
|
|
|
public MagicPlayer getWinner() {
|
|
return players[0] == losingPlayer ? players[1] : players[0];
|
|
}
|
|
|
|
public MagicSource getActiveSource() {
|
|
return activeSource;
|
|
}
|
|
|
|
public boolean hasTurn(final MagicPlayer player) {
|
|
return player == turnPlayer;
|
|
}
|
|
|
|
public int getNrOfPermanents(final MagicPermanentState state) {
|
|
return Arrays.stream(players)
|
|
.mapToInt(p -> p.getNrOfPermanents(state))
|
|
.sum();
|
|
}
|
|
|
|
public int getNrOfPermanents(final MagicType type) {
|
|
return Arrays.stream(players)
|
|
.mapToInt(p -> p.getNrOfPermanents(type))
|
|
.sum();
|
|
}
|
|
|
|
public int getNrOfPermanents(final MagicSubType subType) {
|
|
return Arrays.stream(players)
|
|
.mapToInt(p -> p.getNrOfPermanents(subType))
|
|
.sum();
|
|
}
|
|
|
|
public int getNrOfPermanents(final MagicColor color) {
|
|
return Arrays.stream(players)
|
|
.mapToInt(p -> p.getNrOfPermanents(color))
|
|
.sum();
|
|
}
|
|
|
|
public int getNrOfPermanents(final MagicTargetFilter<MagicPermanent> filter) {
|
|
return getNrOfPermanents(MagicSource.NONE, filter);
|
|
}
|
|
|
|
public int getNrOfPermanents(final MagicSource source, final MagicTargetFilter<MagicPermanent> filter) {
|
|
return Arrays.stream(players)
|
|
.mapToInt(p -> p.getNrOfPermanents(source, filter))
|
|
.sum();
|
|
}
|
|
|
|
public boolean canPlaySorcery(final MagicPlayer controller) {
|
|
return phase.getType().isMain() &&
|
|
stack.isEmpty() &&
|
|
turnPlayer == controller;
|
|
}
|
|
|
|
public boolean canPlayLand(final MagicPlayer controller) {
|
|
return landsPlayed < maxLands && canPlaySorcery(controller);
|
|
}
|
|
|
|
public int getLandsPlayed() {
|
|
return landsPlayed;
|
|
}
|
|
|
|
public void incLandsPlayed() {
|
|
landsPlayed++;
|
|
}
|
|
|
|
public void resetLandsPlayed() {
|
|
landsPlayed = 0;
|
|
}
|
|
|
|
public void setLandsPlayed(final int lp) {
|
|
landsPlayed = lp;
|
|
}
|
|
|
|
public void incMaxLands() {
|
|
maxLands++;
|
|
}
|
|
|
|
public void resetMaxLands() {
|
|
maxLands = 1;
|
|
}
|
|
|
|
public int getSpellsCast() {
|
|
int spellCount = 0;
|
|
for (final MagicPlayer player : players) {
|
|
spellCount += player.getSpellsCast();
|
|
}
|
|
return spellCount;
|
|
}
|
|
|
|
public int getSpellsCastLastTurn() {
|
|
int spellCount = 0;
|
|
for (final MagicPlayer player : players) {
|
|
spellCount += player.getSpellsCastLastTurn();
|
|
}
|
|
return spellCount;
|
|
}
|
|
|
|
public void incSpellsCast(final MagicPlayer player) {
|
|
player.incSpellsCast();
|
|
}
|
|
|
|
public boolean getCreatureDiedThisTurn() {
|
|
return creatureDiedThisTurn;
|
|
}
|
|
|
|
public void setCreatureDiedThisTurn(final boolean died) {
|
|
creatureDiedThisTurn = died;
|
|
}
|
|
|
|
public MagicStack getStack() {
|
|
return stack;
|
|
}
|
|
|
|
public MagicStack getPendingTriggers() {
|
|
return pendingStack;
|
|
}
|
|
|
|
public boolean hasItem(final MagicSource source, final String desc) {
|
|
return stack.hasItem(source, desc) || pendingStack.hasItem(source, desc);
|
|
}
|
|
|
|
public void setPriorityPassed(final boolean passed) {
|
|
priorityPassed=passed;
|
|
}
|
|
|
|
public boolean getPriorityPassed() {
|
|
return priorityPassed;
|
|
}
|
|
|
|
public void incrementPriorityPassedCount() {
|
|
priorityPassedCount++;
|
|
}
|
|
|
|
public void setPriorityPassedCount(final int count) {
|
|
priorityPassedCount=count;
|
|
}
|
|
|
|
public int getPriorityPassedCount() {
|
|
return priorityPassedCount;
|
|
}
|
|
|
|
public MagicSource createDelayedSource(final MagicCardDefinition cdef, final MagicPlayer controller) {
|
|
return new MagicCard(cdef, controller.map(this), getUniqueId());
|
|
}
|
|
|
|
public MagicSource createDelayedSource(final MagicObject obj, final MagicPlayer controller) {
|
|
return new MagicCard(obj.getCardDefinition(), controller.map(this), getUniqueId());
|
|
}
|
|
|
|
public MagicPermanent createPermanent(final MagicCard card,final MagicPlayer controller) {
|
|
return new MagicPermanent(getUniqueId(),card,controller);
|
|
}
|
|
|
|
public MagicPermanent createPermanent(final MagicCard card, final MagicCardDefinition cardDef, final MagicPlayer controller) {
|
|
return new MagicPermanent(getUniqueId(),card,cardDef,controller);
|
|
}
|
|
|
|
public MagicCardList getExiledUntilEndOfTurn() {
|
|
return exiledUntilEndOfTurn;
|
|
}
|
|
|
|
public void setStateCheckRequired(final boolean required) {
|
|
stateCheckRequired = required;
|
|
}
|
|
|
|
public void setStateCheckRequired() {
|
|
stateCheckRequired = true;
|
|
}
|
|
|
|
public boolean getStateCheckRequired() {
|
|
return stateCheckRequired;
|
|
}
|
|
|
|
public void checkStatePutTriggers() {
|
|
while (stateCheckRequired) {
|
|
stateCheckRequired = false;
|
|
|
|
// Check if a player has lost
|
|
for (final MagicPlayer player : getAPNAP()) {
|
|
player.generateStateBasedActions();
|
|
}
|
|
|
|
// Check permanents' state
|
|
for (final MagicPlayer player : getAPNAP()) {
|
|
for (final MagicPermanent permanent : player.getPermanents()) {
|
|
permanent.generateStateBasedActions();
|
|
}}
|
|
|
|
update();
|
|
// some action may set stateCheckRequired to true, if so loop again
|
|
}
|
|
|
|
// update log with messages from state-based actions
|
|
logMessages();
|
|
|
|
// put pending triggers on stack in APNAP order
|
|
pendingStack.sortAPNAP(getTurnPlayer());
|
|
while (!pendingStack.isEmpty()) {
|
|
doAction(new PutItemOnStackAction(pendingStack.peek()));
|
|
doAction(new DequeueTriggerAction());
|
|
}
|
|
}
|
|
|
|
public void checkUniquenessRule(final MagicPermanent permanent) {
|
|
// 704.5k "legend rule"
|
|
if (permanent.hasType(MagicType.Legendary)) {
|
|
final MagicTargetFilter<MagicPermanent> targetFilter=new MagicLegendaryCopiesFilter(permanent.getName());
|
|
final Collection<MagicPermanent> targets=targetFilter.filter(permanent.getController());
|
|
if (targets.size() > 1) {
|
|
addEvent(new MagicUniquenessEvent(permanent, targetFilter));
|
|
}
|
|
}
|
|
|
|
// 704.5m "world rule"
|
|
if (permanent.hasType(MagicType.World)) {
|
|
final MagicTargetFilter<MagicPermanent> targetFilter = new MagicOtherPermanentTargetFilter(MagicTargetFilterFactory.WORLD, permanent);
|
|
final Collection<MagicPermanent> targets = targetFilter.filter(permanent.getController());
|
|
for (final MagicPermanent world : targets) {
|
|
logAppendMessage(
|
|
world.getController(),
|
|
MagicMessage.format("%s is put into its owner's graveyard.", world)
|
|
);
|
|
doAction(new RemoveFromPlayAction(
|
|
world,
|
|
MagicLocationType.Graveyard
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
public Object[] map(final Object[] data) {
|
|
final int length=data.length;
|
|
final Object[] mappedData=new Object[length];
|
|
for (int index=0;index<length;index++) {
|
|
final Object obj=data[index];
|
|
if (obj != null && obj instanceof MagicMappable) {
|
|
mappedData[index]=((MagicMappable)obj).map(this);
|
|
} else {
|
|
assert obj == null ||
|
|
obj instanceof Enum ||
|
|
obj instanceof Number ||
|
|
obj instanceof String :
|
|
obj.getClass().getName() + " not mapped";
|
|
mappedData[index]=obj;
|
|
}
|
|
}
|
|
return mappedData;
|
|
}
|
|
|
|
// ***** TARGETTING *****
|
|
|
|
public boolean hasLegalTargets(final MagicPlayer player, final MagicSource source, final MagicTargetChoice targetChoice, final boolean hints) {
|
|
|
|
if (targetChoice == MagicTargetChoice.NONE) {
|
|
return true;
|
|
}
|
|
|
|
final Collection<? extends MagicTarget> targets = targetChoice.getTargetFilter().filter(
|
|
source,
|
|
player,
|
|
targetChoice.getTargetHint(hints)
|
|
);
|
|
|
|
if (!targetChoice.isTargeted()) {
|
|
return !targets.isEmpty();
|
|
}
|
|
|
|
for (final MagicTarget target : targets) {
|
|
if (target.isValidTarget(source)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public List<MagicTarget> getLegalTargets(final MagicPlayer player, final MagicSource source, final MagicTargetChoice targetChoice, final MagicTargetHint targetHint) {
|
|
|
|
final Collection<? extends MagicTarget> targets = targetChoice.getTargetFilter().filter(
|
|
source,
|
|
player,
|
|
targetHint
|
|
);
|
|
|
|
final List<MagicTarget> options;
|
|
if (targetChoice.isTargeted()) {
|
|
options= new ArrayList<>();
|
|
for (final MagicTarget target : targets) {
|
|
if (target.isValidTarget(source)) {
|
|
options.add(target);
|
|
}
|
|
}
|
|
} else {
|
|
options= new ArrayList<>(targets);
|
|
}
|
|
|
|
if (options.isEmpty()) {
|
|
// Try again without using hints
|
|
if (targetHint != MagicTargetHint.None) {
|
|
return getLegalTargets(player, source, targetChoice, MagicTargetHint.None);
|
|
// Add none when there are no legal targets. Only for triggers.
|
|
} else {
|
|
options.add(MagicTargetNone.getInstance());
|
|
}
|
|
}
|
|
return options;
|
|
}
|
|
|
|
public <T extends MagicTarget> boolean isLegalTarget(final MagicPlayer player, final MagicSource source, final MagicTargetChoice targetChoice, final T target) {
|
|
|
|
@SuppressWarnings("unchecked")
|
|
MagicTargetFilter<T> targetFilter = (MagicTargetFilter<T>)targetChoice.getTargetFilter();
|
|
|
|
if (target==null ||
|
|
target==MagicTargetNone.getInstance() ||
|
|
!targetFilter.accept(source,player,target)) {
|
|
return false;
|
|
}
|
|
|
|
if (target.isLegalTarget(player, targetFilter)) {
|
|
return !targetChoice.isTargeted() || target.isValidTarget(source);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ***** STATICS *****
|
|
|
|
public void addStatics(final MagicPermanent permanent) {
|
|
for (final MagicStatic mstatic : permanent.getStatics()) {
|
|
addStatic(permanent, mstatic);
|
|
}
|
|
}
|
|
|
|
public void addStatics(final MagicPermanent permanent, final Collection<MagicStatic> mstatics) {
|
|
for (final MagicStatic mstatic : mstatics) {
|
|
addStatic(permanent, mstatic);
|
|
}
|
|
}
|
|
|
|
public Collection<MagicPermanentStatic> removeSelfStatics(final MagicPermanent permanent) {
|
|
return statics.remove(permanent, permanent.getStatics());
|
|
}
|
|
|
|
public Collection<MagicPermanentStatic> removeAllStatics(final MagicPermanent permanent) {
|
|
return statics.remove(permanent);
|
|
}
|
|
|
|
public MagicPermanentStatic addStatic(final MagicPermanent permanent, final MagicStatic mstatic) {
|
|
return addStatic(new MagicPermanentStatic(getUniqueId(),permanent,mstatic));
|
|
}
|
|
|
|
public MagicPermanentStatic addStatic(final MagicPermanentStatic permanentStatic) {
|
|
statics.add(permanentStatic);
|
|
return permanentStatic;
|
|
}
|
|
|
|
public void addStatics(final Collection<MagicPermanentStatic> aStatics) {
|
|
for (final MagicPermanentStatic mpstatic : aStatics) {
|
|
addStatic(mpstatic);
|
|
}
|
|
}
|
|
|
|
public Collection<MagicPermanentStatic> getStatics(final MagicLayer layer) {
|
|
return statics.get(layer);
|
|
}
|
|
|
|
public Collection<MagicPermanentStatic> removeTurnStatics() {
|
|
return statics.removeTurn();
|
|
}
|
|
|
|
public void removeStatic(final MagicPermanent permanent,final MagicStatic mstatic) {
|
|
statics.remove(permanent, mstatic);
|
|
}
|
|
|
|
public void removeStatics(final MagicPermanent permanent,final Collection<MagicStatic> mstatics) {
|
|
statics.remove(permanent, mstatics);
|
|
}
|
|
|
|
|
|
// ***** TRIGGERS *****
|
|
|
|
/** Executes triggers immediately when they have no choices, otherwise ignore them. */
|
|
public void setImmediate(final boolean aImmediate) {
|
|
immediate = aImmediate;
|
|
}
|
|
|
|
public boolean isImmediate() {
|
|
return immediate;
|
|
}
|
|
|
|
public MagicPermanentTrigger addTrigger(final MagicPermanentTrigger permanentTrigger) {
|
|
additionalTriggers.add(permanentTrigger);
|
|
return permanentTrigger;
|
|
}
|
|
|
|
public void addTriggers(final List<MagicPermanentTrigger> triggersList) {
|
|
for (final MagicPermanentTrigger permanentTrigger : triggersList) {
|
|
addTrigger(permanentTrigger);
|
|
}
|
|
}
|
|
|
|
public MagicPermanentTrigger addTrigger(final MagicPermanent permanent, final MagicTrigger<?> trigger) {
|
|
return addTrigger(new MagicPermanentTrigger(getUniqueId(),permanent,trigger, false));
|
|
}
|
|
|
|
public MagicPermanentTrigger addTurnTrigger(final MagicPermanent permanent, final MagicTrigger<?> trigger) {
|
|
return addTrigger(new MagicPermanentTrigger(getUniqueId(),permanent,trigger, true));
|
|
}
|
|
|
|
public void removeTrigger(final MagicPermanentTrigger permanentTrigger) {
|
|
additionalTriggers.remove(permanentTrigger);
|
|
}
|
|
|
|
public MagicPermanentTrigger removeTrigger(final MagicPermanent permanent, MagicTrigger<?> trigger) {
|
|
return additionalTriggers.remove(permanent, trigger);
|
|
}
|
|
|
|
public Collection<MagicPermanentTrigger> removeTriggers(final MagicPermanent permanent) {
|
|
return additionalTriggers.remove(permanent);
|
|
}
|
|
|
|
public List<MagicPermanentTrigger> removeTurnTriggers() {
|
|
return additionalTriggers.removeTurn();
|
|
}
|
|
|
|
public <T> void executeTrigger(final MagicTrigger<T> trigger, final MagicPermanent permanent, final MagicSource source, final T data) {
|
|
|
|
if (!trigger.accept(permanent, data)) {
|
|
return;
|
|
}
|
|
|
|
final MagicEvent event=trigger.executeTrigger(this,permanent,data);
|
|
|
|
if (!event.isValid()) {
|
|
return;
|
|
}
|
|
|
|
if (immediate) {
|
|
if (event.hasChoice()) {
|
|
// ignore
|
|
} else if (trigger.usesStack()) {
|
|
doAction(new EnqueueTriggerAction(event));
|
|
} else {
|
|
executeEvent(event, MagicEvent.NO_CHOICE_RESULTS);
|
|
}
|
|
} else if (trigger.usesStack()) {
|
|
doAction(new EnqueueTriggerAction(event));
|
|
} else {
|
|
addEvent(event);
|
|
}
|
|
}
|
|
|
|
public <T> void executeTrigger(final MagicTriggerType type,final T data) {
|
|
final Collection<MagicPermanentTrigger> typeTriggers=triggers.get(type);
|
|
if (typeTriggers.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
final Collection<MagicPermanentTrigger> copiedTriggers= new ArrayList<>(typeTriggers);
|
|
for (final MagicPermanentTrigger permanentTrigger : copiedTriggers) {
|
|
final MagicPermanent permanent = permanentTrigger.getPermanent();
|
|
@SuppressWarnings("unchecked")
|
|
final MagicTrigger<T> trigger = (MagicTrigger<T>)permanentTrigger.getTrigger();
|
|
executeTrigger(trigger,permanent,permanent,data);
|
|
}
|
|
}
|
|
|
|
public void setConceded(final boolean b) {
|
|
isConceded = true;
|
|
}
|
|
public boolean isConceded() {
|
|
return isConceded;
|
|
}
|
|
|
|
public void playSound(MagicSound aSound) {
|
|
if (isReal()) {
|
|
aSound.play();
|
|
}
|
|
}
|
|
|
|
public long getStartTimeMilli() {
|
|
return startTimeMilli;
|
|
}
|
|
}
|