mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 03:08:02 +00:00
Support updating game view
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user