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

View File

@@ -7,28 +7,109 @@ import forge.game.card.CardView;
import forge.game.combat.AttackingBand;
import forge.game.combat.Combat;
import forge.game.combat.CombatView;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
import forge.game.player.PlayerView;
import forge.game.spellability.SpellAbilityView;
import forge.game.spellability.StackItemView;
import forge.game.zone.MagicStack;
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<PlayerView> players = new TrackableIndex<PlayerView>();
private final TrackableIndex<SpellAbilityView> spellAbilities = new TrackableIndex<SpellAbilityView>();
private final TrackableIndex<StackItemView> stackItems = new TrackableIndex<StackItemView>();
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;
}
public void refreshCombat(Game game) {
final Combat combat = game.getCombat();
void updateCombat(Combat combat) {
if (combat == null) {
combatView = null;
return;

View File

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

View File

@@ -4029,7 +4029,7 @@ public class Card extends GameEntity implements Comparable<Card>, IIdentifiable
if (!CardUtil.getColors(this).hasAnyColor(mask))
return false;
} 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))) {
return false;
}

View File

@@ -1029,7 +1029,7 @@ public class CardFactoryUtil {
return doXMath(c.getFirstSpellAbility().getTotalManaSpent(), m, c);
}
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")) {
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) {
List<Card> res = new ArrayList<Card>();
final Game game = src.getGame();
res.addAll(game.getStack().getCardsCastThisTurn());
res.addAll(game.getStack().getSpellsCastThisTurn());
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) {
List<Card> res = new ArrayList<Card>();
final Game game = src.getGame();
res.addAll(game.getStack().getCardsCastLastTurn());
res.addAll(game.getStack().getSpellsCastLastTurn());
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)
private List<Card> combatantsThatDealtFirstStrikeDamage = Lists.newArrayList();
public Combat(Player attacker) {
playerWhoAttacks = attacker;
@@ -72,7 +71,7 @@ public class Combat {
}
}
}
public final Player getAttackingPlayer() {
return this.playerWhoAttacks;
}
@@ -83,14 +82,14 @@ public class Combat {
public final List<GameEntity> getDefendersControlledBy(Player who) {
List<GameEntity> res = Lists.newArrayList();
for(GameEntity ge : attackableEntries) {
for (GameEntity ge : attackableEntries) {
// if defender is the player himself or his cards
if (ge == who || ge instanceof Card && ((Card) ge).getController() == who)
res.add(ge);
}
return res;
}
public final List<Card> getDefendingPlaneswalkers() {
final List<Card> pwDefending = new ArrayList<Card>();
for (final GameEntity o : attackableEntries) {
@@ -104,10 +103,10 @@ public class Combat {
public final List<AttackingBand> getAttackingBandsOf(GameEntity defender) {
return Lists.newArrayList(attackedByBands.get(defender));
}
public final List<Card> getAttackersOf(GameEntity defender) {
List<Card> result = new ArrayList<Card>();
for(AttackingBand v : attackedByBands.get(defender)) {
for (AttackingBand v : attackedByBands.get(defender)) {
result.addAll(v.getAttackers());
}
return result;
@@ -116,7 +115,7 @@ public class Combat {
public final void addAttacker(final Card c, GameEntity defender) {
addAttacker(c, defender, null);
}
public final void addAttacker(final Card c, GameEntity defender, AttackingBand band) {
Collection<AttackingBand> attackersOfDefender = attackedByBands.get(defender);
if (attackersOfDefender == null) {
@@ -133,7 +132,8 @@ public class Combat {
if (band == null || !attackersOfDefender.contains(band)) {
band = new AttackingBand(c, defender);
attackersOfDefender.add(band);
} else {
}
else {
band.addAttacker(c);
}
}
@@ -141,11 +141,12 @@ public class Combat {
public final GameEntity getDefenderByAttacker(final Card c) {
return getDefenderByAttacker(getBandOfAttacker(c));
}
public final GameEntity getDefenderByAttacker(final AttackingBand c) {
for(Entry<GameEntity, AttackingBand> e : attackedByBands.entries()) {
if ( e.getValue() == c )
for (Entry<GameEntity, AttackingBand> e : attackedByBands.entries()) {
if (e.getValue() == c) {
return e.getKey();
}
}
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)
public final AttackingBand getBandOfAttacker(final Card c) {
for(AttackingBand ab : attackedByBands.values()) {
if ( ab.contains(c) )
for (AttackingBand ab : attackedByBands.values()) {
if (ab.contains(c))
return ab;
}
CombatLki lki = lkiCache.get(c);
@@ -186,16 +187,19 @@ public class Combat {
public boolean isAttacking(Card card, GameEntity defender) {
AttackingBand ab = getBandOfAttacker(card);
for(Entry<GameEntity, AttackingBand> ee : attackedByBands.entries())
if ( ee.getValue() == ab )
for (Entry<GameEntity, AttackingBand> ee : attackedByBands.entries()) {
if (ee.getValue() == ab) {
return ee.getKey() == defender;
}
}
return false;
}
public final List<Card> getAttackers() {
List<Card> result = Lists.newArrayList();
for(AttackingBand ab : attackedByBands.values())
for (AttackingBand ab : attackedByBands.values()) {
result.addAll(ab.getAttackers());
}
return result;
}
@@ -230,7 +234,7 @@ public class Combat {
public final void removeBlockAssignment(final Card attacker, final Card blocker) {
AttackingBand band = getBandOfAttacker(attacker);
Collection<Card> cc = blockedBands.get(band);
if( cc != null)
if (cc != null)
cc.remove(blocker);
}
@@ -242,8 +246,8 @@ public class Combat {
public final List<Card> getAllBlockers() {
List<Card> result = new ArrayList<Card>();
for(Card blocker : blockedBands.values()) {
if(!result.contains(blocker))
for (Card blocker : blockedBands.values()) {
if (!result.contains(blocker))
result.add(blocker);
}
return result;
@@ -254,19 +258,19 @@ public class Combat {
return blockers == null ? Lists.<Card>newArrayList() : Lists.newArrayList(blockers);
}
public final List<Card> getAttackersBlockedBy(final Card blocker) {
List<Card> blocked = new ArrayList<Card>();
for(Entry<AttackingBand, Card> s : blockedBands.entries()) {
if (s.getValue().equals(blocker))
for (Entry<AttackingBand, Card> s : blockedBands.entries()) {
if (s.getValue().equals(blocker)) {
blocked.addAll(s.getKey().getAttackers());
}
}
return blocked;
}
public final List<AttackingBand> getAttackingBandsBlockedBy(Card blocker) {
List<AttackingBand> bands = Lists.newArrayList();
for( Entry<AttackingBand, Card> kv : blockedBands.entries()) {
for (Entry<AttackingBand, Card> kv : blockedBands.entries()) {
if (kv.getValue().equals(blocker))
bands.add(kv.getKey());
}
@@ -277,9 +281,11 @@ public class Combat {
Card attacker = source;
if (source.isAura()) {
attacker = source.getEnchantingCard();
} else if (source.isEquipment()) {
}
else if (source.isEquipment()) {
attacker = source.getEquipping();
} else if (source.isFortification()) {
}
else if (source.isFortification()) {
attacker = source.getFortifying();
}
@@ -292,16 +298,16 @@ public class Combat {
List<Pair<Card, List<Card>>> blockersNeedManualOrdering = new ArrayList<>();
for(AttackingBand band : attackedByBands.values())
for (AttackingBand band : attackedByBands.values())
{
if (band.isEmpty()) continue;
Collection<Card> blockers = blockedBands.get(band);
if ( blockers == null || blockers.isEmpty() )
if (blockers == null || blockers.isEmpty())
continue;
for(Card attacker : band.getAttackers()) {
if ( blockers.size() <= 1 )
for (Card attacker : band.getAttackers()) {
if (blockers.size() <= 1)
blockersOrderedForDamageAssignment.put(attacker, Lists.newArrayList(blockers));
else // process it a bit later
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
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?
List<Card> orderedBlockers = playerWhoAttacks.getController().orderBlockers(pair.getLeft(), pair.getRight()); // we know there's a list
blockersOrderedForDamageAssignment.put(pair.getLeft(), orderedBlockers);
@@ -358,7 +364,7 @@ public class Combat {
blockersOrderedForDamageAssignment.remove(c);
Collection<Card> blockers = blockedBands.get(ab);
if ( blockers != null ) {
if (blockers != null) {
for (Card b : blockers) {
// Clear removed attacker from assignment order
if (this.attackersOrderedForDamageAssignment.containsKey(b)) {
@@ -372,7 +378,7 @@ public class Combat {
// removes references to this defender from all indices and orders
private void unregisterDefender(final Card c, AttackingBand bandBeingBlocked) {
this.attackersOrderedForDamageAssignment.remove(c);
for(Card atk : bandBeingBlocked.getAttackers()) {
for (Card atk : bandBeingBlocked.getAttackers()) {
if (this.blockersOrderedForDamageAssignment.containsKey(atk)) {
this.blockersOrderedForDamageAssignment.get(atk).remove(c);
}
@@ -388,49 +394,52 @@ public class Combat {
}
// if not found in attackers, look for this card in blockers
for(Entry<AttackingBand, Card> be : blockedBands.entries()) {
if(be.getValue().equals(c)) {
for (Entry<AttackingBand, Card> be : blockedBands.entries()) {
if (be.getValue().equals(c)) {
unregisterDefender(c, be.getKey());
}
}
// remove card from map
while(blockedBands.values().remove(c));
} // removeFromCombat()
public final void removeAbsentCombatants() {
public final boolean removeAbsentCombatants() {
// iterate all attackers and remove them
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();
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);
if ( !c.isInPlay() || !c.isCreature() ) {
if (!c.isInPlay() || !c.isCreature()) {
missingCombatants.add(c);
}
}
}
for(Entry<AttackingBand, Card> be : blockedBands.entries()) {
for (Entry<AttackingBand, Card> be : blockedBands.entries()) {
Card blocker = be.getValue();
if ( !blocker.isInPlay() || !blocker.isCreature() ) {
if (!blocker.isInPlay() || !blocker.isCreature()) {
missingCombatants.add(blocker);
}
}
if (missingCombatants.isEmpty()) { return false; }
for (Card c : missingCombatants) {
removeFromCombat(c);
}
return true;
}
// Call this method right after turn-based action of declare blockers has been performed
public final void fireTriggersForUnblockedAttackers() {
for(AttackingBand ab : attackedByBands.values()) {
for (AttackingBand ab : attackedByBands.values()) {
Collection<Card> blockers = blockedBands.get(ab);
boolean isBlocked = blockers != null && !blockers.isEmpty();
ab.setBlocked(isBlocked);
if (!isBlocked )
if (!isBlocked) {
for (Card attacker : ab.getAttackers()) {
// Run Unblocked Trigger
final HashMap<String, Object> runParams = new HashMap<String, Object>();
@@ -438,6 +447,7 @@ public class Combat {
runParams.put("Defender",this.getDefenderByAttacker(attacker));
attacker.getGame().getTriggerHandler().runTrigger(TriggerType.AttackerUnblocked, runParams, false);
}
}
}
}
@@ -473,12 +483,11 @@ public class Combat {
}
}
}
return assignedDamage;
}
private final boolean assignAttackersDamage(boolean firstStrikeDamage) {
// Assign damage by Attackers
// Assign damage by Attackers
this.defendingDamageMap.clear(); // this should really happen in deal damage
List<Card> orderedBlockers = null;
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
this.addDefendingDamage(damageDealt, attacker);
} // No damage happens if blocked but no blockers left
} else {
}
else {
GameEntity defender = getDefenderByAttacker(band);
Player assigningPlayer = this.getAttackingPlayer();
// Defensive Formation is very similar to Banding with Blockers
// 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.")) {
assigningPlayer = (Player)defender;
} else if ( AttackingBand.isValidBand(orderedBlockers, true)){
}
else if (AttackingBand.isValidBand(orderedBlockers, true)){
assigningPlayer = orderedBlockers.get(0).getController();
}
Map<Card, Integer> map = assigningPlayer.getController().assignCombatDamage(attacker, orderedBlockers, damageDealt, defender, this.getAttackingPlayer() != assigningPlayer);
for (Entry<Card, Integer> dt : map.entrySet()) {
if( dt.getKey() == null) {
if (dt.getKey() == null) {
if (dt.getValue() > 0)
addDefendingDamage(dt.getValue(), attacker);
} else {
dt.getKey().addAssignedDamage(dt.getValue(), attacker);
}
}
} // if !hasFirstStrike ...
} // for
return assignedDamage;
@@ -540,29 +550,29 @@ public class Combat {
private final boolean dealDamageThisPhase(Card combatant, boolean firstStrikeDamage) {
// 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
if (combatant.hasDoubleStrike())
if (combatant.hasDoubleStrike()) {
return true;
if (firstStrikeDamage && combatant.hasFirstStrike())
}
if (firstStrikeDamage && combatant.hasFirstStrike()) {
return true;
}
return !firstStrikeDamage && !this.combatantsThatDealtFirstStrikeDamage.contains(combatant);
}
// Damage to whatever was protected there.
private final void addDefendingDamage(final int n, final Card source) {
final GameEntity ge = this.getDefenderByAttacker(source);
if (ge instanceof Card) {
final Card planeswalker = (Card) ge;
planeswalker.addAssignedDamage(n, source);
return;
}
if (!this.defendingDamageMap.containsKey(source)) {
this.defendingDamageMap.put(source, n);
} else {
}
else {
this.defendingDamageMap.put(source, this.defendingDamageMap.get(source) + n);
}
}
@@ -577,14 +587,8 @@ public class Combat {
return assignedDamage;
}
/**
* <p>
* dealAssignedDamage.
* </p>
*/
public void dealAssignedDamage() {
// This function handles both Regular and First Strike combat assignment
final HashMap<Card, Integer> defMap = this.defendingDamageMap;
final HashMap<GameEntity, List<Card>> wasDamaged = new HashMap<GameEntity, List<Card>>();
@@ -600,11 +604,13 @@ public class Combat {
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 (wasDamaged.containsKey(defender)) {
wasDamaged.get(defender).add(entry.getKey());
} else {
}
else {
List<Card> l = new ArrayList<Card>();
l.add(entry.getKey());
wasDamaged.put(defender, l);
@@ -649,59 +655,42 @@ public class Combat {
runParams.put("DamageTarget", ge);
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) {
AttackingBand band = getBandOfAttacker(att);
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() {
List<Card> unblocked = new ArrayList<Card>();
for (AttackingBand ab : attackedByBands.values())
if ( Boolean.TRUE.equals(ab.isBlocked()) )
for (AttackingBand ab : attackedByBands.values()) {
if (Boolean.TRUE.equals(ab.isBlocked())) {
unblocked.addAll(ab.getAttackers());
}
}
return unblocked;
}
public boolean isPlayerAttacked(Player who) {
for(GameEntity defender : attackedByBands.keySet() ) {
for (GameEntity defender : attackedByBands.keySet()) {
Card defenderAsCard = defender instanceof Card ? (Card)defender : null;
if ((null != defenderAsCard && defenderAsCard.getController() != who ) ||
(null == defenderAsCard && defender != who) )
if ((null != defenderAsCard && defenderAsCard.getController() != who) ||
(null == defenderAsCard && defender != who)) {
continue; // defender is not related to player 'who'
for(AttackingBand ab : attackedByBands.get(defender)) {
if ( !ab.isEmpty() )
}
for (AttackingBand ab : attackedByBands.get(defender)) {
if (!ab.isEmpty()) {
return true;
}
}
}
return false;
}
public boolean isBlocking(Card blocker) {
if ( !blocker.isInPlay() ) {
if (!blocker.isInPlay()) {
CombatLki lki = lkiCache.get(blocker);
return null != lki && !lki.isAttacker; // was blocking something anyway
}
@@ -710,7 +699,7 @@ public class Combat {
public boolean isBlocking(Card blocker, Card attacker) {
AttackingBand ab = getBandOfAttacker(attacker);
if ( !blocker.isInPlay() ) {
if (!blocker.isInPlay()) {
CombatLki lki = lkiCache.get(blocker);
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);
}
/**
* TODO: Write javadoc for this method.
* @param lastKnownInfo
*/
public void saveLKI(Card lastKnownInfo) {
List<AttackingBand> attackersBlocked = null;
AttackingBand attackingBand = getBandOfAttacker(lastKnownInfo);
boolean isAttacker = attackingBand != null;
if ( !isAttacker ) {
if (!isAttacker) {
attackersBlocked= getAttackingBandsBlockedBy(lastKnownInfo);
if ( attackersBlocked.isEmpty() )
return; // card was not even in combat
if (attackersBlocked.isEmpty()) {
return; // card was not even in combat
}
}
List<AttackingBand> relatedBands = isAttacker ? Lists.newArrayList(attackingBand) : attackersBlocked;
lkiCache.put(lastKnownInfo, new CombatLki(isAttacker, relatedBands));
}
} // Class Combat
}

View File

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

View File

@@ -57,15 +57,12 @@ import java.util.*;
* @version $Id: PhaseHandler.java 13001 2012-01-08 12:25:25Z Sloth $
*/
public class PhaseHandler implements java.io.Serializable {
/** Constant <code>serialVersionUID=5207222278370963197L</code>. */
private static final long serialVersionUID = 5207222278370963197L;
// Start turn at 0, since we start even before first untap
private PhaseType phase = null;
private int turn = 0;
private final transient Stack<ExtraTurn> extraTurns = new Stack<ExtraTurn>();
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;
public PhaseHandler(final 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) {
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() {
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() {
return this.pPlayerPriority;
return pPlayerPriority;
}
/**
* <p>
* setPriority.
* </p>
*
* @param p
* a {@link forge.game.player.Player} object.
*/
public final void setPriority(final Player p) {
this.pFirstPriority = p;
this.pPlayerPriority = p;
pFirstPriority = p;
pPlayerPriority = p;
}
/**
* <p>
* resetPriority.
* </p>
*/
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 Combat getCombat() { return this.combat; }
public final Combat getCombat() { return combat; }
private void advanceToNextPhase() {
PhaseType oldPhase = phase;
if (this.bRepeatCleanup) { // for when Cleanup needs to repeat itself
this.bRepeatCleanup = false;
if (bRepeatCleanup) { // for when Cleanup needs to repeat itself
bRepeatCleanup = false;
}
else {
// If the phase that's ending has a stack of additional phases
// Take the LIFO one and move to that instead of the normal one
if (this.extraPhases.containsKey(phase)) {
PhaseType nextPhase = this.extraPhases.get(phase).pop();
if (extraPhases.containsKey(phase)) {
PhaseType nextPhase = extraPhases.get(phase).pop();
// If no more additional phases are available, remove it from the map
// and let the next add, reput the key
if (this.extraPhases.get(phase).isEmpty()) {
this.extraPhases.remove(phase);
if (extraPhases.get(phase).isEmpty()) {
extraPhases.remove(phase);
}
this.phase = nextPhase;
setPhase(nextPhase);
}
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";
if (this.phase == PhaseType.UNTAP) {
this.turn++;
if (phase == PhaseType.UNTAP) {
turn++;
game.updateTurnForView();
game.fireEvent(new GameEventTurnBegan(playerTurn, turn));
// 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());
}
game.fireEvent(new GameEventTurnPhase(this.getPlayerTurn(), this.getPhase(), phaseType));
game.fireEvent(new GameEventTurnPhase(playerTurn, phase, phaseType));
}
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.");
case UPKEEP:
return getPlayerTurn().hasKeyword("Skip your upkeep step.");
return playerTurn.hasKeyword("Skip your upkeep step.");
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_DECLARE_ATTACKERS:
@@ -236,7 +207,7 @@ public class PhaseHandler implements java.io.Serializable {
case COMBAT_DECLARE_BLOCKERS:
case COMBAT_FIRST_STRIKE_DAMAGE:
case COMBAT_DAMAGE:
return !this.inCombat();
return !inCombat();
default:
return false;
@@ -256,7 +227,7 @@ public class PhaseHandler implements java.io.Serializable {
}
else {
// Perform turn-based actions
switch(this.getPhase()) {
switch (phase) {
case UNTAP:
givePriorityToPlayer = false;
game.getUntap().executeUntil(playerTurn);
@@ -264,19 +235,19 @@ public class PhaseHandler implements java.io.Serializable {
break;
case UPKEEP:
this.nUpkeepsThisTurn++;
this.nUpkeepsThisGame++;
game.getUpkeep().executeUntil(this.getPlayerTurn());
nUpkeepsThisTurn++;
nUpkeepsThisGame++;
game.getUpkeep().executeUntil(playerTurn);
game.getUpkeep().executeAt();
break;
case DRAW:
this.getPlayerTurn().drawCard();
playerTurn.drawCard();
break;
case MAIN1:
if (this.getPlayerTurn().isArchenemy() && this.isPreCombatMain()) {
this.getPlayerTurn().setSchemeInMotion();
if (playerTurn.isArchenemy() && isPreCombatMain()) {
playerTurn.setSchemeInMotion();
}
break;
@@ -294,6 +265,7 @@ public class PhaseHandler implements java.io.Serializable {
if (combat != null && combat.getAttackers().isEmpty()
&& !game.getTriggerHandler().hasDelayedTriggers()) {
combat = null;
game.updateCombatForView();
}
}
@@ -308,11 +280,13 @@ public class PhaseHandler implements java.io.Serializable {
break;
case COMBAT_FIRST_STRIKE_DAMAGE:
combat.removeAbsentCombatants();
if (combat.removeAbsentCombatants()) {
game.updateCombatForView();
}
// no first strikers, skip this step
if (!combat.assignCombatDamage(true)) {
this.givePriorityToPlayer = false;
givePriorityToPlayer = false;
}
else {
combat.dealAssignedDamage();
@@ -320,10 +294,12 @@ public class PhaseHandler implements java.io.Serializable {
break;
case COMBAT_DAMAGE:
combat.removeAbsentCombatants();
if (combat.removeAbsentCombatants()) {
game.updateCombatForView();
}
if (!combat.assignCombatDamage(false)) {
this.givePriorityToPlayer = false;
givePriorityToPlayer = false;
}
else {
combat.dealAssignedDamage();
@@ -374,9 +350,9 @@ public class PhaseHandler implements java.io.Serializable {
player.onCleanupPhase();
player.getController().autoPassCancel(); // autopass won't wrap to next turn
}
this.getPlayerTurn().removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(this.getNextTurn());
this.nUpkeepsThisTurn = 0;
playerTurn.removeKeyword("Skip all combat phases of this turn.");
game.getCleanup().executeUntil(getNextTurn());
nUpkeepsThisTurn = 0;
// Rule 514.3
givePriorityToPlayer = false;
@@ -393,8 +369,8 @@ public class PhaseHandler implements java.io.Serializable {
if (!skipped) {
// Run triggers if phase isn't being skipped
final HashMap<String, Object> runParams = new HashMap<String, Object>();
runParams.put("Phase", this.getPhase().nameForScripts);
runParams.put("Player", this.getPlayerTurn());
runParams.put("Phase", phase.nameForScripts);
runParams.put("Player", playerTurn);
game.getTriggerHandler().runTrigger(TriggerType.Phase, runParams, false);
}
@@ -408,7 +384,6 @@ public class PhaseHandler implements java.io.Serializable {
}
}
private void onPhaseEnd() {
// If the Stack isn't empty why is nextPhase being called?
if (!game.getStack().isEmpty()) {
@@ -428,19 +403,19 @@ public class PhaseHandler implements java.io.Serializable {
}
}
switch (this.phase) {
switch (phase) {
case UNTAP:
this.nCombatsThisTurn = 0;
nCombatsThisTurn = 0;
break;
case UPKEEP:
for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
c.getDamageHistory().setNotAttackedSinceLastUpkeepOf(this.getPlayerTurn());
c.getDamageHistory().setNotBlockedSinceLastUpkeepOf(this.getPlayerTurn());
c.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(this.getPlayerTurn());
c.getDamageHistory().setNotAttackedSinceLastUpkeepOf(playerTurn);
c.getDamageHistory().setNotBlockedSinceLastUpkeepOf(playerTurn);
c.getDamageHistory().setNotBeenBlockedSinceLastUpkeepOf(playerTurn);
}
game.getUpkeep().executeUntilEndOfPhase(this.getPlayerTurn());
game.getUpkeep().registerUntilEndCommand(this.getPlayerTurn());
game.getUpkeep().executeUntilEndOfPhase(playerTurn);
game.getUpkeep().registerUntilEndCommand(playerTurn);
break;
case COMBAT_END:
@@ -451,7 +426,7 @@ public class PhaseHandler implements java.io.Serializable {
eventEndCombat = new GameEventCombatEnded(attackers, blockers);
}
combat = null;
this.getPlayerTurn().resetAttackedThisCombat();
playerTurn.resetAttackedThisCombat();
if (eventEndCombat != null) {
game.fireEvent(eventEndCombat);
@@ -459,11 +434,11 @@ public class PhaseHandler implements java.io.Serializable {
break;
case CLEANUP:
this.bPreventCombatDamageThisTurn = false;
if (!this.bRepeatCleanup) {
this.setPlayerTurn(this.handleNextTurn());
bPreventCombatDamageThisTurn = false;
if (!bRepeatCleanup) {
setPlayerTurn(handleNextTurn());
}
this.planarDiceRolledthisTurn = 0;
planarDiceRolledthisTurn = 0;
// Play the End Turn sound
game.fireEvent(new GameEventTurnEnded());
break;
@@ -498,7 +473,7 @@ public class PhaseHandler implements java.io.Serializable {
}
}
this.nCombatsThisTurn++;
nCombatsThisTurn++;
// Prepare and fire event 'attackers declared'
Multimap<GameEntity, Card> attackersMap = ArrayListMultimap.create();
@@ -544,11 +519,10 @@ public class PhaseHandler implements java.io.Serializable {
}
game.getTriggerHandler().resetActiveTriggers();
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
}
private void declareBlockersTurnBasedAction() {
Player p = playerTurn;
@@ -688,10 +662,10 @@ public class PhaseHandler implements java.io.Serializable {
a.getDamageHistory().setCreatureGotBlockedThisCombat(true);
}
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
}
private static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) {
Cost blockCost = new Cost(ManaCost.ZERO, true);
// 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);
}
/**
* Checks if is prevent combat damage this turn.
*
* @return true, if is prevent combat damage this turn
*/
public final boolean isPreventCombatDamageThisTurn() {
return this.bPreventCombatDamageThisTurn;
return bPreventCombatDamageThisTurn;
}
/**
* <p>
* handleNextTurn.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
private Player handleNextTurn() {
game.getStack().onNextTurn();
for (final Player p1 : game.getPlayers()) {
@@ -762,35 +723,28 @@ public class PhaseHandler implements java.io.Serializable {
}
}
}
return next;
}
/**
* <p>
* getNextActivePlayer.
* </p>
*
* @return a {@link forge.game.player.Player} object.
*/
private Player getNextActivePlayer() {
ExtraTurn extraTurn = !this.extraTurns.isEmpty() ? this.extraTurns.pop() : null;
Player nextPlayer = extraTurn != null ? extraTurn.getPlayer() : game.getNextPlayerAfter(this.getPlayerTurn());
ExtraTurn extraTurn = !extraTurns.isEmpty() ? extraTurns.pop() : null;
Player nextPlayer = extraTurn != null ? extraTurn.getPlayer() : game.getNextPlayerAfter(playerTurn);
if (extraTurn != null) {
// 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.")) {
return getNextActivePlayer();
}
} else
}
else {
nextPlayer.setExtraTurn(false);
}
if (nextPlayer.hasKeyword("Skip your next turn.")) {
nextPlayer.removeKeyword("Skip your next turn.");
if( null == extraTurn )
this.setPlayerTurn(nextPlayer);
setPlayerTurn(nextPlayer);
return getNextActivePlayer();
}
@@ -808,7 +762,7 @@ public class PhaseHandler implements java.io.Serializable {
}
crd.untap();
if( null == extraTurn )
this.setPlayerTurn(nextPlayer);
setPlayerTurn(nextPlayer);
return getNextActivePlayer();
}
}
@@ -824,152 +778,55 @@ public class PhaseHandler implements java.io.Serializable {
nextPlayer.addKeyword("Schemes can't be set in motion this turn.");
}
}
return nextPlayer;
}
/**
* <p>
* 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);
public final synchronized boolean is(final PhaseType phase0, final Player player0) {
return phase == phase0 && playerTurn.equals(player0);
}
/**
* <p>
* is.
* </p>
*
* @param phase0
* a {@link forge.game.phase.PhaseType} object.
* @return a boolean.
*/
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() {
if (this.extraTurns.isEmpty()) {
return game.getNextPlayerAfter(this.getPlayerTurn());
if (extraTurns.isEmpty()) {
return game.getNextPlayerAfter(playerTurn);
}
return this.extraTurns.peek().getPlayer();
return extraTurns.peek().getPlayer();
}
/**
* <p>
* addExtraTurn.
* </p>
*
* @param player
* a {@link forge.game.player.Player} object.
*/
public final ExtraTurn addExtraTurn(final Player player) {
// use a stack to handle extra turns, make sure the bottom of the stack
// restores original turn order
if (this.extraTurns.isEmpty()) {
this.extraTurns.push(new ExtraTurn(game.getNextPlayerAfter(this.getPlayerTurn())));
if (extraTurns.isEmpty()) {
extraTurns.push(new ExtraTurn(game.getNextPlayerAfter(playerTurn)));
}
return this.extraTurns.push(new ExtraTurn(player));
return extraTurns.push(new ExtraTurn(player));
}
/**
* <p>
* addExtraPhase.
* </p>
*
*/
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.
// If multiple extra phases are created after the same phase, the most recently created phase will occur first.
if (!this.extraPhases.containsKey(afterPhase)) {
this.extraPhases.put(afterPhase, new Stack<PhaseType>());
if (!extraPhases.containsKey(afterPhase)) {
extraPhases.put(afterPhase, new Stack<PhaseType>());
}
this.extraPhases.get(afterPhase).push(extraPhase);
extraPhases.get(afterPhase).push(extraPhase);
}
/**
* <p>
* isFirstCombat.
* </p>
*
* @return a boolean.
*/
public final boolean isFirstCombat() {
return (this.nCombatsThisTurn == 1);
return (nCombatsThisTurn == 1);
}
/**
* <p>
* isFirstUpkeep.
* </p>
*
* @return a boolean.
*/
public final boolean isFirstUpkeep() {
return (this.nUpkeepsThisTurn == 0);
return (nUpkeepsThisTurn == 0);
}
/**
* <p>
* isFirstUpkeepThisGame.
* </p>
*
* @return a boolean.
*/
public final boolean isFirstUpkeepThisGame() {
return (this.nUpkeepsThisGame == 0);
return (nUpkeepsThisGame == 0);
}
/**
* <p>
* isPreCombatMain.
* </p>
*
* @return a boolean.
*/
public final boolean isPreCombatMain() {
return (this.nCombatsThisTurn == 0);
return (nCombatsThisTurn == 0);
}
private final static boolean DEBUG_PHASES = false;
@@ -996,7 +853,7 @@ public class PhaseHandler implements java.io.Serializable {
sw.start();
}
game.fireEvent(new GameEventPlayerPriority(getPlayerTurn(), getPhase(), getPriorityPlayer()));
game.fireEvent(new GameEventPlayerPriority(playerTurn, phase, getPriorityPlayer()));
SpellAbility chosenSa = null;
int loopCount = 0;
@@ -1017,7 +874,7 @@ public class PhaseHandler implements java.io.Serializable {
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
System.out.println("Active player is no longer in the game...");
pPlayerPriority = game.getNextPlayerAfter(this.getPriorityPlayer());
pPlayerPriority = game.getNextPlayerAfter(getPriorityPlayer());
pFirstPriority = pPlayerPriority;
}
@@ -1045,13 +902,13 @@ public class PhaseHandler implements java.io.Serializable {
}
}
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
// the firstAction is the player who gained Priority First in this segment
// of Priority
Player nextPlayer = game.getNextPlayerAfter(this.getPriorityPlayer());
Player nextPlayer = game.getNextPlayerAfter(getPriorityPlayer());
if (game.isGameOver() || nextPlayer == null) { return; } // conceded?
@@ -1060,13 +917,13 @@ public class PhaseHandler implements java.io.Serializable {
if (pFirstPriority == nextPlayer) {
if (game.getStack().isEmpty()) {
if (playerTurn.hasLost()) {
this.setPriority(game.getNextPlayerAfter(playerTurn));
setPriority(game.getNextPlayerAfter(playerTurn));
} else {
this.setPriority(playerTurn);
setPriority(playerTurn);
}
// end phase
this.givePriorityToPlayer = true;
givePriorityToPlayer = true;
onPhaseEnd();
advanceToNextPhase();
onPhaseBegin();
@@ -1075,12 +932,13 @@ public class PhaseHandler implements java.io.Serializable {
}
else {
// pass the priority to other player
this.pPlayerPriority = nextPlayer;
pPlayerPriority = nextPlayer;
}
// If ever the karn's ultimate resolved
if (game.getAge() == GameStage.RestartedByKarn) {
phase = null;
setPhase(null);
game.updatePhaseForView();
game.fireEvent(new GameEventGameRestarted(playerTurn));
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
// 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) {
if (null != phase0) this.phase = phase0;
if (null != player0) {
if (phase0 != null) {
setPhase(phase0);
}
if (player0 != null) {
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
}
public final void endTurnByEffect() {
this.combat = null;
this.extraPhases.clear();
this.phase = PhaseType.CLEANUP;
this.onPhaseBegin();
combat = null;
extraPhases.clear();
setPhase(PhaseType.CLEANUP);
onPhaseBegin();
}
public final void setPreventCombatDamageThisTurn(final boolean b) {
this.bPreventCombatDamageThisTurn = true;
bPreventCombatDamageThisTurn = true;
}
/**
* @return the planarDiceRolledthisTurn
*/
public int getPlanarDiceRolledthisTurn() {
return planarDiceRolledthisTurn;
}
public void incPlanarDiceRolledthisTurn() {
this.planarDiceRolledthisTurn++;
planarDiceRolledthisTurn++;
}
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() {
givePriorityToPlayer = true;
}
public final void setPlayerDeclaresBlockers(Player player) {
this.playerDeclaresBlockers = player;
public final void setPlayerDeclaresAttackers(Player player) {
playerDeclaresAttackers = player;
}
public final void setPlayerDeclaresAttackers(Player player) {
this.playerDeclaresAttackers = player;
public final void setPlayerDeclaresBlockers(Player player) {
playerDeclaresBlockers = player;
}
public void endCombat() {

View File

@@ -70,7 +70,7 @@ public class StaticAbilityCantBeCast {
if (params.containsKey("NumLimitEachTurn") && activator != null) {
int limit = Integer.parseInt(params.get("NumLimitEachTurn"));
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);
if (CardLists.filterControlledBy(thisTurnCast, activator).size() < limit) {
return false;

View File

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

View File

@@ -1,6 +1,8 @@
package forge.trackable;
import forge.card.CardRarity;
import forge.game.GameType;
import forge.game.phase.PhaseType;
import forge.game.zone.ZoneType;
import forge.trackable.TrackableTypes;
import forge.trackable.TrackableTypes.TrackableType;
@@ -114,7 +116,20 @@ public enum TrackableProperty {
BandsWithDefenders(null),
BandsWithBlockers(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;