PhaseHandler cleanup, solving problems with delayed triggers already added to simultaneous stack but not yet ordered

This commit is contained in:
Maxmtg
2013-05-29 09:29:50 +00:00
parent 4d34b23a02
commit 01c1c41e43
10 changed files with 277 additions and 363 deletions

View File

@@ -5,7 +5,6 @@ import forge.Card;
import forge.card.ability.SpellAbilityEffect; import forge.card.ability.SpellAbilityEffect;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState; import forge.game.GameState;
import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
public class EndTurnEffect extends SpellAbilityEffect { public class EndTurnEffect extends SpellAbilityEffect {
@@ -39,7 +38,7 @@ public class EndTurnEffect extends SpellAbilityEffect {
// 4) The current phase and/or step ends. The game skips straight to the // 4) The current phase and/or step ends. The game skips straight to the
// cleanup step. The cleanup step happens in its entirety. // cleanup step. The cleanup step happens in its entirety.
game.getPhaseHandler().setPhaseState(PhaseType.CLEANUP); game.getPhaseHandler().endTurnByEffect();
// Update observers // Update observers
game.getStack().updateObservers(); game.getStack().updateObservers();

View File

@@ -92,7 +92,8 @@ public class RestartGameEffect extends SpellAbilityEffect {
trigHandler.clearSuppression(TriggerType.ChangesZone); trigHandler.clearSuppression(TriggerType.ChangesZone);
game.setAge(GameAge.RestartedByKarn); game.setAge(GameAge.RestartedByKarn);
game.getPhaseHandler().setPlayerTurn(sa.getActivatingPlayer()); // Do not need this because ability will resolve only during that player's turn
//game.getPhaseHandler().setPlayerTurn(sa.getActivatingPlayer());
// Set turn number? // Set turn number?

View File

@@ -282,4 +282,9 @@ public class SpellAbilityStackInstance {
return true; return true;
} }
@Override
public String toString() {
return String.format("%s->%s", getSourceCard(), stackDescription);
}
} }

View File

@@ -96,8 +96,6 @@ public class InputAttack extends InputSyncronizedBase {
protected final void onOk() { protected final void onOk() {
// Propaganda costs could have been paid here. // Propaganda costs could have been paid here.
setCurrentDefender(null); // remove highlights setCurrentDefender(null); // remove highlights
game.getPhaseHandler().setCombat(!game.getCombat().getAttackers().isEmpty());
stop(); stop();
} }

View File

@@ -1065,6 +1065,7 @@ public class GameAction {
if (game.getTriggerHandler().runWaitingTriggers()) { if (game.getTriggerHandler().runWaitingTriggers()) {
checkAgain = true; checkAgain = true;
// Place triggers on stack // Place triggers on stack
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
} }
if (this.handleLegendRule()) { if (this.handleLegendRule()) {
@@ -1085,10 +1086,6 @@ public class GameAction {
// Clear Simultaneous triggers at the end of the game // Clear Simultaneous triggers at the end of the game
game.setGameOver(endGame); game.setGameOver(endGame);
game.getStack().clearSimultaneousStack(); game.getStack().clearSimultaneousStack();
if (!refreeze) {
game.getStack().unfreezeStack();
}
return;
} }
if (!refreeze) { if (!refreeze) {

View File

@@ -845,11 +845,7 @@ public class AiController {
// 12/2/10(sol) the decision making here has moved to getAttackers() // 12/2/10(sol) the decision making here has moved to getAttackers()
game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers()); game.setCombat(new AiAttackController(player, player.getOpponent()).getAttackers());
final List<Card> att = game.getCombat().getAttackers(); for (final Card element : game.getCombat().getAttackers()) {
game.getPhaseHandler().setCombat(!att.isEmpty());
for (final Card element : att) {
// tapping of attackers happens after Propaganda is paid for // tapping of attackers happens after Propaganda is paid for
final StringBuilder sb = new StringBuilder(); final StringBuilder sb = new StringBuilder();
sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker."); sb.append("Computer just assigned ").append(element.getName()).append(" as an attacker.");

View File

@@ -21,12 +21,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang.time.StopWatch; import org.apache.commons.lang.time.StopWatch;
import com.esotericsoftware.minlog.Log;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.FThreads; import forge.FThreads;
@@ -82,11 +78,11 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
private Player pPlayerPriority = null; private Player pPlayerPriority = null;
private Player pFirstPriority = null; private Player pFirstPriority = null;
private AtomicBoolean bCombat = new AtomicBoolean(false); private boolean bCombat = false;
private boolean bRepeatCleanup = false; private boolean bRepeatCleanup = false;
/** The need to next phase. */ /** The need to next phase. */
private boolean isPlayerPriorityAllowed = false; private boolean givePriorityToPlayer = false;
private final transient GameState game; private final transient GameState game;
@@ -95,28 +91,12 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
game = game0; game = game0;
} }
/**
* <p>
* isPlayerTurn.
* </p>
*
* @param player
* a {@link forge.game.player.Player} object.
* @return a boolean.
*/
public final boolean isPlayerTurn(final Player player) { public final boolean isPlayerTurn(final Player player) {
return player.equals(this.playerTurn); return player.equals(this.playerTurn);
} }
/** private final void setPlayerTurn(final Player s) {
* <p>
* Setter for the field <code>playerTurn</code>.
* </p>
*
* @param s
* a {@link forge.game.player.Player} object.
*/
public final void setPlayerTurn(final Player s) {
this.playerTurn = s; this.playerTurn = s;
this.setPriority(s); this.setPriority(s);
} }
@@ -166,9 +146,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
* a {@link forge.game.player.Player} object. * a {@link forge.game.player.Player} object.
*/ */
public final void setPriority(final Player p) { public final void setPriority(final Player p) {
if (game.getStack() != null) { game.getStack().chooseOrderOfSimultaneousStackEntryAll();
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
this.pFirstPriority = p; this.pFirstPriority = p;
this.pPlayerPriority = p; this.pPlayerPriority = p;
@@ -191,19 +169,7 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
* @return a boolean. * @return a boolean.
*/ */
public final boolean inCombat() { public final boolean inCombat() {
return this.bCombat.get(); return this.bCombat;
}
/**
* <p>
* setCombat.
* </p>
*
* @param b
* a boolean.
*/
public final void setCombat(boolean value) {
this.bCombat.set(value);
} }
/** /**
@@ -215,18 +181,42 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
this.bRepeatCleanup = true; this.bRepeatCleanup = true;
} }
/** private void advanceToNextPhase() {
* <p> PhaseType oldPhase = phase;
* nextPhase.
* </p> if (this.bRepeatCleanup) { // for when Cleanup needs to repeat itself
*/ this.bRepeatCleanup = false;
private final void nextPhase() { } else {
this.setPlayersPriorityPermission(true); // PlayerPriorityAllowed = false; // If the phase that's ending has a stack of additional phases
// Take the LIFO one and move to that instead of the normal one
if (this.extraPhases.containsKey(phase)) {
PhaseType nextPhase = this.extraPhases.get(phase).pop();
// If no more additional phases are available, remove it from the map
// and let the next add, reput the key
if (this.extraPhases.get(phase).isEmpty()) {
this.extraPhases.remove(phase);
}
this.phase = nextPhase;
} else {
this.phase = PhaseType.getNext(phase);
}
}
String phaseType = oldPhase == phase ? "Repeat" : phase == PhaseType.getNext(oldPhase) ? "" : "Additional";
if (this.phase == PhaseType.UNTAP) {
this.turn++;
game.getGameLog().add(GameEventType.TURN, "Turn " + this.turn + " (" + this.getPlayerTurn() + ")");
}
game.getEvents().post(new PhaseEvent(this.getPlayerTurn(), this.getPhase(), phaseType));
}
private void onPhaseEnd() {
// If the Stack isn't empty why is nextPhase being called? // If the Stack isn't empty why is nextPhase being called?
if (!game.getStack().isEmpty()) { if (!game.getStack().isEmpty()) {
Log.debug("Phase.nextPhase() is called, but Stack isn't empty."); throw new IllegalStateException("Phase.nextPhase() is called, but Stack isn't empty.");
return;
} }
for (Player p : game.getPlayers()) { for (Player p : game.getPlayers()) {
@@ -239,253 +229,211 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
} }
p.updateObservers(); p.updateObservers();
} }
switch (this.phase) {
case UNTAP:
this.nCombatsThisTurn = 0;
break;
if( phase != null ) { case COMBAT_DECLARE_ATTACKERS:
switch (this.phase) { this.bCombat = !game.getCombat().getAttackers().isEmpty();
case UNTAP: game.getStack().unfreezeStack();
this.nCombatsThisTurn = 0; this.nCombatsThisTurn++;
break; break;
case COMBAT_DECLARE_ATTACKERS:
game.getStack().unfreezeStack();
this.nCombatsThisTurn++;
break;
case COMBAT_DECLARE_BLOCKERS:
game.getStack().unfreezeStack();
break;
case COMBAT_END:
game.getCombat().reset(playerTurn);
this.getPlayerTurn().resetAttackedThisCombat();
this.bCombat.set(false);
break;
case CLEANUP:
this.bPreventCombatDamageThisTurn = false;
if (!this.bRepeatCleanup) {
this.setPlayerTurn(this.handleNextTurn());
}
this.planarDiceRolledthisTurn = 0;
// Play the End Turn sound
game.getEvents().post(new EndOfTurnEvent());
break;
default: // no action
}
}
String phaseType = ""; case COMBAT_DECLARE_BLOCKERS:
if (this.bRepeatCleanup) { // for when Cleanup needs to repeat itself game.getStack().unfreezeStack();
this.bRepeatCleanup = false; break;
phaseType = "Repeat ";
} else { case COMBAT_END:
// If the phase that's ending has a stack of additional phases game.getCombat().reset(playerTurn);
// Take the LIFO one and move to that instead of the normal one this.getPlayerTurn().resetAttackedThisCombat();
if (this.extraPhases.containsKey(phase)) { this.bCombat = false;
PhaseType nextPhase = this.extraPhases.get(phase).pop(); break;
// If no more additional phases are available, remove it from the map
// and let the next add, reput the key case CLEANUP:
if (this.extraPhases.get(phase).isEmpty()) { this.bPreventCombatDamageThisTurn = false;
this.extraPhases.remove(phase); if (!this.bRepeatCleanup) {
this.setPlayerTurn(this.handleNextTurn());
} }
this.phase = nextPhase; this.planarDiceRolledthisTurn = 0;
phaseType = "Additional "; // Play the End Turn sound
} else { game.getEvents().post(new EndOfTurnEvent());
this.phase = PhaseType.getNext(phase); break;
} default: // no action
}
// **** Anything BELOW Here is actually in the next phase. Maybe move
// this to handleBeginPhase
if (this.phase == PhaseType.UNTAP) {
this.turn++;
game.getGameLog().add(GameEventType.TURN, "Turn " + this.turn + " (" + this.getPlayerTurn() + ")");
} }
game.getEvents().post(new PhaseEvent(this.getPlayerTurn(), this.getPhase(), phaseType));
} }
private final void handleBeginPhase() { private boolean isSkippingPhase(PhaseType phase) {
if (null == playerTurn) { switch(phase) {
return; case UPKEEP:
return getPlayerTurn().hasKeyword("Skip your upkeep step.");
case DRAW:
return getPlayerTurn().isSkippingDraw() || getTurn() == 1 && game.getPlayers().size() == 2;
case COMBAT_BEGIN:
case COMBAT_DECLARE_ATTACKERS:
return playerTurn.isSkippingCombat();
case COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY:
case COMBAT_DECLARE_BLOCKERS:
case COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY:
case COMBAT_FIRST_STRIKE_DAMAGE:
case COMBAT_DAMAGE:
return !this.inCombat();
default:
return false;
} }
// Handle effects that happen at the beginning of phases }
game.getAction().checkStateEffects();
private final void onPhaseBegin() {
switch(this.getPhase()) { if ( isSkippingPhase(phase) ) {
case UNTAP: givePriorityToPlayer = false;
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc()); if( phase == PhaseType.COMBAT_DECLARE_ATTACKERS )
game.getPhaseHandler().setPlayersPriorityPermission(false); playerTurn.removeKeyword("Skip your next combat phase.");
} else {
game.getCombat().reset(playerTurn); // Perform turn-based actions
switch(this.getPhase()) {
// Tokens starting game in play should suffer from Sum. Sickness case UNTAP:
final List<Card> list = playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield); //SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
for (final Card c : list) { givePriorityToPlayer = false;
if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) {
c.setSickness(false); game.getCombat().reset(playerTurn);
// Tokens starting game in play should suffer from Sum. Sickness
final List<Card> list = playerTurn.getCardsIncludePhasingIn(ZoneType.Battlefield);
for (final Card c : list) {
if (playerTurn.getTurn() > 0 || !c.isStartsGameInPlay()) {
c.setSickness(false);
}
} }
} playerTurn.incrementTurn();
playerTurn.incrementTurn();
game.getAction().resetActivationsPerTurn();
game.getAction().resetActivationsPerTurn();
final List<Card> lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED);
final List<Card> lands = CardLists.filter(playerTurn.getLandsInPlay(), Presets.UNTAPPED); playerTurn.setNumPowerSurgeLands(lands.size());
playerTurn.setNumPowerSurgeLands(lands.size());
// anything before this point happens regardless of whether the Untap
// anything before this point happens regardless of whether the Untap // phase is skipped
// phase is skipped
if (!PhaseUtil.isSkipUntap(playerTurn)) {
if (!PhaseUtil.isSkipUntap(playerTurn)) { game.getUntap().executeUntil(playerTurn);
game.getUntap().executeUntil(playerTurn); game.getUntap().executeAt();
game.getUntap().executeAt(); }
}
break;
break;
case UPKEEP:
case UPKEEP:
if (this.getPlayerTurn().hasKeyword("Skip your upkeep step.")) {
this.setPlayersPriorityPermission(false);
} else {
this.nUpkeepsThisTurn++; this.nUpkeepsThisTurn++;
game.getUpkeep().executeUntil(this.getPlayerTurn()); game.getUpkeep().executeUntil(this.getPlayerTurn());
game.getUpkeep().executeAt(); game.getUpkeep().executeAt();
} break;
break;
case DRAW:
case DRAW:
if (getTurn() == 1 || this.getPlayerTurn().isSkippingDraw()) {
this.setPlayersPriorityPermission(false);
} else {
this.getPlayerTurn().drawCard(); this.getPlayerTurn().drawCard();
} break;
break;
case MAIN1:
case MAIN1: if (this.getPlayerTurn().isArchenemy()) {
if (this.getPlayerTurn().isArchenemy()) { this.getPlayerTurn().setSchemeInMotion();
this.getPlayerTurn().setSchemeInMotion(); }
} break;
break;
case COMBAT_BEGIN:
case COMBAT_BEGIN: //PhaseUtil.verifyCombat();
//PhaseUtil.verifyCombat(); break;
if (playerTurn.isSkippingCombat()) {
this.setPlayersPriorityPermission(false); case COMBAT_DECLARE_ATTACKERS:
} break;
break;
case COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY:
case COMBAT_DECLARE_ATTACKERS:
if (playerTurn.isSkippingCombat()) {
this.setPlayersPriorityPermission(false);
playerTurn.removeKeyword("Skip your next combat phase.");
} /* else game.getInputQueue().setInput(playerTurn.getController().getAttackInput());*/
break;
case COMBAT_DECLARE_ATTACKERS_INSTANT_ABILITY:
if (this.inCombat()) {
PhaseUtil.handleDeclareAttackers(game); PhaseUtil.handleDeclareAttackers(game);
CombatUtil.showCombat(game); break;
} else {
this.setPlayersPriorityPermission(false); case COMBAT_DECLARE_BLOCKERS:
}
break;
// we can skip AfterBlockers and AfterAttackers if necessary
case COMBAT_DECLARE_BLOCKERS:
if (this.inCombat()) {
game.getCombat().verifyCreaturesInPlay(); game.getCombat().verifyCreaturesInPlay();
CombatUtil.showCombat(game); break;
} else {
this.setPlayersPriorityPermission(false); case COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY:
}
break;
case COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY:
// After declare blockers are finished being declared mark them
// blocked and trigger blocking things
if (this.inCombat()) {
PhaseUtil.handleDeclareBlockers(game); PhaseUtil.handleDeclareBlockers(game);
CombatUtil.showCombat(game); break;
} else {
this.setPlayersPriorityPermission(false); case COMBAT_FIRST_STRIKE_DAMAGE:
}
break;
case COMBAT_FIRST_STRIKE_DAMAGE:
if (!this.inCombat()) {
this.setPlayersPriorityPermission(false);
} else {
game.getCombat().verifyCreaturesInPlay(); game.getCombat().verifyCreaturesInPlay();
// no first strikers, skip this step // no first strikers, skip this step
if (!game.getCombat().assignCombatDamage(true)) { if (!game.getCombat().assignCombatDamage(true)) {
this.setPlayersPriorityPermission(false); this.givePriorityToPlayer = false;
} else { } else {
game.getCombat().dealAssignedDamage(); game.getCombat().dealAssignedDamage();
game.getAction().checkStateEffects(); game.getAction().checkStateEffects();
CombatUtil.showCombat(game);
} }
} break;
break;
case COMBAT_DAMAGE:
case COMBAT_DAMAGE:
if (!this.inCombat()) {
this.setPlayersPriorityPermission(false);
} else {
game.getCombat().verifyCreaturesInPlay(); game.getCombat().verifyCreaturesInPlay();
if (!game.getCombat().assignCombatDamage(false)) { if (!game.getCombat().assignCombatDamage(false)) {
this.setPlayersPriorityPermission(false); this.givePriorityToPlayer = false;
} else { } else {
game.getCombat().dealAssignedDamage(); game.getCombat().dealAssignedDamage();
game.getAction().checkStateEffects(); game.getAction().checkStateEffects();
CombatUtil.showCombat(game);
} }
} break;
break;
case COMBAT_END:
// End Combat always happens
game.getEndOfCombat().executeUntil();
game.getEndOfCombat().executeAt();
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
break;
case MAIN2:
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
break;
case END_OF_TURN:
game.getEndOfTurn().executeAt();
break;
case CLEANUP:
// Reset Damage received map
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
c.onCleanupPhase(playerTurn);
}
game.getEndOfCombat().executeUntil(); //Repeat here in case Time Stop et. al. ends combat early
game.getEndOfTurn().executeUntil();
for (Player player : game.getPlayers()) {
player.onCleanupPhase();
player.getController().autoPassCancel(); // autopass won't wrap to next turn
}
this.getPlayerTurn().removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(this.getNextTurn());
this.nUpkeepsThisTurn = 0;
break;
default:
break;
}
case COMBAT_END: if (inCombat()) {
// End Combat always happens
game.getEndOfCombat().executeUntil();
game.getEndOfCombat().executeAt();
CombatUtil.showCombat(game); CombatUtil.showCombat(game);
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc()); }
break; }
case MAIN2: // Handle effects that happen at the beginning of phases
CombatUtil.showCombat(game); game.getAction().checkStateEffects();
//SDisplayUtil.showTab(EDocID.REPORT_STACK.getDoc());
break;
case END_OF_TURN: if (this.givePriorityToPlayer) {
game.getEndOfTurn().executeAt();
break;
case CLEANUP:
// Reset Damage received map
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
c.onCleanupPhase(playerTurn);
}
game.getEndOfCombat().executeUntil(); //Repeat here in case Time Stop et. al. ends combat early
game.getEndOfTurn().executeUntil();
for (Player player : game.getPlayers()) {
player.onCleanupPhase();
player.getController().autoPassCancel(); // autopass won't wrap to next turn
}
this.getPlayerTurn().removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(this.getNextTurn());
this.nUpkeepsThisTurn = 0;
break;
default:
break;
}
if (this.isPlayerPriorityAllowed()) {
// Run triggers if phase isn't being skipped // Run triggers if phase isn't being skipped
final HashMap<String, Object> runParams = new HashMap<String, Object>(); final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Phase", this.getPhase().NameForScripts); runParams.put("Phase", this.getPhase().NameForScripts);
@@ -495,7 +443,6 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
// This line fixes Combat Damage triggers not going off when they should // This line fixes Combat Damage triggers not going off when they should
game.getStack().unfreezeStack(); game.getStack().unfreezeStack();
} }
/** /**
@@ -715,8 +662,11 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
if(phase != null) if(phase != null)
throw new IllegalStateException("Turns already started, call this only once per game"); throw new IllegalStateException("Turns already started, call this only once per game");
setPlayerTurn(goesFirst); setPlayerTurn(goesFirst);
advancePhase(); advanceToNextPhase();
onPhaseBegin();
updateObservers();
// don't even offer priority, because it's untap of 1st turn now // don't even offer priority, because it's untap of 1st turn now
while (!game.isGameOver()) { // loop only while is playing while (!game.isGameOver()) { // loop only while is playing
@@ -732,18 +682,24 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
// System.out.println(String.format("%s %s: %s passes priority to %s", playerTurn, phase, actingPlayer, nextPlayer)); // System.out.println(String.format("%s %s: %s passes priority to %s", playerTurn, phase, actingPlayer, nextPlayer));
if (firstAction.equals(nextPlayer)) { if (firstAction.equals(nextPlayer)) {
if (game.getStack().isEmpty()) { if (game.getStack().isEmpty()) {
advancePhase(); this.setPriority(this.getPlayerTurn()); // this needs to be set early as we exit the phase
// end phase
this.givePriorityToPlayer = true;
onPhaseEnd();
advanceToNextPhase();
onPhaseBegin();
} else if (!game.getStack().hasSimultaneousStackEntries()) { } else if (!game.getStack().hasSimultaneousStackEntries()) {
game.getStack().resolveStack(); game.getStack().resolveStack();
game.getStack().chooseOrderOfSimultaneousStackEntryAll(); game.getStack().chooseOrderOfSimultaneousStackEntryAll();
updateObservers();
} }
} else { } else {
// pass the priority to other player // pass the priority to other player
this.pPlayerPriority = nextPlayer; this.pPlayerPriority = nextPlayer;
updateObservers();
} }
updateObservers();
// If ever the karn's ultimate resolved // If ever the karn's ultimate resolved
if( game.getAge() == GameAge.RestartedByKarn) { if( game.getAge() == GameAge.RestartedByKarn) {
phase = null; phase = null;
@@ -756,16 +712,18 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
if ( phase == PhaseType.COMBAT_DECLARE_ATTACKERS || phase == PhaseType.COMBAT_DECLARE_BLOCKERS) if ( phase == PhaseType.COMBAT_DECLARE_ATTACKERS || phase == PhaseType.COMBAT_DECLARE_BLOCKERS)
game.getStack().freezeStack(); game.getStack().freezeStack();
boolean givePriority = isPlayerPriorityAllowed; boolean givePriority = givePriorityToPlayer;
if ( phase == PhaseType.COMBAT_DECLARE_BLOCKERS) { if ( phase == PhaseType.COMBAT_DECLARE_BLOCKERS)
givePriority = game.getCombat().isPlayerAttacked(pPlayerPriority); givePriority = game.getCombat().isPlayerAttacked(pPlayerPriority);
}
if ( phase == PhaseType.COMBAT_DECLARE_ATTACKERS && playerTurn != pPlayerPriority ) if ( phase == PhaseType.COMBAT_DECLARE_ATTACKERS && playerTurn != pPlayerPriority )
givePriority = false; givePriority = false;
if( DEBUG_PHASES ) if( DEBUG_PHASES ) {
System.out.println("\t\tStack: " + game.getStack());
System.out.print(FThreads.prependThreadId(debugPrintState(givePriority))); System.out.print(FThreads.prependThreadId(debugPrintState(givePriority)));
}
if( givePriority ) { if( givePriority ) {
@@ -776,66 +734,24 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
if( DEBUG_PHASES ) { if( DEBUG_PHASES ) {
sw.stop(); sw.stop();
System.out.print("... passed in " + sw.getTime()/1000f + " ms\n"); System.out.print("... passed in " + sw.getTime()/1000f + " s\n");
System.out.println("\t\tStack: " + game.getStack());
sw.reset(); sw.reset();
} }
} else if( DEBUG_PHASES ){ } else if( DEBUG_PHASES ){
System.out.print(" >>\n"); System.out.print(" >>\n");
} }
} }
} }
private void advancePhase() { // may be called externally only from gameAction after mulligans // this is a hack for the setup game state mode, do not use outside of devSetupGameState code
// as it avoids calling any of the phase effects that may be necessary in a less enforced context
this.setPriority(this.getPlayerTurn()); // this needs to be set early as we exit the phase public final void devModeSet(final PhaseType phase0, final Player player0) {
// end phase if (null != phase0) this.phase = phase0;
setPlayersPriorityPermission(true); if (null != player0 )
nextPhase(); setPlayerTurn(player0);
// When consecutively skipping phases (like in combat) this section
// pushes through that block
handleBeginPhase();
// it no longer does.
updateObservers();
}
/**
* <p>
* Setter for the field <code>needToNextPhase</code>.
* </p>
*
* @param needToNextPhase
* a boolean.
*/
public final void setPlayersPriorityPermission(final boolean mayHavePriority) {
this.isPlayerPriorityAllowed = mayHavePriority;
}
/**
* <p>
* isNeedToNextPhase.
* </p>
*
* @return a boolean.
*/
public final boolean isPlayerPriorityAllowed() {
return this.isPlayerPriorityAllowed;
}
// this is a hack for the setup game state mode, do not use outside of
// devSetupGameState code
// as it avoids calling any of the phase effects that may be necessary in a
// less enforced context
/**
* <p>
* setDevPhaseState.
* </p>
*
* @param phase
* a {@link java.forge.game.phase.PhaseType} object.
*/
public final void setDevPhaseState(final PhaseType phase0) {
this.phase = phase0;
} }
/** /**
@@ -843,18 +759,12 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
* *
* @param phaseID the new phase state * @param phaseID the new phase state
*/ */
public final void setPhaseState(final PhaseType phase0) { public final void endTurnByEffect() {
this.phase = phase0; this.phase = PhaseType.CLEANUP;
this.handleBeginPhase(); this.onPhaseBegin();
} }
/**
*
* TODO Write javadoc for this method.
*
* @param b
* a boolean
*/
public final void setPreventCombatDamageThisTurn(final boolean b) { public final void setPreventCombatDamageThisTurn(final boolean b) {
this.bPreventCombatDamageThisTurn = true; this.bPreventCombatDamageThisTurn = true;
} }
@@ -876,4 +786,10 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
} }
// just to avoid exposing variable to oute classes
public void onStackResolved() {
givePriorityToPlayer = true;
}
} }

