A propper implementation of Rule 514 - Cleanup phase

This commit is contained in:
Maxmtg
2013-05-29 10:00:54 +00:00
parent 358ef743f3
commit c9450c16f7
6 changed files with 123 additions and 139 deletions

View File

@@ -801,23 +801,6 @@ public class AiController {
playSpellAbilities(game);
} else {
switch(phase) {
case CLEANUP:
if ( game.getPhaseHandler().getPlayerTurn() == player ) {
final int size = player.getCardsIn(ZoneType.Hand).size();
if (!player.isUnlimitedHandSize()) {
int max = Math.min(player.getZone(ZoneType.Hand).size(), size - player.getMaxHandSize());
if( max > 0) {
final List<Card> toDiscard = getCardsToDiscard(max, (String[])null, null);
for (int i = 0; i < toDiscard.size(); i++) {
player.discard(toDiscard.get(i), null);
}
}
game.getStack().chooseOrderOfSimultaneousStackEntryAll();
}
}
break;
case COMBAT_DECLARE_ATTACKERS:
declareAttackers();
break;

View File

@@ -172,15 +172,6 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
return this.bCombat;
}
/**
* <p>
* repeatPhase.
* </p>
*/
public final void repeatPhase() {
this.bRepeatCleanup = true;
}
private void advanceToNextPhase() {
PhaseType oldPhase = phase;
@@ -213,83 +204,6 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
}
private void onPhaseEnd() {
// If the Stack isn't empty why is nextPhase being called?
if (!game.getStack().isEmpty()) {
throw new IllegalStateException("Phase.nextPhase() is called, but Stack isn't empty.");
}
for (Player p : game.getPlayers()) {
int burn = p.getManaPool().clearPool(true);
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_MANABURN)) {
p.loseLife(burn);
// Play the Mana Burn sound
game.getEvents().post(new ManaBurnEvent());
}
p.updateObservers();
}
switch (this.phase) {
case UNTAP:
this.nCombatsThisTurn = 0;
break;
case COMBAT_DECLARE_ATTACKERS:
this.bCombat = !game.getCombat().getAttackers().isEmpty();
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 = 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
}
}
private boolean isSkippingPhase(PhaseType phase) {
switch(phase) {
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;
}
}
private final void onPhaseBegin() {
if ( isSkippingPhase(phase) ) {
@@ -404,6 +318,18 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
break;
case CLEANUP:
// Rule 514.1
final int handSize = playerTurn.getZone(ZoneType.Hand).size();
final int max = playerTurn.getMaxHandSize();
int numDiscard = playerTurn.isUnlimitedHandSize() || handSize <= max || handSize == 0 ? 0 : handSize - max;
if ( numDiscard > 0 ) {
for(Card c : playerTurn.getController().chooseCardsToDiscardToMaximumHandSize(numDiscard)){
playerTurn.discard(c, null);
}
}
// Rule 514.2
// Reset Damage received map
for (final Card c : game.getCardsIn(ZoneType.Battlefield)) {
c.onCleanupPhase(playerTurn);
@@ -419,6 +345,9 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
this.getPlayerTurn().removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(this.getNextTurn());
this.nUpkeepsThisTurn = 0;
// Rule 514.3
givePriorityToPlayer = false;
break;
default:
@@ -443,6 +372,90 @@ public class PhaseHandler extends MyObservable implements java.io.Serializable {
// This line fixes Combat Damage triggers not going off when they should
game.getStack().unfreezeStack();
// Rule 514.3a
if( phase == PhaseType.CLEANUP && !game.getStack().isEmpty() ) {
bRepeatCleanup = true;
givePriorityToPlayer = true;
}
}
private void onPhaseEnd() {
// If the Stack isn't empty why is nextPhase being called?
if (!game.getStack().isEmpty()) {
throw new IllegalStateException("Phase.nextPhase() is called, but Stack isn't empty.");
}
for (Player p : game.getPlayers()) {
int burn = p.getManaPool().clearPool(true);
if (Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_MANABURN)) {
p.loseLife(burn);
// Play the Mana Burn sound
game.getEvents().post(new ManaBurnEvent());
}
p.updateObservers();
}
switch (this.phase) {
case UNTAP:
this.nCombatsThisTurn = 0;
break;
case COMBAT_DECLARE_ATTACKERS:
this.bCombat = !game.getCombat().getAttackers().isEmpty();
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 = 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
}
}
private boolean isSkippingPhase(PhaseType phase) {
switch(phase) {
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;
}
}
/**

View File

@@ -126,4 +126,5 @@ public abstract class PlayerController {
public abstract List<Card> getCardsToMulligan(boolean isCommander, Player firstPlayer);
public abstract void takePriority();
public abstract List<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard);
}

View File

@@ -282,4 +282,9 @@ public class PlayerControllerAi extends PlayerController {
brains.onPriorityRecieved();
// use separate thread for AI?
}
@Override
public List<Card> chooseCardsToDiscardToMaximumHandSize(int numDiscard) {
return brains.getCardsToDiscard(numDiscard, (String[])null, null);
}
}

View File

@@ -33,7 +33,6 @@ import forge.deck.DeckSection;
import forge.game.GameState;
import forge.game.GameType;
import forge.game.phase.PhaseType;
import forge.game.zone.MagicStack;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.gui.GuiDialog;
@@ -483,7 +482,6 @@ public class PlayerControllerHuman extends PlayerController {
@Override
public void takePriority() {
PhaseType phase = game.getPhaseHandler().getPhase();
MagicStack stack = game.getStack();
boolean maySkipPriority = mayAutoPass(phase) || isUiSetToSkipPhase(game.getPhaseHandler().getPlayerTurn(), phase);
if (game.getStack().isEmpty() && maySkipPriority) {
@@ -504,32 +502,22 @@ public class PlayerControllerHuman extends PlayerController {
Singletons.getControl().getInputQueue().setInputAndWait(inpBlock);
return;
case CLEANUP:
if( stack.isEmpty() ) {
default:
showDefaultInput();
}
}
@Override
public List<Card> chooseCardsToDiscardToMaximumHandSize(int nDiscard) {
final int n = player.getZone(ZoneType.Hand).size();
final int max = player.getMaxHandSize();
// goes to the next phase
int nDiscard = player.isUnlimitedHandSize() || n <= max || n == 0 ? 0 : n - max;
if ( nDiscard > 0 ) {
InputSelectCardsFromList inp = new InputSelectCardsFromList(nDiscard, nDiscard, player.getZone(ZoneType.Hand).getCards());
String msgFmt = "Cleanup Phase: You can only have a maximum of %d cards, you currently have %d cards in your hand - select %d card(s) to discard";
String message = String.format(msgFmt, max, n, nDiscard);
inp.setMessage(message);
inp.setCancelAllowed(false);
Singletons.getControl().getInputQueue().setInputAndWait(inp);
for(Card c : inp.getSelected()){
player.discard(c, null);
}
}
break;
}
// fallthough
default:
showDefaultInput();
}
return inp.getSelected();
}
}

View File

@@ -59,7 +59,6 @@ import forge.game.ai.ComputerUtil;
import forge.game.ai.ComputerUtilCard;
import forge.game.ai.ComputerUtilCost;
import forge.game.event.SpellResolvedEvent;
import forge.game.phase.PhaseType;
import forge.game.player.HumanPlay;
import forge.game.player.Player;
import forge.gui.GuiChoose;
@@ -367,11 +366,6 @@ public class MagicStack extends MyObservable implements Iterable<SpellAbilitySta
System.out.println(sp.getSourceCard().getName() + " - activatingPlayer not set before adding to stack.");
}
if (game.getPhaseHandler().is(PhaseType.CLEANUP)) {
// If something triggers during Cleanup, need to repeat
game.getPhaseHandler().repeatPhase();
}
if ((sp instanceof AbilityTriggered) || (sp instanceof AbilityStatic)) {
// TODO: make working triggered ability
sp.resolve();