Support updating game view

This commit is contained in:
drdev
2014-10-09 15:01:45 +00:00
parent 91e509ffd6
commit 8f023aaed3
12 changed files with 478 additions and 815 deletions

View File

@@ -97,7 +97,7 @@ public class Game {
private GameOutcome outcome; private GameOutcome outcome;
private boolean disableAutoYields; private boolean disableAutoYields;
private final GameView view = new GameView(); private final GameView view;
public Game(List<RegisteredPlayer> players0, GameRules rules0, Match match0) { /* no more zones to map here */ public Game(List<RegisteredPlayer> players0, GameRules rules0, Match match0) { /* no more zones to map here */
rules = rules0; rules = rules0;
@@ -147,6 +147,8 @@ public class Game {
endOfTurn = new EndOfTurn(this); endOfTurn = new EndOfTurn(this);
endOfCombat = new Phase(PhaseType.COMBAT_END); endOfCombat = new Phase(PhaseType.COMBAT_END);
view = new GameView(this);
subscribeToEvents(gameLog.getEventVisitor()); subscribeToEvents(gameLog.getEventVisitor());
} }
@@ -154,10 +156,15 @@ public class Game {
return view; return view;
} }
/**
* Gets the players who are still fighting to win.
*/
public final List<Player> getPlayers() {
return roIngamePlayers;
}
/** /**
* Gets the players who are still fighting to win, in turn order. * Gets the players who are still fighting to win, in turn order.
*
* @return the players
*/ */
public final List<Player> getPlayersInTurnOrder() { public final List<Player> getPlayersInTurnOrder() {
if (turnOrder.isDefaultDirection()) { if (turnOrder.isDefaultDirection()) {
@@ -166,129 +173,72 @@ public class Game {
return roIngamePlayersReversed; return roIngamePlayersReversed;
} }
/**
* Gets the players who are still fighting to win.
*
* @return the players
*/
public final List<Player> getPlayers() {
return roIngamePlayers;
}
/** /**
* Gets the players who participated in match (regardless of outcome). * Gets the players who participated in match (regardless of outcome).
* <i>Use this in UI and after match calculations</i> * <i>Use this in UI and after match calculations</i>
*
* @return the players
*/ */
public final List<Player> getRegisteredPlayers() { public final List<Player> getRegisteredPlayers() {
return allPlayers; return allPlayers;
} }
/** public final Untap getUntap() {
* Gets the cleanup step. return untap;
* }
* @return the cleanup step public final Upkeep getUpkeep() {
*/ return upkeep;
}
public final Phase getEndOfCombat() {
return endOfCombat;
}
public final EndOfTurn getEndOfTurn() {
return endOfTurn;
}
public final Phase getCleanup() { public final Phase getCleanup() {
return cleanup; return cleanup;
} }
/**
* Gets the end of turn.
*
* @return the endOfTurn
*/
public final EndOfTurn getEndOfTurn() {
return endOfTurn;
}
/**
* Gets the end of combat.
*
* @return the endOfCombat
*/
public final Phase getEndOfCombat() {
return endOfCombat;
}
/**
* Gets the upkeep.
*
* @return the upkeep
*/
public final Upkeep getUpkeep() {
return upkeep;
}
/**
* Gets the untap.
*
* @return the upkeep
*/
public final Untap getUntap() {
return untap;
}
/**
* Gets the phaseHandler.
*
* @return the phaseHandler
*/
public final PhaseHandler getPhaseHandler() { public final PhaseHandler getPhaseHandler() {
return phaseHandler; return phaseHandler;
} }
public final void updateTurnForView() {
view.updateTurn(phaseHandler);
}
public final void updatePhaseForView() {
view.updatePhase(phaseHandler);
}
public final void updatePlayerTurnForView() {
view.updatePlayerTurn(phaseHandler);
}
/**
* Gets the stack.
*
* @return the stack
*/
public final MagicStack getStack() { public final MagicStack getStack() {
return stack; return stack;
} }
public final void updateStackForView() {
view.updateStack(stack);
}
/**
* Gets the static effects.
*
* @return the staticEffects
*/
public final StaticEffects getStaticEffects() { public final StaticEffects getStaticEffects() {
return staticEffects; return staticEffects;
} }
/**
* Gets the trigger handler.
*
* @return the triggerHandler
*/
public final TriggerHandler getTriggerHandler() { public final TriggerHandler getTriggerHandler() {
return triggerHandler; return triggerHandler;
} }
/**
* Gets the combat.
*
* @return the combat
*/
public final Combat getCombat() { public final Combat getCombat() {
return getPhaseHandler().getCombat(); return getPhaseHandler().getCombat();
} }
public final void updateCombatForView() {
view.updateCombat(getCombat());
}
/**
* Gets the game log.
*
* @return the game log
*/
public final GameLog getGameLog() { public final GameLog getGameLog() {
return gameLog; return gameLog;
} }
public final void updateGameLogForView() {
view.updateGameLog(gameLog);
}
/**
* Gets the stack zone.
*
* @return the stackZone
*/
public final Zone getStackZone() { public final Zone getStackZone() {
return stackZone; return stackZone;
} }
@@ -311,37 +261,25 @@ public class Game {
} }
/** /**
* Get the turn order. * The Direction in which the turn order of this Game currently proceeds.
* @return the Direction in which the turn order of this Game currently
* proceeds.
*/ */
public final Direction getTurnOrder() { public final Direction getTurnOrder() {
return turnOrder; return turnOrder;
} }
public final void reverseTurnOrder() { public final void reverseTurnOrder() {
turnOrder = turnOrder.getOtherDirection(); turnOrder = turnOrder.getOtherDirection();
} }
public final void resetTurnOrder() { public final void resetTurnOrder() {
turnOrder = Direction.getDefaultDirection(); turnOrder = Direction.getDefaultDirection();
} }
/** /**
* Create and return the next timestamp. * Create and return the next timestamp.
*
* @return the next timestamp
*/ */
public final long getNextTimestamp() { public final long getNextTimestamp() {
timestamp = getTimestamp() + 1; timestamp = getTimestamp() + 1;
return getTimestamp(); return getTimestamp();
} }
/**
* Gets the timestamp.
*
* @return the timestamp
*/
public final long getTimestamp() { public final long getTimestamp() {
return timestamp; return timestamp;
} }
@@ -350,24 +288,14 @@ public class Game {
return outcome; return outcome;
} }
/**
* @return the replacementHandler
*/
public ReplacementHandler getReplacementHandler() { public ReplacementHandler getReplacementHandler() {
return replacementHandler; return replacementHandler;
} }
/**
* @return the gameOver
*/
public synchronized boolean isGameOver() { public synchronized boolean isGameOver() {
return age == GameStage.GameOver; return age == GameStage.GameOver;
} }
/**
* @param reason
* @param go the gameOver to set
*/
public synchronized void setGameOver(GameEndReason reason) { public synchronized void setGameOver(GameEndReason reason) {
age = GameStage.GameOver; age = GameStage.GameOver;
for (Player p : allPlayers) { for (Player p : allPlayers) {
@@ -384,6 +312,8 @@ public class Game {
outcome = result; outcome = result;
match.addGamePlayed(this); match.addGamePlayed(this);
view.updateGameOver(this);
// The log shall listen to events and generate text internally // The log shall listen to events and generate text internally
fireEvent(new GameEventGameOutcome(result, match.getPlayedGames())); fireEvent(new GameEventGameOutcome(result, match.getPlayedGames()));
} }
@@ -544,10 +474,6 @@ public class Game {
return position; return position;
} }
/**
* TODO: Write javadoc for this method.
* @param p
*/
public void onPlayerLost(Player p) { public void onPlayerLost(Player p) {
ingamePlayers.remove(p); ingamePlayers.remove(p);
@@ -571,23 +497,13 @@ public class Game {
events.register(subscriber); events.register(subscriber);
} }
/**
* @return the type of game (Constructed/Limited/Planechase/etc...)
*/
public GameRules getRules() { public GameRules getRules() {
return rules; return rules;
} }
/**
* @return the activePlane
*/
public List<Card> getActivePlanes() { public List<Card> getActivePlanes() {
return activePlanes; return activePlanes;
} }
/**
* @param activePlane0 the activePlane to set
*/
public void setActivePlanes(List<Card> activePlane0) { public void setActivePlanes(List<Card> activePlane0) {
activePlanes = activePlane0; activePlanes = activePlane0;
} }
@@ -604,7 +520,7 @@ public class Game {
Card c = getCardsIn(ZoneType.Command).get(i); Card c = getCardsIn(ZoneType.Command).get(i);
if (c.isScheme() && !c.isType("Ongoing")) { if (c.isScheme() && !c.isType("Ongoing")) {
boolean foundonstack = false; boolean foundonstack = false;
for (SpellAbilityStackInstance si : getStack()) { for (SpellAbilityStackInstance si : stack) {
if (si.getSourceCard().equals(c)) { if (si.getSourceCard().equals(c)) {
foundonstack = true; foundonstack = true;
break; break;
@@ -719,7 +635,6 @@ public class Game {
chooseRandomCardsForAnte(player, anteed); chooseRandomCardsForAnte(player, anteed);
} }
} }
return anteed; return anteed;
} }

View File

@@ -7,28 +7,109 @@ import forge.game.card.CardView;
import forge.game.combat.AttackingBand; import forge.game.combat.AttackingBand;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.combat.CombatView; import forge.game.combat.CombatView;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView; import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.StackItemView; import forge.game.spellability.StackItemView;
import forge.game.zone.MagicStack;
import forge.trackable.TrackableIndex; import forge.trackable.TrackableIndex;
import forge.trackable.TrackableObject;
import forge.trackable.TrackableProperty;
public class GameView { public class GameView extends TrackableObject {
private final TrackableIndex<CardView> cards = new TrackableIndex<CardView>(); private final TrackableIndex<CardView> cards = new TrackableIndex<CardView>();
private final TrackableIndex<PlayerView> players = new TrackableIndex<PlayerView>(); private final TrackableIndex<PlayerView> players = new TrackableIndex<PlayerView>();
private final TrackableIndex<SpellAbilityView> spellAbilities = new TrackableIndex<SpellAbilityView>(); private final TrackableIndex<SpellAbilityView> spellAbilities = new TrackableIndex<SpellAbilityView>();
private final TrackableIndex<StackItemView> stackItems = new TrackableIndex<StackItemView>(); private final TrackableIndex<StackItemView> stackItems = new TrackableIndex<StackItemView>();
private CombatView combatView; private CombatView combatView;
public GameView() { public GameView(Game game) {
super(-1); //ID not needed
set(TrackableProperty.WinningTeam, -1);
GameRules rules = game.getRules();
set(TrackableProperty.Commander, rules.hasAppliedVariant(GameType.Commander));
set(TrackableProperty.GameType, rules.getGameType());
set(TrackableProperty.PoisonCountersToLose, rules.getPoisonCountersToLose());
set(TrackableProperty.NumGamesInMatch, rules.getGamesPerMatch());
set(TrackableProperty.GameLog, game.getGameLog());
set(TrackableProperty.NumPlayedGamesInMatch, game.getMatch().getPlayedGames().size());
} }
public CombatView getCombatView() { public boolean isCommander() {
return get(TrackableProperty.Commander);
}
public GameType getGameType() {
return get(TrackableProperty.GameType);
}
public int getPoisonCountersToLose() {
return get(TrackableProperty.PoisonCountersToLose);
}
public int getNumGamesInMatch() {
return get(TrackableProperty.NumGamesInMatch);
}
public int getTurn() {
return get(TrackableProperty.Turn);
}
void updateTurn(PhaseHandler phaseHandler) {
set(TrackableProperty.Turn, phaseHandler.getTurn());
}
public PhaseType getPhase() {
return get(TrackableProperty.Phase);
}
void updatePhase(PhaseHandler phaseHandler) {
set(TrackableProperty.Phase, phaseHandler.getPhase());
}
public PlayerView getPlayerTurn() {
return get(TrackableProperty.PlayerTurn);
}
void updatePlayerTurn(PhaseHandler phaseHandler) {
set(TrackableProperty.PlayerTurn, PlayerView.get(phaseHandler.getPlayerTurn()));
}
public int getStormCount() {
return get(TrackableProperty.StormCount);
}
void updateStack(MagicStack stack) {
set(TrackableProperty.StormCount, stack.getSpellsCastThisTurn().size());
}
public boolean isFirstGameInMatch() {
return getNumPlayedGamesInMatch() == 0;
}
public int getNumPlayedGamesInMatch() {
return get(TrackableProperty.NumPlayedGamesInMatch);
}
public boolean isGameOver() {
return get(TrackableProperty.GameOver);
}
public boolean isMatchOver() {
return get(TrackableProperty.MatchOver);
}
public int getWinningTeam() {
return get(TrackableProperty.WinningTeam);
}
void updateGameOver(Game game) {
set(TrackableProperty.GameOver, game.isGameOver());
set(TrackableProperty.MatchOver, game.getMatch().isMatchOver());
set(TrackableProperty.WinningTeam, game.getOutcome() == null ? -1 : game.getOutcome().getWinningTeam());
}
public GameLog getGameLog() {
return get(TrackableProperty.GameLog);
}
void updateGameLog(GameLog gameLog) {
flagAsChanged(TrackableProperty.GameLog); //don't need to set the property since it won't change
}
public CombatView getCombat() {
return combatView; return combatView;
} }
void updateCombat(Combat combat) {
public void refreshCombat(Game game) {
final Combat combat = game.getCombat();
if (combat == null) { if (combat == null) {
combatView = null; combatView = null;
return; return;

View File

@@ -27,26 +27,20 @@ public class Match {
private final List<GameOutcome> gamesPlayed = new ArrayList<GameOutcome>(); private final List<GameOutcome> gamesPlayed = new ArrayList<GameOutcome>();
private final List<GameOutcome> gamesPlayedRo; private final List<GameOutcome> gamesPlayedRo;
public Match(GameRules rules, List<RegisteredPlayer> players0) { public Match(GameRules rules0, List<RegisteredPlayer> players0) {
gamesPlayedRo = Collections.unmodifiableList(gamesPlayed); gamesPlayedRo = Collections.unmodifiableList(gamesPlayed);
players = Collections.unmodifiableList(Lists.newArrayList(players0)); players = Collections.unmodifiableList(Lists.newArrayList(players0));
this.rules = rules; rules = rules0;
} }
public GameRules getRules() { public GameRules getRules() {
return rules; return rules;
} }
/**
* Gets the games played.
*
* @return the games played
*/
public final List<GameOutcome> getPlayedGames() { public final List<GameOutcome> getPlayedGames() {
return this.gamesPlayedRo; return gamesPlayedRo;
} }
public void addGamePlayed(Game finished) { public void addGamePlayed(Game finished) {
if (!finished.isGameOver()) { if (!finished.isGameOver()) {
throw new IllegalStateException("Game is not over yet."); throw new IllegalStateException("Game is not over yet.");
@@ -54,17 +48,11 @@ public class Match {
gamesPlayed.add(finished.getOutcome()); gamesPlayed.add(finished.getOutcome());
} }
/**
* TODO: Write javadoc for this method.
*/
public Game createGame() { public Game createGame() {
Game game = new Game(players, rules, this); Game game = new Game(players, rules, this);
return game; return game;
} }
/**
* TODO: Write javadoc for this method.
*/
public void startGame(final Game game) { public void startGame(final Game game) {
prepareAllZones(game); prepareAllZones(game);
if (rules.useAnte()) { // Deciding which cards go to ante if (rules.useAnte()) { // Deciding which cards go to ante
@@ -103,11 +91,6 @@ public class Match {
return gamesPlayedRo; return gamesPlayedRo;
} }
/**
* TODO: Write javadoc for this method.
*
* @return
*/
public boolean isMatchOver() { public boolean isMatchOver() {
int[] victories = new int[players.size()]; int[] victories = new int[players.size()];
for (GameOutcome go : gamesPlayed) { for (GameOutcome go : gamesPlayed) {
@@ -130,12 +113,6 @@ public class Match {
return gamesPlayed.size() >= rules.getGamesPerMatch(); return gamesPlayed.size() >= rules.getGamesPerMatch();
} }
/**
* TODO: Write javadoc for this method.
*
* @param questPlayer
* @return
*/
public int getGamesWonBy(LobbyPlayer questPlayer) { public int getGamesWonBy(LobbyPlayer questPlayer) {
int sum = 0; int sum = 0;
for (GameOutcome go : gamesPlayed) { for (GameOutcome go : gamesPlayed) {
@@ -146,12 +123,6 @@ public class Match {
return sum; return sum;
} }
/**
* TODO: Write javadoc for this method.
*
* @param questPlayer
* @return
*/
public boolean isWonBy(LobbyPlayer questPlayer) { public boolean isWonBy(LobbyPlayer questPlayer) {
return getGamesWonBy(questPlayer) >= rules.getGamesToWinMatch(); return getGamesWonBy(questPlayer) >= rules.getGamesToWinMatch();
} }
@@ -170,7 +141,6 @@ public class Match {
} }
} }
} }
return myRemovedAnteCards; return myRemovedAnteCards;
} }
@@ -274,7 +244,6 @@ public class Match {
private void executeAnte(Game lastGame) { private void executeAnte(Game lastGame) {
GameOutcome outcome = lastGame.getOutcome(); GameOutcome outcome = lastGame.getOutcome();
// remove all the lost cards from owners' decks // remove all the lost cards from owners' decks
List<PaperCard> losses = new ArrayList<PaperCard>(); List<PaperCard> losses = new ArrayList<PaperCard>();
int cntPlayers = players.size(); int cntPlayers = players.size();
@@ -358,7 +327,6 @@ public class Match {
} }
} }
} }
// Other game types (like Quest) need to do something in their own calls to actually update data // Other game types (like Quest) need to do something in their own calls to actually update data
} }
} }

View File

@@ -4029,7 +4029,7 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
if (!CardUtil.getColors(this).hasAnyColor(mask)) if (!CardUtil.getColors(this).hasAnyColor(mask))
return false; return false;
} else if (restriction.equals("LastCastThisTurn")) { } else if (restriction.equals("LastCastThisTurn")) {
final List<Card> c = game.getStack().getCardsCastThisTurn(); final List<Card> c = game.getStack().getSpellsCastThisTurn();
if (c.isEmpty() || !sharesColorWith(c.get(c.size() - 1))) { if (c.isEmpty() || !sharesColorWith(c.get(c.size() - 1))) {
return false; return false;
} }

View File

@@ -1029,7 +1029,7 @@ public class CardFactoryUtil {
return doXMath(c.getFirstSpellAbility().getTotalManaSpent(), m, c); return doXMath(c.getFirstSpellAbility().getTotalManaSpent(), m, c);
} }
if (sq[0].equals("StormCount")) { if (sq[0].equals("StormCount")) {
return doXMath(game.getStack().getCardsCastThisTurn().size() - 1, m, c); return doXMath(game.getStack().getSpellsCastThisTurn().size() - 1, m, c);
} }
if (sq[0].equals("DamageDoneThisTurn")) { if (sq[0].equals("DamageDoneThisTurn")) {
return doXMath(c.getDamageDoneThisTurn(), m, c); return doXMath(c.getDamageDoneThisTurn(), m, c);

View File

@@ -191,7 +191,7 @@ public final class CardUtil {
public static List<Card> getThisTurnCast(final String valid, final Card src) { public static List<Card> getThisTurnCast(final String valid, final Card src) {
List<Card> res = new ArrayList<Card>(); List<Card> res = new ArrayList<Card>();
final Game game = src.getGame(); final Game game = src.getGame();
res.addAll(game.getStack().getCardsCastThisTurn()); res.addAll(game.getStack().getSpellsCastThisTurn());
res = CardLists.getValidCards(res, valid, src.getController(), src); res = CardLists.getValidCards(res, valid, src.getController(), src);
@@ -201,7 +201,7 @@ public final class CardUtil {
public static List<Card> getLastTurnCast(final String valid, final Card src) { public static List<Card> getLastTurnCast(final String valid, final Card src) {
List<Card> res = new ArrayList<Card>(); List<Card> res = new ArrayList<Card>();
final Game game = src.getGame(); final Game game = src.getGame();
res.addAll(game.getStack().getCardsCastLastTurn()); res.addAll(game.getStack().getSpellsCastLastTurn());
res = CardLists.getValidCards(res, valid, src.getController(), src); res = CardLists.getValidCards(res, valid, src.getController(), src);

View File

@@ -59,7 +59,6 @@ public class Combat {
// List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW) // List holds creatures who have dealt 1st strike damage to disallow them deal damage on regular basis (unless they have double-strike KW)
private List<Card> combatantsThatDealtFirstStrikeDamage = Lists.newArrayList(); private List<Card> combatantsThatDealtFirstStrikeDamage = Lists.newArrayList();
public Combat(Player attacker) { public Combat(Player attacker) {
playerWhoAttacks = attacker; playerWhoAttacks = attacker;
@@ -83,7 +82,7 @@ public class Combat {
public final List<GameEntity> getDefendersControlledBy(Player who) { public final List<GameEntity> getDefendersControlledBy(Player who) {
List<GameEntity> res = Lists.newArrayList(); List<GameEntity> res = Lists.newArrayList();
for(GameEntity ge : attackableEntries) { for (GameEntity ge : attackableEntries) {
// if defender is the player himself or his cards // if defender is the player himself or his cards
if (ge == who || ge instanceof Card && ((Card) ge).getController() == who) if (ge == who || ge instanceof Card && ((Card) ge).getController() == who)
res.add(ge); res.add(ge);
@@ -107,7 +106,7 @@ public class Combat {
public final List<Card> getAttackersOf(GameEntity defender) { public final List<Card> getAttackersOf(GameEntity defender) {
List<Card> result = new ArrayList<Card>(); List<Card> result = new ArrayList<Card>();
for(AttackingBand v : attackedByBands.get(defender)) { for (AttackingBand v : attackedByBands.get(defender)) {
result.addAll(v.getAttackers()); result.addAll(v.getAttackers());
} }
return result; return result;
@@ -133,7 +132,8 @@ public class Combat {
if (band == null || !attackersOfDefender.contains(band)) { if (band == null || !attackersOfDefender.contains(band)) {
band = new AttackingBand(c, defender); band = new AttackingBand(c, defender);
attackersOfDefender.add(band); attackersOfDefender.add(band);
} else { }
else {
band.addAttacker(c); band.addAttacker(c);
} }
} }
@@ -143,9 +143,10 @@ public class Combat {
} }
public final GameEntity getDefenderByAttacker(final AttackingBand c) { public final GameEntity getDefenderByAttacker(final AttackingBand c) {
for(Entry<GameEntity, AttackingBand> e : attackedByBands.entries()) { for (Entry<GameEntity, AttackingBand> e : attackedByBands.entries()) {
if ( e.getValue() == c ) if (e.getValue() == c) {
return e.getKey(); return e.getKey();
}
} }
return null; return null;
} }
@@ -167,8 +168,8 @@ public class Combat {
// takes LKI into consideration, should use it at all times (though a single iteration over multimap seems faster) // takes LKI into consideration, should use it at all times (though a single iteration over multimap seems faster)
public final AttackingBand getBandOfAttacker(final Card c) { public final AttackingBand getBandOfAttacker(final Card c) {
for(AttackingBand ab : attackedByBands.values()) { for (AttackingBand ab : attackedByBands.values()) {
if ( ab.contains(c) ) if (ab.contains(c))
return ab; return ab;
} }
CombatLki lki = lkiCache.get(c); CombatLki lki = lkiCache.get(c);
@@ -186,16 +187,19 @@ public class Combat {
public boolean isAttacking(Card card, GameEntity defender) { public boolean isAttacking(Card card, GameEntity defender) {
AttackingBand ab = getBandOfAttacker(card); AttackingBand ab = getBandOfAttacker(card);
for(Entry<GameEntity, AttackingBand> ee : attackedByBands.entries()) for (Entry<GameEntity, AttackingBand> ee : attackedByBands.entries()) {
if ( ee.getValue() == ab ) if (ee.getValue() == ab) {
return ee.getKey() == defender; return ee.getKey() == defender;
}
}
return false; return false;
} }
public final List<Card> getAttackers() { public final List<Card> getAttackers() {
List<Card> result = Lists.newArrayList(); List<Card> result = Lists.newArrayList();
for(AttackingBand ab : attackedByBands.values()) for (AttackingBand ab : attackedByBands.values()) {
result.addAll(ab.getAttackers()); result.addAll(ab.getAttackers());
}
return result; return result;
} }
@@ -230,7 +234,7 @@ public class Combat {
public final void removeBlockAssignment(final Card attacker, final Card blocker) { public final void removeBlockAssignment(final Card attacker, final Card blocker) {
AttackingBand band = getBandOfAttacker(attacker); AttackingBand band = getBandOfAttacker(attacker);
Collection<Card> cc = blockedBands.get(band); Collection<Card> cc = blockedBands.get(band);
if( cc != null) if (cc != null)
cc.remove(blocker); cc.remove(blocker);
} }
@@ -242,8 +246,8 @@ public class Combat {
public final List<Card> getAllBlockers() { public final List<Card> getAllBlockers() {
List<Card> result = new ArrayList<Card>(); List<Card> result = new ArrayList<Card>();
for(Card blocker : blockedBands.values()) { for (Card blocker : blockedBands.values()) {
if(!result.contains(blocker)) if (!result.contains(blocker))
result.add(blocker); result.add(blocker);
} }
return result; return result;
@@ -254,19 +258,19 @@ public class Combat {
return blockers == null ? Lists.<Card>newArrayList() : Lists.newArrayList(blockers); return blockers == null ? Lists.<Card>newArrayList() : Lists.newArrayList(blockers);
} }
public final List<Card> getAttackersBlockedBy(final Card blocker) { public final List<Card> getAttackersBlockedBy(final Card blocker) {
List<Card> blocked = new ArrayList<Card>(); List<Card> blocked = new ArrayList<Card>();
for(Entry<AttackingBand, Card> s : blockedBands.entries()) { for (Entry<AttackingBand, Card> s : blockedBands.entries()) {
if (s.getValue().equals(blocker)) if (s.getValue().equals(blocker)) {
blocked.addAll(s.getKey().getAttackers()); blocked.addAll(s.getKey().getAttackers());
}
} }
return blocked; return blocked;
} }
public final List<AttackingBand> getAttackingBandsBlockedBy(Card blocker) { public final List<AttackingBand> getAttackingBandsBlockedBy(Card blocker) {
List<AttackingBand> bands = Lists.newArrayList(); List<AttackingBand> bands = Lists.newArrayList();
for( Entry<AttackingBand, Card> kv : blockedBands.entries()) { for (Entry<AttackingBand, Card> kv : blockedBands.entries()) {
if (kv.getValue().equals(blocker)) if (kv.getValue().equals(blocker))
bands.add(kv.getKey()); bands.add(kv.getKey());
} }
@@ -277,9 +281,11 @@ public class Combat {
Card attacker = source; Card attacker = source;
if (source.isAura()) { if (source.isAura()) {
attacker = source.getEnchantingCard(); attacker = source.getEnchantingCard();
} else if (source.isEquipment()) { }
else if (source.isEquipment()) {
attacker = source.getEquipping(); attacker = source.getEquipping();
} else if (source.isFortification()) { }
else if (source.isFortification()) {
attacker = source.getFortifying(); attacker = source.getFortifying();
} }
@@ -292,16 +298,16 @@ public class Combat {
List<Pair<Card, List<Card>>> blockersNeedManualOrdering = new ArrayList<>(); List<Pair<Card, List<Card>>> blockersNeedManualOrdering = new ArrayList<>();
for(AttackingBand band : attackedByBands.values()) for (AttackingBand band : attackedByBands.values())
{ {
if (band.isEmpty()) continue; if (band.isEmpty()) continue;
Collection<Card> blockers = blockedBands.get(band); Collection<Card> blockers = blockedBands.get(band);
if ( blockers == null || blockers.isEmpty() ) if (blockers == null || blockers.isEmpty())
continue; continue;
for(Card attacker : band.getAttackers()) { for (Card attacker : band.getAttackers()) {
if ( blockers.size() <= 1 ) if (blockers.size() <= 1)
blockersOrderedForDamageAssignment.put(attacker, Lists.newArrayList(blockers)); blockersOrderedForDamageAssignment.put(attacker, Lists.newArrayList(blockers));
else // process it a bit later else // process it a bit later
blockersNeedManualOrdering.add(Pair.of(attacker, (List<Card>)blockers)); // we know there's a list blockersNeedManualOrdering.add(Pair.of(attacker, (List<Card>)blockers)); // we know there's a list
@@ -309,7 +315,7 @@ public class Combat {
} }
// brought this out of iteration on bands to avoid concurrency problems // brought this out of iteration on bands to avoid concurrency problems
for(Pair<Card, List<Card>> pair : blockersNeedManualOrdering){ for (Pair<Card, List<Card>> pair : blockersNeedManualOrdering){
// Damage Ordering needs to take cards like Melee into account, is that happening? // Damage Ordering needs to take cards like Melee into account, is that happening?
List<Card> orderedBlockers = playerWhoAttacks.getController().orderBlockers(pair.getLeft(), pair.getRight()); // we know there's a list List<Card> orderedBlockers = playerWhoAttacks.getController().orderBlockers(pair.getLeft(), pair.getRight()); // we know there's a list
blockersOrderedForDamageAssignment.put(pair.getLeft(), orderedBlockers); blockersOrderedForDamageAssignment.put(pair.getLeft(), orderedBlockers);
@@ -358,7 +364,7 @@ public class Combat {
blockersOrderedForDamageAssignment.remove(c); blockersOrderedForDamageAssignment.remove(c);
Collection<Card> blockers = blockedBands.get(ab); Collection<Card> blockers = blockedBands.get(ab);
if ( blockers != null ) { if (blockers != null) {
for (Card b : blockers) { for (Card b : blockers) {
// Clear removed attacker from assignment order // Clear removed attacker from assignment order
if (this.attackersOrderedForDamageAssignment.containsKey(b)) { if (this.attackersOrderedForDamageAssignment.containsKey(b)) {
@@ -372,7 +378,7 @@ public class Combat {
// removes references to this defender from all indices and orders // removes references to this defender from all indices and orders
private void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) { private void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) {
this.attackersOrderedForDamageAssignment.remove(c); this.attackersOrderedForDamageAssignment.remove(c);
for(Card atk : bandBeingBlocked.getAttackers()) { for (Card atk : bandBeingBlocked.getAttackers()) {
if (this.blockersOrderedForDamageAssignment.containsKey(atk)) { if (this.blockersOrderedForDamageAssignment.containsKey(atk)) {
this.blockersOrderedForDamageAssignment.get(atk).remove(c); this.blockersOrderedForDamageAssignment.get(atk).remove(c);
} }
@@ -388,49 +394,52 @@ public class Combat {
} }
// if not found in attackers, look for this card in blockers // if not found in attackers, look for this card in blockers
for(Entry<AttackingBand, Card> be : blockedBands.entries()) { for (Entry<AttackingBand, Card> be : blockedBands.entries()) {
if(be.getValue().equals(c)) { if (be.getValue().equals(c)) {
unregisterDefender(c, be.getKey()); unregisterDefender(c, be.getKey());
} }
} }
// remove card from map // remove card from map
while(blockedBands.values().remove(c)); while(blockedBands.values().remove(c));
} // removeFromCombat() } // removeFromCombat()
public final void removeAbsentCombatants() { public final boolean removeAbsentCombatants() {
// iterate all attackers and remove them // iterate all attackers and remove them
List<Card> missingCombatants = new ArrayList<>(); List<Card> missingCombatants = new ArrayList<>();
for(Entry<GameEntity, AttackingBand> ee : attackedByBands.entries()) { for (Entry<GameEntity, AttackingBand> ee : attackedByBands.entries()) {
List<Card> atk = ee.getValue().getAttackers(); List<Card> atk = ee.getValue().getAttackers();
for(int i = atk.size() - 1; i >= 0; i--) { // might remove items from collection, so no iterators for (int i = atk.size() - 1; i >= 0; i--) { // might remove items from collection, so no iterators
Card c = atk.get(i); Card c = atk.get(i);
if ( !c.isInPlay() || !c.isCreature() ) { if (!c.isInPlay() || !c.isCreature()) {
missingCombatants.add(c); missingCombatants.add(c);
} }
} }
} }
for(Entry<AttackingBand, Card> be : blockedBands.entries()) { for (Entry<AttackingBand, Card> be : blockedBands.entries()) {
Card blocker = be.getValue(); Card blocker = be.getValue();
if ( !blocker.isInPlay() || !blocker.isCreature() ) { if (!blocker.isInPlay() || !blocker.isCreature()) {
missingCombatants.add(blocker); missingCombatants.add(blocker);
} }
} }
if (missingCombatants.isEmpty()) { return false; }
for (Card c : missingCombatants) { for (Card c : missingCombatants) {
removeFromCombat(c); removeFromCombat(c);
} }
return true;
} }
// Call this method right after turn-based action of declare blockers has been performed // Call this method right after turn-based action of declare blockers has been performed
public final void fireTriggersForUnblockedAttackers() { public final void fireTriggersForUnblockedAttackers() {
for(AttackingBand ab : attackedByBands.values()) { for (AttackingBand ab : attackedByBands.values()) {
Collection<Card> blockers = blockedBands.get(ab); Collection<Card> blockers = blockedBands.get(ab);
boolean isBlocked = blockers != null && !blockers.isEmpty(); boolean isBlocked = blockers != null && !blockers.isEmpty();
ab.setBlocked(isBlocked); ab.setBlocked(isBlocked);
if (!isBlocked ) if (!isBlocked) {
for (Card attacker : ab.getAttackers()) { for (Card attacker : ab.getAttackers()) {
// Run Unblocked Trigger // Run Unblocked Trigger
final HashMap<String, Object> runParams = new HashMap<String, Object>(); final HashMap<String, Object> runParams = new HashMap<String, Object>();
@@ -438,6 +447,7 @@ public class Combat {
runParams.put("Defender",this.getDefenderByAttacker(attacker)); runParams.put("Defender",this.getDefenderByAttacker(attacker));
attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false); attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false);
} }
}
} }
} }
@@ -473,12 +483,11 @@ public class Combat {
} }
} }
} }
return assignedDamage; return assignedDamage;
} }
private final boolean assignAttackersDamage(boolean firstStrikeDamage) { private final boolean assignAttackersDamage(boolean firstStrikeDamage) {
// Assign damage by Attackers // Assign damage by Attackers
this.defendingDamageMap.clear(); // this should really happen in deal damage this.defendingDamageMap.clear(); // this should really happen in deal damage
List<Card> orderedBlockers = null; List<Card> orderedBlockers = null;
final List<Card> attackers = this.getAttackers(); final List<Card> attackers = this.getAttackers();
@@ -511,27 +520,28 @@ public class Combat {
if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked if (trampler || !band.isBlocked()) { // this is called after declare blockers, no worries 'bout nulls in isBlocked
this.addDefendingDamage(damageDealt, attacker); this.addDefendingDamage(damageDealt, attacker);
} // No damage happens if blocked but no blockers left } // No damage happens if blocked but no blockers left
} else { }
else {
GameEntity defender = getDefenderByAttacker(band); GameEntity defender = getDefenderByAttacker(band);
Player assigningPlayer = this.getAttackingPlayer(); Player assigningPlayer = this.getAttackingPlayer();
// Defensive Formation is very similar to Banding with Blockers // Defensive Formation is very similar to Banding with Blockers
// It allows the defending player to assign damage instead of the attacking player // It allows the defending player to assign damage instead of the attacking player
if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) { if (defender instanceof Player && defender.hasKeyword("You assign combat damage of each creature attacking you.")) {
assigningPlayer = (Player)defender; assigningPlayer = (Player)defender;
} else if ( AttackingBand.isValidBand(orderedBlockers, true)){ }
else if (AttackingBand.isValidBand(orderedBlockers, true)){
assigningPlayer = orderedBlockers.get(0).getController(); assigningPlayer = orderedBlockers.get(0).getController();
} }
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers, damageDealt, defender, this.getAttackingPlayer() != assigningPlayer); Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers, damageDealt, defender, this.getAttackingPlayer() != assigningPlayer);
for (Entry<Card, Integer> dt : map.entrySet()) { for (Entry<Card, Integer> dt : map.entrySet()) {
if( dt.getKey() == null) { if (dt.getKey() == null) {
if (dt.getValue() > 0) if (dt.getValue() > 0)
addDefendingDamage(dt.getValue(), attacker); addDefendingDamage(dt.getValue(), attacker);
} else { } else {
dt.getKey().addAssignedDamage(dt.getValue(), attacker); dt.getKey().addAssignedDamage(dt.getValue(), attacker);
} }
} }
} // if !hasFirstStrike ... } // if !hasFirstStrike ...
} // for } // for
return assignedDamage; return assignedDamage;
@@ -540,12 +550,12 @@ public class Combat {
private final boolean dealDamageThisPhase(Card combatant, boolean firstStrikeDamage) { private final boolean dealDamageThisPhase(Card combatant, boolean firstStrikeDamage) {
// During first strike damage, double strike and first strike deal damage // During first strike damage, double strike and first strike deal damage
// During regular strike damage, double strike and anyone who hasn't dealt damage deal damage // During regular strike damage, double strike and anyone who hasn't dealt damage deal damage
if (combatant.hasDoubleStrike()) if (combatant.hasDoubleStrike()) {
return true; return true;
}
if (firstStrikeDamage && combatant.hasFirstStrike()) if (firstStrikeDamage && combatant.hasFirstStrike()) {
return true; return true;
}
return !firstStrikeDamage && !this.combatantsThatDealtFirstStrikeDamage.contains(combatant); return !firstStrikeDamage && !this.combatantsThatDealtFirstStrikeDamage.contains(combatant);
} }
@@ -556,13 +566,13 @@ public class Combat {
if (ge instanceof Card) { if (ge instanceof Card) {
final Card planeswalker = (Card) ge; final Card planeswalker = (Card) ge;
planeswalker.addAssignedDamage(n, source); planeswalker.addAssignedDamage(n, source);
return; return;
} }
if (!this.defendingDamageMap.containsKey(source)) { if (!this.defendingDamageMap.containsKey(source)) {
this.defendingDamageMap.put(source, n); this.defendingDamageMap.put(source, n);
} else { }
else {
this.defendingDamageMap.put(source, this.defendingDamageMap.get(source) + n); this.defendingDamageMap.put(source, this.defendingDamageMap.get(source) + n);
} }
} }
@@ -577,14 +587,8 @@ public class Combat {
return assignedDamage; return assignedDamage;
} }
/**
* <p>
* dealAssignedDamage.
* </p>
*/
public void dealAssignedDamage() { public void dealAssignedDamage() {
// This function handles both Regular and First Strike combat assignment // This function handles both Regular and First Strike combat assignment
final HashMap<Card, Integer> defMap = this.defendingDamageMap; final HashMap<Card, Integer> defMap = this.defendingDamageMap;
final HashMap<GameEntity, List<Card>> wasDamaged = new HashMap<GameEntity, List<Card>>(); final HashMap<GameEntity, List<Card>> wasDamaged = new HashMap<GameEntity, List<Card>>();
@@ -600,11 +604,13 @@ public class Combat {
wasDamaged.put(defender, l); wasDamaged.put(defender, l);
} }
} }
} else if (defender instanceof Card) { // planeswalker }
else if (defender instanceof Card) { // planeswalker
if (((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey())) { if (((Card) defender).getController().addCombatDamage(entry.getValue(), entry.getKey())) {
if (wasDamaged.containsKey(defender)) { if (wasDamaged.containsKey(defender)) {
wasDamaged.get(defender).add(entry.getKey()); wasDamaged.get(defender).add(entry.getKey());
} else { }
else {
List<Card> l = new ArrayList<Card>(); List<Card> l = new ArrayList<Card>();
l.add(entry.getKey()); l.add(entry.getKey());
wasDamaged.put(defender, l); wasDamaged.put(defender, l);
@@ -649,59 +655,42 @@ public class Combat {
runParams.put("DamageTarget", ge); runParams.put("DamageTarget", ge);
ge.getGame().getTriggerHandler().runTrigger(TriggerType.CombatDamageDoneOnce, runParams, false); ge.getGame().getTriggerHandler().runTrigger(TriggerType.CombatDamageDoneOnce, runParams, false);
} }
// This was deeper before, but that resulted in the stack entry acting like before.
// This was deeper before, but that resulted in the stack entry acting
// like before.
} }
/**
* <p>
* isUnblocked.
* </p>
*
* @param att
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
public final boolean isUnblocked(final Card att) { public final boolean isUnblocked(final Card att) {
AttackingBand band = getBandOfAttacker(att); AttackingBand band = getBandOfAttacker(att);
return band == null ? false : Boolean.FALSE.equals(band.isBlocked()); return band == null ? false : Boolean.FALSE.equals(band.isBlocked());
} }
/**
* <p>
* getUnblockedAttackers.
* </p>
*
* @return an array of {@link forge.game.card.Card} objects.
*/
public final List<Card> getUnblockedAttackers() { public final List<Card> getUnblockedAttackers() {
List<Card> unblocked = new ArrayList<Card>(); List<Card> unblocked = new ArrayList<Card>();
for (AttackingBand ab : attackedByBands.values()) for (AttackingBand ab : attackedByBands.values()) {
if ( Boolean.TRUE.equals(ab.isBlocked()) ) if (Boolean.TRUE.equals(ab.isBlocked())) {
unblocked.addAll(ab.getAttackers()); unblocked.addAll(ab.getAttackers());
}
}
return unblocked; return unblocked;
} }
public boolean isPlayerAttacked(Player who) { public boolean isPlayerAttacked(Player who) {
for(GameEntity defender : attackedByBands.keySet() ) { for (GameEntity defender : attackedByBands.keySet()) {
Card defenderAsCard = defender instanceof Card ? (Card)defender : null; Card defenderAsCard = defender instanceof Card ? (Card)defender : null;
if ((null != defenderAsCard && defenderAsCard.getController() != who ) || if ((null != defenderAsCard && defenderAsCard.getController() != who) ||
(null == defenderAsCard && defender != who) ) (null == defenderAsCard && defender != who)) {
continue; // defender is not related to player 'who' continue; // defender is not related to player 'who'
}
for(AttackingBand ab : attackedByBands.get(defender)) { for (AttackingBand ab : attackedByBands.get(defender)) {
if ( !ab.isEmpty() ) if (!ab.isEmpty()) {
return true; return true;
}
} }
} }
return false; return false;
} }
public boolean isBlocking(Card blocker) { public boolean isBlocking(Card blocker) {
if ( !blocker.isInPlay() ) { if (!blocker.isInPlay()) {
CombatLki lki = lkiCache.get(blocker); CombatLki lki = lkiCache.get(blocker);
return null != lki && !lki.isAttacker; // was blocking something anyway return null != lki && !lki.isAttacker; // was blocking something anyway
} }
@@ -710,7 +699,7 @@ public class Combat {
public boolean isBlocking(Card blocker, Card attacker) { public boolean isBlocking(Card blocker, Card attacker) {
AttackingBand ab = getBandOfAttacker(attacker); AttackingBand ab = getBandOfAttacker(attacker);
if ( !blocker.isInPlay() ) { if (!blocker.isInPlay()) {
CombatLki lki = lkiCache.get(blocker); CombatLki lki = lkiCache.get(blocker);
return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band return null != lki && !lki.isAttacker && lki.relatedBands.contains(ab); // was blocking that very band
} }
@@ -718,21 +707,17 @@ public class Combat {
return blockers != null && blockers.contains(blocker); return blockers != null && blockers.contains(blocker);
} }
/**
* TODO: Write javadoc for this method.
* @param lastKnownInfo
*/
public void saveLKI(Card lastKnownInfo) { public void saveLKI(Card lastKnownInfo) {
List<AttackingBand> attackersBlocked = null; List<AttackingBand> attackersBlocked = null;
AttackingBand attackingBand = getBandOfAttacker(lastKnownInfo); AttackingBand attackingBand = getBandOfAttacker(lastKnownInfo);
boolean isAttacker = attackingBand != null; boolean isAttacker = attackingBand != null;
if ( !isAttacker ) { if (!isAttacker) {
attackersBlocked= getAttackingBandsBlockedBy(lastKnownInfo); attackersBlocked= getAttackingBandsBlockedBy(lastKnownInfo);
if ( attackersBlocked.isEmpty() ) if (attackersBlocked.isEmpty()) {
return; // card was not even in combat return; // card was not even in combat
}
} }
List<AttackingBand> relatedBands = isAttacker ? Lists.newArrayList(attackingBand) : attackersBlocked; List<AttackingBand> relatedBands = isAttacker ? Lists.newArrayList(attackingBand) : attackersBlocked;
lkiCache.put(lastKnownInfo, new CombatLki(isAttacker, relatedBands)); lkiCache.put(lastKnownInfo, new CombatLki(isAttacker, relatedBands));
} }
}
} // Class Combat

View File

@@ -227,7 +227,7 @@ public class ManaCostAdjustment {
if (activator == null ) { if (activator == null ) {
return; return;
} }
if (CardLists.filterControlledBy(activator.getGame().getStack().getCardsCastThisTurn(), if (CardLists.filterControlledBy(activator.getGame().getStack().getSpellsCastThisTurn(),
activator).size() > 0) { activator).size() > 0) {
return; return;
} }
@@ -368,7 +368,7 @@ public class ManaCostAdjustment {
if (activator == null ) { if (activator == null ) {
return; return;
} }
if (CardLists.filterControlledBy(activator.getGame().getStack().getCardsCastThisTurn(), if (CardLists.filterControlledBy(activator.getGame().getStack().getSpellsCastThisTurn(),
activator).size() > 0) { activator).size() > 0) {
return; return;
} }

View File

@@ -57,15 +57,12 @@ import java.util.*;
* @version $Id: PhaseHandler.java 13001 2012-01-08 12:25:25Z Sloth $ * @version $Id: PhaseHandler.java 13001 2012-01-08 12:25:25Z Sloth $
*/ */
public class PhaseHandler implements java.io.Serializable { public class PhaseHandler implements java.io.Serializable {
/** Constant <code>serialVersionUID=5207222278370963197L</code>. */
private static final long serialVersionUID = 5207222278370963197L; private static final long serialVersionUID = 5207222278370963197L;
// Start turn at 0, since we start even before first untap // Start turn at 0, since we start even before first untap
private PhaseType phase = null; private PhaseType phase = null;
private int turn = 0; private int turn = 0;
private final transient Stack<ExtraTurn> extraTurns = new Stack<ExtraTurn>(); private final transient Stack<ExtraTurn> extraTurns = new Stack<ExtraTurn>();
private final transient Map<PhaseType, Stack<PhaseType>> extraPhases = new HashMap<PhaseType, Stack<PhaseType>>(); private final transient Map<PhaseType, Stack<PhaseType>> extraPhases = new HashMap<PhaseType, Stack<PhaseType>>();
@@ -93,97 +90,70 @@ public class PhaseHandler implements java.io.Serializable {
private final transient Game game; private final transient Game game;
public PhaseHandler(final Game game0) { public PhaseHandler(final Game game0) {
game = game0; game = game0;
} }
public final PhaseType getPhase() {
return phase;
}
private final void setPhase(final PhaseType phase0) {
if (phase == phase0) { return; }
phase = phase0;
game.updatePhaseForView();
}
public final int getTurn() {
return turn;
}
public final boolean isPlayerTurn(final Player player) { public final boolean isPlayerTurn(final Player player) {
return player.equals(this.playerTurn); return player.equals(playerTurn);
} }
private final void setPlayerTurn(final Player s) {
this.playerTurn = s;
this.setPriority(s);
}
/**
* <p>
* Getter for the field <code>playerTurn</code>.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
public final Player getPlayerTurn() { public final Player getPlayerTurn() {
return this.playerTurn; return playerTurn;
}
private final void setPlayerTurn(final Player playerTurn0) {
if (playerTurn == playerTurn0) { return; }
playerTurn = playerTurn0;
game.updatePlayerTurnForView();
setPriority(playerTurn);
} }
// priority player
/**
* <p>
* getPriorityPlayer.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
public final Player getPriorityPlayer() { public final Player getPriorityPlayer() {
return this.pPlayerPriority; return pPlayerPriority;
} }
/**
* <p>
* setPriority.
* </p>
*
* @param p
* a {@link forge.game.player.Player} object.
*/
public final void setPriority(final Player p) { public final void setPriority(final Player p) {
this.pFirstPriority = p; pFirstPriority = p;
this.pPlayerPriority = p; pPlayerPriority = p;
} }
/**
* <p>
* resetPriority.
* </p>
*/
public final void resetPriority() { public final void resetPriority() {
this.setPriority(this.playerTurn); setPriority(playerTurn);
} }
/**
* <p>
* inCombat.
* </p>
*
* @return a boolean.
*/
public final boolean inCombat() { return combat != null; } public final boolean inCombat() { return combat != null; }
public final Combat getCombat() { return this.combat; } public final Combat getCombat() { return combat; }
private void advanceToNextPhase() { private void advanceToNextPhase() {
PhaseType oldPhase = phase; PhaseType oldPhase = phase;
if (this.bRepeatCleanup) { // for when Cleanup needs to repeat itself if (bRepeatCleanup) { // for when Cleanup needs to repeat itself
this.bRepeatCleanup = false; bRepeatCleanup = false;
} }
else { else {
// If the phase that's ending has a stack of additional phases // 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 // Take the LIFO one and move to that instead of the normal one
if (this.extraPhases.containsKey(phase)) { if (extraPhases.containsKey(phase)) {
PhaseType nextPhase = this.extraPhases.get(phase).pop(); PhaseType nextPhase = extraPhases.get(phase).pop();
// If no more additional phases are available, remove it from the map // If no more additional phases are available, remove it from the map
// and let the next add, reput the key // and let the next add, reput the key
if (this.extraPhases.get(phase).isEmpty()) { if (extraPhases.get(phase).isEmpty()) {
this.extraPhases.remove(phase); extraPhases.remove(phase);
} }
this.phase = nextPhase; setPhase(nextPhase);
} }
else { else {
this.phase = PhaseType.getNext(phase); setPhase(PhaseType.getNext(phase));
} }
} }
@@ -191,8 +161,9 @@ public class PhaseHandler implements java.io.Serializable {
String phaseType = oldPhase == phase ? "Repeat" : phase == PhaseType.getNext(oldPhase) ? "" : "Additional"; String phaseType = oldPhase == phase ? "Repeat" : phase == PhaseType.getNext(oldPhase) ? "" : "Additional";
if (this.phase == PhaseType.UNTAP) { if (phase == PhaseType.UNTAP) {
this.turn++; turn++;
game.updateTurnForView();
game.fireEvent(new GameEventTurnBegan(playerTurn, turn)); game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
// Tokens starting game in play should suffer from Sum. Sickness // Tokens starting game in play should suffer from Sum. Sickness
@@ -210,7 +181,7 @@ public class PhaseHandler implements java.io.Serializable {
playerTurn.setNumPowerSurgeLands(lands.size()); playerTurn.setNumPowerSurgeLands(lands.size());
} }
game.fireEvent(new GameEventTurnPhase(this.getPlayerTurn(), this.getPhase(), phaseType)); game.fireEvent(new GameEventTurnPhase(playerTurn, phase, phaseType));
} }
private boolean isSkippingPhase(PhaseType phase) { private boolean isSkippingPhase(PhaseType phase) {
@@ -224,10 +195,10 @@ public class PhaseHandler implements java.io.Serializable {
return playerTurn.hasKeyword("Skip the untap step of this turn.") || playerTurn.hasKeyword("Skip your untap step."); return playerTurn.hasKeyword("Skip the untap step of this turn.") || playerTurn.hasKeyword("Skip your untap step.");
case UPKEEP: case UPKEEP:
return getPlayerTurn().hasKeyword("Skip your upkeep step."); return playerTurn.hasKeyword("Skip your upkeep step.");
case DRAW: case DRAW:
return getPlayerTurn().isSkippingDraw() || getTurn() == 1 && game.getPlayers().size() == 2; return playerTurn.isSkippingDraw() || turn == 1 && game.getPlayers().size() == 2;
case COMBAT_BEGIN: case COMBAT_BEGIN:
case COMBAT_DECLARE_ATTACKERS: case COMBAT_DECLARE_ATTACKERS:
@@ -236,7 +207,7 @@ public class PhaseHandler implements java.io.Serializable {
case COMBAT_DECLARE_BLOCKERS: case COMBAT_DECLARE_BLOCKERS:
case COMBAT_FIRST_STRIKE_DAMAGE: case COMBAT_FIRST_STRIKE_DAMAGE:
case COMBAT_DAMAGE: case COMBAT_DAMAGE:
return !this.inCombat(); return !inCombat();
default: default:
return false; return false;
@@ -256,7 +227,7 @@ public class PhaseHandler implements java.io.Serializable {
} }
else { else {
// Perform turn-based actions // Perform turn-based actions
switch(this.getPhase()) { switch (phase) {
case UNTAP: case UNTAP:
givePriorityToPlayer = false; givePriorityToPlayer = false;
game.getUntap().executeUntil(playerTurn); game.getUntap().executeUntil(playerTurn);
@@ -264,19 +235,19 @@ public class PhaseHandler implements java.io.Serializable {
break; break;
case UPKEEP: case UPKEEP:
this.nUpkeepsThisTurn++; nUpkeepsThisTurn++;
this.nUpkeepsThisGame++; nUpkeepsThisGame++;
game.getUpkeep().executeUntil(this.getPlayerTurn()); game.getUpkeep().executeUntil(playerTurn);
game.getUpkeep().executeAt(); game.getUpkeep().executeAt();
break; break;
case DRAW: case DRAW:
this.getPlayerTurn().drawCard(); playerTurn.drawCard();
break; break;
case MAIN1: case MAIN1:
if (this.getPlayerTurn().isArchenemy() && this.isPreCombatMain()) { if (playerTurn.isArchenemy() && isPreCombatMain()) {
this.getPlayerTurn().setSchemeInMotion(); playerTurn.setSchemeInMotion();
} }
break; break;
@@ -294,6 +265,7 @@ public class PhaseHandler implements java.io.Serializable {
if (combat != null && combat.getAttackers().isEmpty() if (combat != null && combat.getAttackers().isEmpty()
&& !game.getTriggerHandler().hasDelayedTriggers()) { && !game.getTriggerHandler().hasDelayedTriggers()) {
combat = null; combat = null;
game.updateCombatForView();
} }
} }
@@ -308,11 +280,13 @@ public class PhaseHandler implements java.io.Serializable {
break; break;
case COMBAT_FIRST_STRIKE_DAMAGE: case COMBAT_FIRST_STRIKE_DAMAGE:
combat.removeAbsentCombatants(); if (combat.removeAbsentCombatants()) {
game.updateCombatForView();
}
// no first strikers, skip this step // no first strikers, skip this step
if (!combat.assignCombatDamage(true)) { if (!combat.assignCombatDamage(true)) {
this.givePriorityToPlayer = false; givePriorityToPlayer = false;
} }
else { else {
combat.dealAssignedDamage(); combat.dealAssignedDamage();
@@ -320,10 +294,12 @@ public class PhaseHandler implements java.io.Serializable {
break; break;
case COMBAT_DAMAGE: case COMBAT_DAMAGE:
combat.removeAbsentCombatants(); if (combat.removeAbsentCombatants()) {
game.updateCombatForView();
}
if (!combat.assignCombatDamage(false)) { if (!combat.assignCombatDamage(false)) {
this.givePriorityToPlayer = false; givePriorityToPlayer = false;
} }
else { else {
combat.dealAssignedDamage(); combat.dealAssignedDamage();
@@ -374,9 +350,9 @@ public class PhaseHandler implements java.io.Serializable {
player.onCleanupPhase(); player.onCleanupPhase();
player.getController().autoPassCancel(); // autopass won't wrap to next turn player.getController().autoPassCancel(); // autopass won't wrap to next turn
} }
this.getPlayerTurn().removeKeyword("Skip all combat phases of this turn."); playerTurn.removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(this.getNextTurn()); game.getCleanup().executeUntil(getNextTurn());
this.nUpkeepsThisTurn = 0; nUpkeepsThisTurn = 0;
// Rule 514.3 // Rule 514.3
givePriorityToPlayer = false; givePriorityToPlayer = false;
@@ -393,8 +369,8 @@ public class PhaseHandler implements java.io.Serializable {
if (!skipped) { if (!skipped) {
// 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", phase.nameForScripts);
runParams.put("Player", this.getPlayerTurn()); runParams.put("Player", playerTurn);
game.getTriggerHandler().runTrigger(TriggerType.Phase, runParams, false); game.getTriggerHandler().runTrigger(TriggerType.Phase, runParams, false);
} }
@@ -408,7 +384,6 @@ public class PhaseHandler implements java.io.Serializable {
} }
} }
private void onPhaseEnd() { 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()) {
@@ -428,19 +403,19 @@ public class PhaseHandler implements java.io.Serializable {
} }
} }
switch (this.phase) { switch (phase) {
case UNTAP: case UNTAP:
this.nCombatsThisTurn = 0; nCombatsThisTurn = 0;
break; break;
case UPKEEP: case UPKEEP:
for (Card c : game.getCardsIn(ZoneType.Battlefield)) { for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
c.getDamageHistory().setNotAttackedSinceLastUpkeepOf(this.getPlayerTurn()); c.getDamageHistory().setNotAttackedSinceLastUpkeepOf(playerTurn);
c.getDamageHistory().setNotBlockedSinceLastUpkeepOf(this.getPlayerTurn()); c.getDamageHistory().setNotBlockedSinceLastUpkeepOf(playerTurn);
c.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(this.getPlayerTurn()); c.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(playerTurn);
} }
game.getUpkeep().executeUntilEndOfPhase(this.getPlayerTurn()); game.getUpkeep().executeUntilEndOfPhase(playerTurn);
game.getUpkeep().registerUntilEndCommand(this.getPlayerTurn()); game.getUpkeep().registerUntilEndCommand(playerTurn);
break; break;
case COMBAT_END: case COMBAT_END:
@@ -451,7 +426,7 @@ public class PhaseHandler implements java.io.Serializable {
eventEndCombat = new GameEventCombatEnded(attackers, blockers); eventEndCombat = new GameEventCombatEnded(attackers, blockers);
} }
combat = null; combat = null;
this.getPlayerTurn().resetAttackedThisCombat(); playerTurn.resetAttackedThisCombat();
if (eventEndCombat != null) { if (eventEndCombat != null) {
game.fireEvent(eventEndCombat); game.fireEvent(eventEndCombat);
@@ -459,11 +434,11 @@ public class PhaseHandler implements java.io.Serializable {
break; break;
case CLEANUP: case CLEANUP:
this.bPreventCombatDamageThisTurn = false; bPreventCombatDamageThisTurn = false;
if (!this.bRepeatCleanup) { if (!bRepeatCleanup) {
this.setPlayerTurn(this.handleNextTurn()); setPlayerTurn(handleNextTurn());
} }
this.planarDiceRolledthisTurn = 0; planarDiceRolledthisTurn = 0;
// Play the End Turn sound // Play the End Turn sound
game.fireEvent(new GameEventTurnEnded()); game.fireEvent(new GameEventTurnEnded());
break; break;
@@ -498,7 +473,7 @@ public class PhaseHandler implements java.io.Serializable {
} }
} }
this.nCombatsThisTurn++; nCombatsThisTurn++;
// Prepare and fire event 'attackers declared' // Prepare and fire event 'attackers declared'
Multimap<GameEntity, Card> attackersMap = ArrayListMultimap.create(); Multimap<GameEntity, Card> attackersMap = ArrayListMultimap.create();
@@ -544,11 +519,10 @@ public class PhaseHandler implements java.io.Serializable {
} }
game.getTriggerHandler().resetActiveTriggers(); game.getTriggerHandler().resetActiveTriggers();
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged()); game.fireEvent(new GameEventCombatChanged());
} }
private void declareBlockersTurnBasedAction() { private void declareBlockersTurnBasedAction() {
Player p = playerTurn; Player p = playerTurn;
@@ -688,10 +662,10 @@ public class PhaseHandler implements java.io.Serializable {
a.getDamageHistory().setCreatureGotBlockedThisCombat(true); a.getDamageHistory().setCreatureGotBlockedThisCombat(true);
} }
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged()); game.fireEvent(new GameEventCombatChanged());
} }
private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) { private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) {
Cost blockCost = new Cost(ManaCost.ZERO, true); Cost blockCost = new Cost(ManaCost.ZERO, true);
// Sort abilities to apply them in proper order // Sort abilities to apply them in proper order
@@ -711,24 +685,11 @@ public class PhaseHandler implements java.io.Serializable {
return noCost || blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker); return noCost || blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker);
} }
/**
* Checks if is prevent combat damage this turn.
*
* @return true, if is prevent combat damage this turn
*/
public final boolean isPreventCombatDamageThisTurn() { public final boolean isPreventCombatDamageThisTurn() {
return this.bPreventCombatDamageThisTurn; return bPreventCombatDamageThisTurn;
} }
/**
* <p>
* handleNextTurn.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
private Player handleNextTurn() { private Player handleNextTurn() {
game.getStack().onNextTurn(); game.getStack().onNextTurn();
for (final Player p1 : game.getPlayers()) { for (final Player p1 : game.getPlayers()) {
@@ -762,35 +723,28 @@ public class PhaseHandler implements java.io.Serializable {
} }
} }
} }
return next; return next;
} }
/**
* <p>
* getNextActivePlayer.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
private Player getNextActivePlayer() { private Player getNextActivePlayer() {
ExtraTurn extraTurn = !extraTurns.isEmpty() ? extraTurns.pop() : null;
ExtraTurn extraTurn = !this.extraTurns.isEmpty() ? this.extraTurns.pop() : null; Player nextPlayer = extraTurn != null ? extraTurn.getPlayer() : game.getNextPlayerAfter(playerTurn);
Player nextPlayer = extraTurn != null ? extraTurn.getPlayer() : game.getNextPlayerAfter(this.getPlayerTurn());
if (extraTurn != null) { if (extraTurn != null) {
// The bottom of the extra turn stack is the normal turn // The bottom of the extra turn stack is the normal turn
nextPlayer.setExtraTurn(!this.extraTurns.isEmpty()); nextPlayer.setExtraTurn(!extraTurns.isEmpty());
if (nextPlayer.hasKeyword("If you would begin an extra turn, skip that turn instead.")) { if (nextPlayer.hasKeyword("If you would begin an extra turn, skip that turn instead.")) {
return getNextActivePlayer(); return getNextActivePlayer();
} }
} else }
else {
nextPlayer.setExtraTurn(false); nextPlayer.setExtraTurn(false);
}
if (nextPlayer.hasKeyword("Skip your next turn.")) { if (nextPlayer.hasKeyword("Skip your next turn.")) {
nextPlayer.removeKeyword("Skip your next turn."); nextPlayer.removeKeyword("Skip your next turn.");
if( null == extraTurn ) if( null == extraTurn )
this.setPlayerTurn(nextPlayer); setPlayerTurn(nextPlayer);
return getNextActivePlayer(); return getNextActivePlayer();
} }
@@ -808,7 +762,7 @@ public class PhaseHandler implements java.io.Serializable {
} }
crd.untap(); crd.untap();
if( null == extraTurn ) if( null == extraTurn )
this.setPlayerTurn(nextPlayer); setPlayerTurn(nextPlayer);
return getNextActivePlayer(); return getNextActivePlayer();
} }
} }
@@ -824,152 +778,55 @@ public class PhaseHandler implements java.io.Serializable {
nextPlayer.addKeyword("Schemes can't be set in motion this turn."); nextPlayer.addKeyword("Schemes can't be set in motion this turn.");
} }
} }
return nextPlayer; return nextPlayer;
} }
/** public final synchronized boolean is(final PhaseType phase0, final Player player0) {
* <p> return phase == phase0 && playerTurn.equals(player0);
* is.
* </p>
*
* @param phase
* a {@link java.lang.String} object.
* @param player
* a {@link forge.game.player.Player} object.
* @return a boolean.
*/
public final synchronized boolean is(final PhaseType phase, final Player player) {
return this.getPhase() == phase && this.getPlayerTurn().equals(player);
} }
/**
* <p>
* is.
* </p>
*
* @param phase0
* a {@link forge.game.phase.PhaseType} object.
* @return a boolean.
*/
public final synchronized boolean is(final PhaseType phase0) { public final synchronized boolean is(final PhaseType phase0) {
return this.getPhase() == phase0; return phase == phase0;
} }
/**
* <p>
* getPhase.
* </p>
*
* @return a {@link java.lang.String} object.
*/
public final PhaseType getPhase() {
return phase;
}
/**
* <p>
* Getter for the field <code>turn</code>.
* </p>
*
* @return a int.
*/
public final int getTurn() {
return this.turn;
}
/**
* <p>
* getNextTurn.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
public final Player getNextTurn() { public final Player getNextTurn() {
if (this.extraTurns.isEmpty()) { if (extraTurns.isEmpty()) {
return game.getNextPlayerAfter(this.getPlayerTurn()); return game.getNextPlayerAfter(playerTurn);
} }
return extraTurns.peek().getPlayer();
return this.extraTurns.peek().getPlayer();
} }
/**
* <p>
* addExtraTurn.
* </p>
*
* @param player
* a {@link forge.game.player.Player} object.
*/
public final ExtraTurn addExtraTurn(final Player player) { public final ExtraTurn addExtraTurn(final Player player) {
// use a stack to handle extra turns, make sure the bottom of the stack // use a stack to handle extra turns, make sure the bottom of the stack
// restores original turn order // restores original turn order
if (this.extraTurns.isEmpty()) { if (extraTurns.isEmpty()) {
this.extraTurns.push(new ExtraTurn(game.getNextPlayerAfter(this.getPlayerTurn()))); extraTurns.push(new ExtraTurn(game.getNextPlayerAfter(playerTurn)));
} }
return extraTurns.push(new ExtraTurn(player));
return this.extraTurns.push(new ExtraTurn(player));
} }
/**
* <p>
* addExtraPhase.
* </p>
*
*/
public final void addExtraPhase(final PhaseType afterPhase, final PhaseType extraPhase) { public final void addExtraPhase(final PhaseType afterPhase, final PhaseType extraPhase) {
// 500.8. Some effects can add phases to a turn. They do this by adding the phases directly after the specified phase. // 500.8. Some effects can add phases to a turn. They do this by adding the phases directly after the specified phase.
// If multiple extra phases are created after the same phase, the most recently created phase will occur first. // If multiple extra phases are created after the same phase, the most recently created phase will occur first.
if (!this.extraPhases.containsKey(afterPhase)) { if (!extraPhases.containsKey(afterPhase)) {
this.extraPhases.put(afterPhase, new Stack<PhaseType>()); extraPhases.put(afterPhase, new Stack<PhaseType>());
} }
extraPhases.get(afterPhase).push(extraPhase);
this.extraPhases.get(afterPhase).push(extraPhase);
} }
/**
* <p>
* isFirstCombat.
* </p>
*
* @return a boolean.
*/
public final boolean isFirstCombat() { public final boolean isFirstCombat() {
return (this.nCombatsThisTurn == 1); return (nCombatsThisTurn == 1);
} }
/**
* <p>
* isFirstUpkeep.
* </p>
*
* @return a boolean.
*/
public final boolean isFirstUpkeep() { public final boolean isFirstUpkeep() {
return (this.nUpkeepsThisTurn == 0); return (nUpkeepsThisTurn == 0);
} }
/**
* <p>
* isFirstUpkeepThisGame.
* </p>
*
* @return a boolean.
*/
public final boolean isFirstUpkeepThisGame() { public final boolean isFirstUpkeepThisGame() {
return (this.nUpkeepsThisGame == 0); return (nUpkeepsThisGame == 0);
} }
/**
* <p>
* isPreCombatMain.
* </p>
*
* @return a boolean.
*/
public final boolean isPreCombatMain() { public final boolean isPreCombatMain() {
return (this.nCombatsThisTurn == 0); return (nCombatsThisTurn == 0);
} }
private final static boolean DEBUG_PHASES = false; private final static boolean DEBUG_PHASES = false;
@@ -996,7 +853,7 @@ public class PhaseHandler implements java.io.Serializable {
sw.start(); sw.start();
} }
game.fireEvent(new GameEventPlayerPriority(getPlayerTurn(), getPhase(), getPriorityPlayer())); game.fireEvent(new GameEventPlayerPriority(playerTurn, phase, getPriorityPlayer()));
SpellAbility chosenSa = null; SpellAbility chosenSa = null;
int loopCount = 0; int loopCount = 0;
@@ -1017,7 +874,7 @@ public class PhaseHandler implements java.io.Serializable {
if (playerTurn.hasLost() && pPlayerPriority.equals(playerTurn) && pFirstPriority.equals(playerTurn)) { if (playerTurn.hasLost() && pPlayerPriority.equals(playerTurn) && pFirstPriority.equals(playerTurn)) {
// If the active player has lost, and they have priority, set the next player to have priority // If the active player has lost, and they have priority, set the next player to have priority
System.out.println("Active player is no longer in the game..."); System.out.println("Active player is no longer in the game...");
pPlayerPriority = game.getNextPlayerAfter(this.getPriorityPlayer()); pPlayerPriority = game.getNextPlayerAfter(getPriorityPlayer());
pFirstPriority = pPlayerPriority; pFirstPriority = pPlayerPriority;
} }
@@ -1045,13 +902,13 @@ public class PhaseHandler implements java.io.Serializable {
} }
} }
else if (DEBUG_PHASES){ else if (DEBUG_PHASES){
System.out.print(" >> (no priority given to " + this.getPriorityPlayer() + ")\n"); System.out.print(" >> (no priority given to " + getPriorityPlayer() + ")\n");
} }
// actingPlayer is the player who may act // actingPlayer is the player who may act
// the firstAction is the player who gained Priority First in this segment // the firstAction is the player who gained Priority First in this segment
// of Priority // of Priority
Player nextPlayer = game.getNextPlayerAfter(this.getPriorityPlayer()); Player nextPlayer = game.getNextPlayerAfter(getPriorityPlayer());
if (game.isGameOver() || nextPlayer == null) { return; } // conceded? if (game.isGameOver() || nextPlayer == null) { return; } // conceded?
@@ -1060,13 +917,13 @@ public class PhaseHandler implements java.io.Serializable {
if (pFirstPriority == nextPlayer) { if (pFirstPriority == nextPlayer) {
if (game.getStack().isEmpty()) { if (game.getStack().isEmpty()) {
if (playerTurn.hasLost()) { if (playerTurn.hasLost()) {
this.setPriority(game.getNextPlayerAfter(playerTurn)); setPriority(game.getNextPlayerAfter(playerTurn));
} else { } else {
this.setPriority(playerTurn); setPriority(playerTurn);
} }
// end phase // end phase
this.givePriorityToPlayer = true; givePriorityToPlayer = true;
onPhaseEnd(); onPhaseEnd();
advanceToNextPhase(); advanceToNextPhase();
onPhaseBegin(); onPhaseBegin();
@@ -1075,12 +932,13 @@ public class PhaseHandler implements java.io.Serializable {
} }
else { else {
// pass the priority to other player // pass the priority to other player
this.pPlayerPriority = nextPlayer; pPlayerPriority = nextPlayer;
} }
// If ever the karn's ultimate resolved // If ever the karn's ultimate resolved
if (game.getAge() == GameStage.RestartedByKarn) { if (game.getAge() == GameStage.RestartedByKarn) {
phase = null; setPhase(null);
game.updatePhaseForView();
game.fireEvent(new GameEventGameRestarted(playerTurn)); game.fireEvent(new GameEventGameRestarted(playerTurn));
return; return;
} }
@@ -1090,53 +948,50 @@ public class PhaseHandler implements java.io.Serializable {
// this is a hack for the setup game state mode, do not use outside of devSetupGameState code // 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 // as it avoids calling any of the phase effects that may be necessary in a less enforced context
public final void devModeSet(final PhaseType phase0, final Player player0) { public final void devModeSet(final PhaseType phase0, final Player player0) {
if (null != phase0) this.phase = phase0; if (phase0 != null) {
if (null != player0) { setPhase(phase0);
}
if (player0 != null) {
setPlayerTurn(player0); setPlayerTurn(player0);
} }
game.fireEvent(new GameEventTurnPhase(this.getPlayerTurn(), this.getPhase(), "")); game.fireEvent(new GameEventTurnPhase(playerTurn, phase, ""));
combat = null; // not-null can be created only when declare attackers phase begins combat = null; // not-null can be created only when declare attackers phase begins
} }
public final void endTurnByEffect() { public final void endTurnByEffect() {
this.combat = null; combat = null;
this.extraPhases.clear(); extraPhases.clear();
this.phase = PhaseType.CLEANUP; setPhase(PhaseType.CLEANUP);
this.onPhaseBegin(); onPhaseBegin();
} }
public final void setPreventCombatDamageThisTurn(final boolean b) { public final void setPreventCombatDamageThisTurn(final boolean b) {
this.bPreventCombatDamageThisTurn = true; bPreventCombatDamageThisTurn = true;
} }
/**
* @return the planarDiceRolledthisTurn
*/
public int getPlanarDiceRolledthisTurn() { public int getPlanarDiceRolledthisTurn() {
return planarDiceRolledthisTurn; return planarDiceRolledthisTurn;
} }
public void incPlanarDiceRolledthisTurn() { public void incPlanarDiceRolledthisTurn() {
this.planarDiceRolledthisTurn++; planarDiceRolledthisTurn++;
} }
public String debugPrintState(boolean hasPriority) { public String debugPrintState(boolean hasPriority) {
return String.format("%s's %s [%sP] %s", getPlayerTurn(), getPhase().nameForUi, hasPriority ? "+" : "-", getPriorityPlayer()); return String.format("%s's %s [%sP] %s", playerTurn, phase.nameForUi, hasPriority ? "+" : "-", getPriorityPlayer());
} }
// just to avoid exposing variable to oute classes // just to avoid exposing variable to outer classes
public void onStackResolved() { public void onStackResolved() {
givePriorityToPlayer = true; givePriorityToPlayer = true;
} }
public final void setPlayerDeclaresBlockers(Player player) { public final void setPlayerDeclaresAttackers(Player player) {
this.playerDeclaresBlockers = player; playerDeclaresAttackers = player;
} }
public final void setPlayerDeclaresAttackers(Player player) { public final void setPlayerDeclaresBlockers(Player player) {
this.playerDeclaresAttackers = player; playerDeclaresBlockers = player;
} }
public void endCombat() { public void endCombat() {

View File

@@ -70,7 +70,7 @@ public class StaticAbilityCantBeCast {
if (params.containsKey("NumLimitEachTurn") && activator != null) { if (params.containsKey("NumLimitEachTurn") && activator != null) {
int limit = Integer.parseInt(params.get("NumLimitEachTurn")); int limit = Integer.parseInt(params.get("NumLimitEachTurn"));
String valid = params.containsKey("ValidCard") ? params.get("ValidCard") : "Card"; String valid = params.containsKey("ValidCard") ? params.get("ValidCard") : "Card";
List<Card> thisTurnCast = CardLists.getValidCards(card.getGame().getStack().getCardsCastThisTurn(), List<Card> thisTurnCast = CardLists.getValidCards(card.getGame().getStack().getSpellsCastThisTurn(),
valid, card.getController(), card); valid, card.getController(), card);
if (CardLists.filterControlledBy(thisTurnCast, activator).size() < limit) { if (CardLists.filterControlledBy(thisTurnCast, activator).size() < limit) {
return false; return false;

View File

@@ -80,62 +80,31 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
private final Game game; private final Game game;
/**
* TODO: Write javadoc for Constructor.
* @param gameState
*/
public MagicStack(Game gameState) { public MagicStack(Game gameState) {
game = gameState; game = gameState;
} }
/**
* <p>
* isFrozen.
* </p>
*
* @return a boolean.
*/
public final boolean isFrozen() { public final boolean isFrozen() {
return this.frozen; return frozen;
} }
/**
* <p>
* Setter for the field <code>frozen</code>.
* </p>
*
* @param frozen0
* a boolean.
*/
public final void setFrozen(final boolean frozen0) { public final void setFrozen(final boolean frozen0) {
this.frozen = frozen0; frozen = frozen0;
} }
/**
* <p>
* reset.
* </p>
*/
public final void reset() { public final void reset() {
this.clear(); clear();
this.simultaneousStackEntryList.clear(); simultaneousStackEntryList.clear();
this.frozen = false; frozen = false;
this.lastTurnCast.clear(); lastTurnCast.clear();
this.thisTurnCast.clear(); thisTurnCast.clear();
this.curResolvingCard = null; curResolvingCard = null;
this.frozenStack.clear(); frozenStack.clear();
this.clearUndoStack(); clearUndoStack();
game.updateStackForView();
} }
/**
* <p>
* isSplitSecondOnStack.
* </p>
*
* @return a boolean.
*/
public final boolean isSplitSecondOnStack() { public final boolean isSplitSecondOnStack() {
for(SpellAbilityStackInstance si : this.stack) { for(SpellAbilityStackInstance si : stack) {
if (si.isSpell() && si.getSourceCard().hasKeyword("Split second")) { if (si.isSpell() && si.getSourceCard().hasKeyword("Split second")) {
return true; return true;
} }
@@ -143,23 +112,10 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
return false; return false;
} }
/**
* <p>
* freezeStack.
* </p>
*/
public final void freezeStack() { public final void freezeStack() {
this.frozen = true; frozen = true;
} }
/**
* <p>
* addAndUnfreeze.
* </p>
*
* @param ability
* a {@link forge.game.spellability.SpellAbility} object.
*/
public final void addAndUnfreeze(final SpellAbility ability) { public final void addAndUnfreeze(final SpellAbility ability) {
ability.getRestrictions().abilityActivated(); ability.getRestrictions().abilityActivated();
ability.checkActivationResloveSubs(); ability.checkActivationResloveSubs();
@@ -174,80 +130,39 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
// Always add the ability here and always unfreeze the stack // Always add the ability here and always unfreeze the stack
this.add(ability); add(ability);
this.unfreezeStack(); unfreezeStack();
} }
/**
* <p>
* unfreezeStack.
* </p>
*/
public final void unfreezeStack() { public final void unfreezeStack() {
this.frozen = false; frozen = false;
// Add all Frozen Abilities onto the stack // Add all Frozen Abilities onto the stack
while (!this.frozenStack.isEmpty()) { while (!frozenStack.isEmpty()) {
final SpellAbility sa = this.frozenStack.pop().getSpellAbility(true); final SpellAbility sa = frozenStack.pop().getSpellAbility(true);
this.add(sa); add(sa);
} }
// Add all waiting triggers onto the stack // Add all waiting triggers onto the stack
game.getTriggerHandler().runWaitingTriggers(); game.getTriggerHandler().runWaitingTriggers();
} }
/**
* <p>
* clearFrozen.
* </p>
*/
public final void clearFrozen() { public final void clearFrozen() {
// TODO: frozen triggered abilities and undoable costs have nasty // TODO: frozen triggered abilities and undoable costs have nasty
// consequences // consequences
this.frozen = false; frozen = false;
this.frozenStack.clear(); frozenStack.clear();
} }
/**
* <p>
* setResolving.
* </p>
*
* @param b
* a boolean.
*/
public final void setResolving(final boolean b) {
this.bResolving = b;
}
/**
* <p>
* getResolving.
* </p>
*
* @return a boolean.
*/
public final boolean isResolving() { public final boolean isResolving() {
return this.bResolving; return bResolving;
}
public final void setResolving(final boolean b) {
bResolving = b;
} }
/**
* <p>
* undo.
* </p>
*
* @return a boolean.
*/
public final boolean canUndo(Player player) { public final boolean canUndo(Player player) {
return undoStackOwner == player; return undoStackOwner == player;
} }
/**
* <p>
* undo.
* </p>
*
* @return a boolean.
*/
public final boolean undo() { public final boolean undo() {
if (undoStack.isEmpty()) { return false; } if (undoStack.isEmpty()) { return false; }
@@ -259,26 +174,12 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
return true; return true;
} }
/**
* <p>
* clearUndoStack.
* </p>
*/
public final void clearUndoStack() { public final void clearUndoStack() {
if (undoStackOwner == null) { return; } if (undoStackOwner == null) { return; }
undoStack.clear(); undoStack.clear();
undoStackOwner = null; undoStackOwner = null;
} }
/**
* <p>
* add.
* </p>
*
* @param sp
* a {@link forge.game.spellability.SpellAbility} object.
*/
public final void add(final SpellAbility sp) { public final void add(final SpellAbility sp) {
SpellAbilityStackInstance si = null; SpellAbilityStackInstance si = null;
final Card source = sp.getHostCard(); final Card source = sp.getHostCard();
@@ -327,9 +228,9 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
} }
if (this.frozen) { if (frozen) {
si = new SpellAbilityStackInstance(sp); si = new SpellAbilityStackInstance(sp);
this.frozenStack.push(si); frozenStack.push(si);
return; return;
} }
int totManaSpent = sp.getPayingMana().size(); int totManaSpent = sp.getPayingMana().size();
@@ -344,7 +245,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
source.addOptionalCostPaid(s); source.addOptionalCostPaid(s);
} }
if (sp.isCopied()) { if (sp.isCopied()) {
si = this.push(sp); si = push(sp);
} }
else { else {
if (sp.isSpell() && source.isCreature() && Iterables.any(activator.getCardsIn(ZoneType.Battlefield), if (sp.isSpell() && source.isCreature() && Iterables.any(activator.getCardsIn(ZoneType.Battlefield),
@@ -383,7 +284,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
// The ability is added to stack HERE // The ability is added to stack HERE
si = this.push(sp); si = push(sp);
if (sp.isSpell() && (source.hasStartOfKeyword("Replicate") if (sp.isSpell() && (source.hasStartOfKeyword("Replicate")
|| ((source.isInstant() || source.isSorcery()) && Iterables.any(activator.getCardsIn(ZoneType.Battlefield), || ((source.isInstant() || source.isSorcery()) && Iterables.any(activator.getCardsIn(ZoneType.Battlefield),
@@ -426,13 +327,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
runParams.put("Activator", sp.getActivatingPlayer()); runParams.put("Activator", sp.getActivatingPlayer());
runParams.put("CastSA", si.getSpellAbility(true)); runParams.put("CastSA", si.getSpellAbility(true));
runParams.put("CastSACMC", si.getSpellAbility(true).getHostCard().getCMC()); runParams.put("CastSACMC", si.getSpellAbility(true).getHostCard().getCMC());
runParams.put("CurrentStormCount", game.getStack().getCardsCastThisTurn().size()); runParams.put("CurrentStormCount", thisTurnCast.size());
game.getTriggerHandler().runTrigger(TriggerType.SpellAbilityCast, runParams, true); game.getTriggerHandler().runTrigger(TriggerType.SpellAbilityCast, runParams, true);
// Run SpellCast triggers // Run SpellCast triggers
if (sp.isSpell()) { if (sp.isSpell()) {
game.getTriggerHandler().runTrigger(TriggerType.SpellCast, runParams, true); game.getTriggerHandler().runTrigger(TriggerType.SpellCast, runParams, true);
this.executeCastCommand(si.getSpellAbility(true).getHostCard()); executeCastCommand(si.getSpellAbility(true).getHostCard());
} }
// Run AbilityCast triggers // Run AbilityCast triggers
@@ -488,26 +389,12 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
} }
/**
* <p>
* size.
* </p>
*
* @return a int.
*/
public final int size() { public final int size() {
return this.stack.size(); return stack.size();
} }
/**
* <p>
* isEmpty.
* </p>
*
* @return a boolean.
*/
public final boolean isEmpty() { public final boolean isEmpty() {
return this.stack.isEmpty(); return stack.isEmpty();
} }
// Push should only be used by add. // Push should only be used by add.
@@ -519,8 +406,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
final SpellAbilityStackInstance si = new SpellAbilityStackInstance(sp); final SpellAbilityStackInstance si = new SpellAbilityStackInstance(sp);
this.stack.addFirst(si); stack.addFirst(si);
game.fireEvent(new GameEventSpellAbilityCast(sp, false));
// 2012-07-21 the following comparison needs to move below the pushes but somehow screws up priority // 2012-07-21 the following comparison needs to move below the pushes but somehow screws up priority
// When it's down there. That makes absolutely no sense to me, so i'm putting it back for now // When it's down there. That makes absolutely no sense to me, so i'm putting it back for now
@@ -530,31 +416,28 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
if (sp.isSpell() && !sp.isCopied()) { if (sp.isSpell() && !sp.isCopied()) {
this.thisTurnCast.add(sp.getHostCard()); thisTurnCast.add(sp.getHostCard());
sp.getActivatingPlayer().addSpellCastThisTurn(); sp.getActivatingPlayer().addSpellCastThisTurn();
} }
if (sp.isAbility() && sp.getRestrictions().isPwAbility()) { if (sp.isAbility() && sp.getRestrictions().isPwAbility()) {
sp.getActivatingPlayer().setActivateLoyaltyAbilityThisTurn(true); sp.getActivatingPlayer().setActivateLoyaltyAbilityThisTurn(true);
} }
game.updateStackForView();
game.fireEvent(new GameEventSpellAbilityCast(sp, false));
return si; return si;
} }
/**
* <p>
* resolveStack.
* </p>
*/
public final void resolveStack() { public final void resolveStack() {
// Resolving the Stack // Resolving the Stack
// freeze the stack while we're in the middle of resolving // freeze the stack while we're in the middle of resolving
this.freezeStack(); freezeStack();
this.setResolving(true); setResolving(true);
// The SpellAbility isn't removed from the Stack until it finishes resolving // The SpellAbility isn't removed from the Stack until it finishes resolving
// temporarily reverted removing SAs after resolution // temporarily reverted removing SAs after resolution
final SpellAbility sa = this.peekAbility(); final SpellAbility sa = peekAbility();
//final SpellAbility sa = this.pop(); //final SpellAbility sa = pop();
// ActivePlayer gains priority first after Resolve // ActivePlayer gains priority first after Resolve
game.getPhaseHandler().resetPriority(); game.getPhaseHandler().resetPriority();
@@ -562,7 +445,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
curResolvingCard = source; curResolvingCard = source;
boolean thisHasFizzled = this.hasFizzled(sa, source, false); boolean thisHasFizzled = hasFizzled(sa, source, false);
if (thisHasFizzled) { // Fizzle if (thisHasFizzled) { // Fizzle
if (sa.hasParam("Bestow")) { if (sa.hasParam("Bestow")) {
@@ -585,7 +468,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled)); game.fireEvent(new GameEventSpellResolved(sa, thisHasFizzled));
this.finishResolving(sa, thisHasFizzled); finishResolving(sa, thisHasFizzled);
if (source.hasStartOfKeyword("Haunt") && !source.isCreature() && game.getZoneOf(source).is(ZoneType.Graveyard)) { if (source.hasStartOfKeyword("Haunt") && !source.isCreature() && game.getZoneOf(source).is(ZoneType.Graveyard)) {
handleHauntForNonPermanents(sa); handleHauntForNonPermanents(sa);
@@ -599,7 +482,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
@Override @Override
public void resolve() { public void resolve() {
game.getAction().exile(source); game.getAction().exile(source);
this.getTargetCard().addHauntedBy(source); getTargetCard().addHauntedBy(source);
} }
}; };
for (int i = 0; i < creats.size(); i++) { for (int i = 0; i < creats.size(); i++) {
@@ -614,32 +497,31 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
haunterDiesWork.setTargetRestrictions(new TargetRestrictions("", "Creature".split(" "), "1", "1")); haunterDiesWork.setTargetRestrictions(new TargetRestrictions("", "Creature".split(" "), "1", "1"));
final Card targetCard = source.getController().getController().chooseSingleEntityForEffect(creats, new SpellAbility.EmptySa(ApiType.InternalHaunt, source), "Choose target creature to haunt."); final Card targetCard = source.getController().getController().chooseSingleEntityForEffect(creats, new SpellAbility.EmptySa(ApiType.InternalHaunt, source), "Choose target creature to haunt.");
haunterDiesWork.setTargetCard(targetCard); haunterDiesWork.setTargetCard(targetCard);
this.add(haunterDiesWork); add(haunterDiesWork);
} }
} }
private final void finishResolving(final SpellAbility sa, final boolean fizzle) { private final void finishResolving(final SpellAbility sa, final boolean fizzle) {
// remove SA and card from the stack // remove SA and card from the stack
this.removeCardFromStack(sa, fizzle); removeCardFromStack(sa, fizzle);
// SpellAbility is removed from the stack here // SpellAbility is removed from the stack here
// temporarily removed removing SA after resolution // temporarily removed removing SA after resolution
final SpellAbilityStackInstance si = this.getInstanceFromSpellAbility(sa); final SpellAbilityStackInstance si = getInstanceFromSpellAbility(sa);
if (si != null) { if (si != null) {
this.remove(si); remove(si);
} }
// After SA resolves we have to do a handful of things // After SA resolves we have to do a handful of things
this.setResolving(false); setResolving(false);
this.unfreezeStack(); unfreezeStack();
sa.resetOnceResolved(); sa.resetOnceResolved();
//game.getAction().checkStaticAbilities(); //game.getAction().checkStaticAbilities();
game.getPhaseHandler().onStackResolved(); game.getPhaseHandler().onStackResolved();
this.curResolvingCard = null; curResolvingCard = null;
// TODO: this is a huge hack. Why is this necessary? // TODO: this is a huge hack. Why is this necessary?
// hostCard in AF is not the same object that's on the battlefield // hostCard in AF is not the same object that's on the battlefield
@@ -660,21 +542,23 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
private final void removeCardFromStack(final SpellAbility sa, final boolean fizzle) { private final void removeCardFromStack(final SpellAbility sa, final boolean fizzle) {
Card source = sa.getHostCard(); Card source = sa.getHostCard();
// do nothing
if (sa.getHostCard().isCopiedSpell() || sa.isAbility()) { if (sa.getHostCard().isCopiedSpell() || sa.isAbility()) {
// do nothing
} }
// Handle cards that need to be moved differently
else if (sa.isBuyBackAbility() && !fizzle) { else if (sa.isBuyBackAbility() && !fizzle) {
// Handle cards that need to be moved differently
game.getAction().moveToHand(source); game.getAction().moveToHand(source);
} else if (sa.isFlashBackAbility()) { }
else if (sa.isFlashBackAbility()) {
game.getAction().exile(source); game.getAction().exile(source);
sa.setFlashBackAbility(false); sa.setFlashBackAbility(false);
} else if (source.hasKeyword("Rebound") }
else if (source.hasKeyword("Rebound")
&& source.getCastFrom() == ZoneType.Hand && source.getCastFrom() == ZoneType.Hand
&& game.getZoneOf(source).is(ZoneType.Stack) && game.getZoneOf(source).is(ZoneType.Stack)
&& source.getOwner().equals(source.getController())) //"If you cast this spell from your hand" && source.getOwner().equals(source.getController())) //"If you cast this spell from your hand"
{ {
//Move rebounding card to exile //Move rebounding card to exile
source = game.getAction().exile(source); source = game.getAction().exile(source);
@@ -689,27 +573,14 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
game.getTriggerHandler().registerDelayedTrigger(reboundTrigger); game.getTriggerHandler().registerDelayedTrigger(reboundTrigger);
} }
else if (!source.isCopiedSpell() &&
// If Spell and still on the Stack then let it goto the graveyard or (source.isInstant() || source.isSorcery() || fizzle) &&
// replace its own movement source.isInZone(ZoneType.Stack)) {
else if (!source.isCopiedSpell() && (source.isInstant() || source.isSorcery() || fizzle) // If Spell and still on the Stack then let it goto the graveyard or replace its own movement
&& source.isInZone(ZoneType.Stack)) {
game.getAction().moveToGraveyard(source); game.getAction().moveToGraveyard(source);
} }
} }
/**
* <p>
* hasFizzled.
* </p>
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param source
* a {@link forge.game.card.Card} object.
* @return a boolean.
*/
private final boolean hasFizzled(final SpellAbility sa, final Card source, final boolean parentFizzled) { private final boolean hasFizzled(final SpellAbility sa, final Card source, final boolean parentFizzled) {
// Can't fizzle unless there are some targets // Can't fizzle unless there are some targets
boolean fizzle = false; boolean fizzle = false;
@@ -768,27 +639,26 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
return fizzle; return fizzle;
} }
return hasFizzled(sa.getSubAbility(), source, fizzle) && fizzle; return hasFizzled(sa.getSubAbility(), source, fizzle) && fizzle;
} }
public final SpellAbilityStackInstance peek() { public final SpellAbilityStackInstance peek() {
return this.stack.peekFirst(); return stack.peekFirst();
} }
public final SpellAbility peekAbility() { public final SpellAbility peekAbility() {
return this.stack.peekFirst().getSpellAbility(true); return stack.peekFirst().getSpellAbility(true);
} }
public final void remove(final SpellAbilityStackInstance si) { public final void remove(final SpellAbilityStackInstance si) {
this.stack.remove(si); stack.remove(si);
this.frozenStack.remove(si); frozenStack.remove(si);
game.fireEvent(new GameEventSpellRemovedFromStack(si.getSpellAbility(true))); game.fireEvent(new GameEventSpellRemovedFromStack(si.getSpellAbility(true)));
} }
public final SpellAbilityStackInstance getInstanceFromSpellAbility(final SpellAbility sa) { public final SpellAbilityStackInstance getInstanceFromSpellAbility(final SpellAbility sa) {
// TODO: Confirm this works! // TODO: Confirm this works!
for (final SpellAbilityStackInstance si : this.stack) { for (final SpellAbilityStackInstance si : stack) {
if (si.compareToSpellAbility(sa)) { if (si.compareToSpellAbility(sa)) {
return si; return si;
} }
@@ -797,15 +667,15 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
public final boolean hasSimultaneousStackEntries() { public final boolean hasSimultaneousStackEntries() {
return !this.simultaneousStackEntryList.isEmpty(); return !simultaneousStackEntryList.isEmpty();
} }
public final void clearSimultaneousStack() { public final void clearSimultaneousStack() {
this.simultaneousStackEntryList.clear(); simultaneousStackEntryList.clear();
} }
public final void addSimultaneousStackEntry(final SpellAbility sa) { public final void addSimultaneousStackEntry(final SpellAbility sa) {
this.simultaneousStackEntryList.add(sa); simultaneousStackEntryList.add(sa);
} }
public boolean addAllTriggeredAbilitiesToStack() { public boolean addAllTriggeredAbilitiesToStack() {
@@ -826,24 +696,24 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
private final boolean chooseOrderOfSimultaneousStackEntry(final Player activePlayer) { private final boolean chooseOrderOfSimultaneousStackEntry(final Player activePlayer) {
if (this.simultaneousStackEntryList.isEmpty()) { if (simultaneousStackEntryList.isEmpty()) {
return false; return false;
} }
final List<SpellAbility> activePlayerSAs = new ArrayList<SpellAbility>(); final List<SpellAbility> activePlayerSAs = new ArrayList<SpellAbility>();
for (int i = 0; i < this.simultaneousStackEntryList.size(); i++) { for (int i = 0; i < simultaneousStackEntryList.size(); i++) {
SpellAbility sa = this.simultaneousStackEntryList.get(i); SpellAbility sa = simultaneousStackEntryList.get(i);
Player activator = sa.getActivatingPlayer(); Player activator = sa.getActivatingPlayer();
if (activator == null) { if (activator == null) {
if (sa.getHostCard().getController().equals(activePlayer)) { if (sa.getHostCard().getController().equals(activePlayer)) {
activePlayerSAs.add(sa); activePlayerSAs.add(sa);
this.simultaneousStackEntryList.remove(i); simultaneousStackEntryList.remove(i);
i--; i--;
} }
} else { } else {
if (activator.equals(activePlayer)) { if (activator.equals(activePlayer)) {
activePlayerSAs.add(sa); activePlayerSAs.add(sa);
this.simultaneousStackEntryList.remove(i); simultaneousStackEntryList.remove(i);
i--; i--;
} }
} }
@@ -856,73 +726,58 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
return true; return true;
} }
/**
* TODO: Write javadoc for this method.
*
* @param triggerID
* the trigger id
* @return true, if successful
*/
public final boolean hasStateTrigger(final int triggerID) { public final boolean hasStateTrigger(final int triggerID) {
for (final SpellAbilityStackInstance sasi : this.stack) { for (final SpellAbilityStackInstance sasi : stack) {
if (sasi.isStateTrigger(triggerID)) { if (sasi.isStateTrigger(triggerID)) {
return true; return true;
} }
} }
for (final SpellAbilityStackInstance sasi : this.frozenStack) { for (final SpellAbilityStackInstance sasi : frozenStack) {
if (sasi.isStateTrigger(triggerID)) { if (sasi.isStateTrigger(triggerID)) {
return true; return true;
} }
} }
for (final SpellAbility sa : this.simultaneousStackEntryList) { for (final SpellAbility sa : simultaneousStackEntryList) {
if (sa.getSourceTrigger() == triggerID) { if (sa.getSourceTrigger() == triggerID) {
return true; return true;
} }
} }
return false; return false;
} }
/** public final List<Card> getSpellsCastThisTurn() {
* Accessor for the field thisTurnCast. return thisTurnCast;
*
* @return a CardList.
*/
public final List<Card> getCardsCastThisTurn() {
return this.thisTurnCast;
} }
/**
* clearCardsCastThisTurn.
*/
public final void onNextTurn() { public final void onNextTurn() {
this.lastTurnCast = new ArrayList<Card>(this.thisTurnCast); if (thisTurnCast.isEmpty()) {
this.thisTurnCast.clear(); lastTurnCast = new ArrayList<Card>();
return;
}
lastTurnCast = new ArrayList<Card>(thisTurnCast);
thisTurnCast.clear();
game.updateStackForView();
} }
/** public final List<Card> getSpellsCastLastTurn() {
* Accessor for the field lastTurnCast. return lastTurnCast;
*
* @return a CardList.
*/
public final List<Card> getCardsCastLastTurn() {
return this.lastTurnCast;
} }
public final void addCastCommand(final String valid, final GameCommand c) { public final void addCastCommand(final String valid, final GameCommand c) {
if (this.commandList.containsKey(valid)) { if (commandList.containsKey(valid)) {
this.commandList.get(valid).add(0, c); commandList.get(valid).add(0, c);
} else { }
this.commandList.put(valid, Lists.newArrayList(c)); else {
commandList.put(valid, Lists.newArrayList(c));
} }
} }
private void executeCastCommand(final Card cast) { private void executeCastCommand(final Card cast) {
for (Entry<String, List<GameCommand>> ev : this.commandList.entrySet()) { for (Entry<String, List<GameCommand>> ev : commandList.entrySet()) {
if (cast.isType(ev.getKey())) { if (cast.isType(ev.getKey())) {
this.execute(ev.getValue()); execute(ev.getValue());
} }
} }
} }
@@ -934,23 +789,13 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
} }
} }
/**
* Checks if is resolving.
*
* @param c the c
* @return true, if is resolving
*/
public final boolean isResolving(Card c) { public final boolean isResolving(Card c) {
if (!this.isResolving() || this.curResolvingCard == null) { if (!isResolving() || curResolvingCard == null) {
return false; return false;
} }
return c.equals(curResolvingCard);
return c.equals(this.curResolvingCard);
} }
/* (non-Javadoc)
* @see java.lang.Iterable#iterator()
*/
@Override @Override
public Iterator<SpellAbilityStackInstance> iterator() { public Iterator<SpellAbilityStackInstance> iterator() {
return stack.iterator(); return stack.iterator();
@@ -960,11 +805,10 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
return stack.descendingIterator(); return stack.descendingIterator();
} }
/**
* TODO: Write javadoc for this method.
*/
public void clear() { public void clear() {
if (stack.isEmpty()) { return; }
stack.clear(); stack.clear();
game.updateStackForView();
game.fireEvent(new GameEventSpellRemovedFromStack(null)); game.fireEvent(new GameEventSpellRemovedFromStack(null));
} }

View File

@@ -1,6 +1,8 @@
package forge.trackable; package forge.trackable;
import forge.card.CardRarity; import forge.card.CardRarity;
import forge.game.GameType;
import forge.game.phase.PhaseType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.trackable.TrackableTypes; import forge.trackable.TrackableTypes;
import forge.trackable.TrackableTypes.TrackableType; import forge.trackable.TrackableTypes.TrackableType;
@@ -114,7 +116,20 @@ public enum TrackableProperty {
BandsWithDefenders(null), BandsWithDefenders(null),
BandsWithBlockers(null), BandsWithBlockers(null),
AttackersWithPlannedBlockers(null), AttackersWithPlannedBlockers(null),
BandsWithPlannedBlockers(null); BandsWithPlannedBlockers(null),
GameType(TrackableTypes.EnumType(GameType.class)),
Turn(TrackableTypes.IntegerType),
WinningTeam(TrackableTypes.IntegerType),
MatchOver(TrackableTypes.BooleanType),
NumGamesInMatch(TrackableTypes.IntegerType),
NumPlayedGamesInMatch(TrackableTypes.IntegerType),
StormCount(TrackableTypes.IntegerType),
GameOver(TrackableTypes.BooleanType),
PoisonCountersToLose(TrackableTypes.IntegerType),
GameLog(TrackableTypes.PlayerViewType),
PlayerTurn(TrackableTypes.PlayerViewType),
Phase(TrackableTypes.EnumType(PhaseType.class));
private final TrackableType<?> type; private final TrackableType<?> type;