View File

@@ -101,9 +101,8 @@ public class PhaseUtil {
// TODO move propaganda to happen as the Attacker is Declared // TODO move propaganda to happen as the Attacker is Declared
// Remove illegal Propaganda attacks first only for attacking the Player // Remove illegal Propaganda attacks first only for attacking the Player
final int size = list.size();
for (int i = 0; i < size; i++) { for (final Card c : list) {
final Card c = list.get(i);
CombatUtil.checkPropagandaEffects(game, c); CombatUtil.checkPropagandaEffects(game, c);
} }
PhaseUtil.handleAttackingTriggers(game); PhaseUtil.handleAttackingTriggers(game);

View File

@@ -198,15 +198,16 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
*/ */
public final void unfreezeStack() { public final void unfreezeStack() {
this.frozen = false; this.frozen = false;
boolean checkState = !this.getFrozenStack().isEmpty();
// Add all Frozen Abilities onto the stack // Add all Frozen Abilities onto the stack
while (!this.getFrozenStack().isEmpty()) { while (!this.getFrozenStack().isEmpty()) {
final SpellAbility sa = this.getFrozenStack().pop().getSpellAbility(); final SpellAbility sa = this.getFrozenStack().pop().getSpellAbility();
this.add(sa); this.add(sa);
} }
// Add all waiting triggers onto the stack // Add all waiting triggers onto the stack
checkState |= game.getTriggerHandler().runWaitingTriggers(); game.getTriggerHandler().runWaitingTriggers();
if (checkState) {
if (!simultaneousStackEntryList.isEmpty()) {
this.chooseOrderOfSimultaneousStackEntryAll(); this.chooseOrderOfSimultaneousStackEntryAll();
game.getAction().checkStateEffects(); game.getAction().checkStateEffects();
} }
@@ -738,7 +739,7 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
game.getAction().checkStateEffects(); game.getAction().checkStateEffects();
game.getPhaseHandler().setPlayersPriorityPermission(true); game.getPhaseHandler().onStackResolved();
this.curResolvingCard = null; this.curResolvingCard = null;
@@ -1108,4 +1109,10 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
public void clear() { public void clear() {
stack.clear(); stack.clear();
} }
@Override
public String toString() {
return String.format("%s==%s==%s", simultaneousStackEntryList, frozenStack.toString(), stack.toString());
}
} }

View File

@@ -48,6 +48,7 @@ import forge.control.input.InputSelectCardsFromList;
import forge.game.GameState; import forge.game.GameState;
import forge.game.GameType; import forge.game.GameType;
import forge.game.PlanarDice; import forge.game.PlanarDice;
import forge.game.phase.PhaseType;
import forge.game.player.HumanPlay; import forge.game.player.HumanPlay;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.PlayerZone; import forge.game.zone.PlayerZone;
@@ -146,15 +147,10 @@ public final class GuiDisplayUtil {
final Player human = game.getPlayers().get(0); final Player human = game.getPlayers().get(0);
final Player ai = game.getPlayers().get(1); final Player ai = game.getPlayers().get(1);
if (tChangePlayer.equals("human")) { Player newPlayerTurn = tChangePlayer.equals("human") ? newPlayerTurn = human : tChangePlayer.equals("ai") ? newPlayerTurn = ai : null;
game.getPhaseHandler().setPlayerTurn(human); PhaseType newPhase = tChangePhase.trim().equalsIgnoreCase("none") ? null : PhaseType.smartValueOf(tChangePhase);
} else if (tChangePlayer.equals("ai")) {
game.getPhaseHandler().setPlayerTurn(ai); game.getPhaseHandler().devModeSet(newPhase, newPlayerTurn);
}
if (!tChangePhase.trim().equalsIgnoreCase("none")) {
game.getPhaseHandler().setDevPhaseState(forge.game.phase.PhaseType.smartValueOf(tChangePhase));
}
game.getCombat().reset(game.getPhaseHandler().getPlayerTurn()); game.getCombat().reset(game.getPhaseHandler().getPlayerTurn());
game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); game.getTriggerHandler().suppressMode(TriggerType.ChangesZone);