Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions forge-game/src/main/java/forge/game/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ public class Game {
public boolean EXPERIMENTAL_RESTORE_SNAPSHOT = false;
// While this is false here, its really set by the Match/Preferences

// If this merges with LKI In the future, it will need to change forms
private GameSnapshot previousGameState = null;
private GameSnapshot pendingSnapshot = null;
private final Deque<GameSnapshot> undoStack = new ArrayDeque<>();
private static final int MAX_UNDO_DEPTH = 10;
private CardCollection lastStateBattlefield = new CardCollection();
private CardCollection lastStateGraveyard = new CardCollection();

Expand Down Expand Up @@ -186,23 +188,49 @@ public CardCollectionView getLastStateGraveyard() {
}

public void stashGameState() {
// Take a snapshot of the current state to restore to previous state
// Save a pending snapshot before the player chooses an action
if (EXPERIMENTAL_RESTORE_SNAPSHOT) {
previousGameState = new GameSnapshot(this);
previousGameState.makeCopy();
pendingSnapshot = new GameSnapshot(this);
pendingSnapshot.makeCopy();
}
}

public void commitGameState() {
// Push the pending snapshot onto the undo stack (called after a successful action)
if (pendingSnapshot != null) {
undoStack.push(pendingSnapshot);
pendingSnapshot = null;
if (undoStack.size() > MAX_UNDO_DEPTH) {
undoStack.removeLast();
}
}
}

public boolean restoreGameState() {
// Restore game state snapshot
if (previousGameState == null || !EXPERIMENTAL_RESTORE_SNAPSHOT) {
// Restore from pending snapshot only (used for rollback of a failed spell/ability).
// This must NOT fall through to the undo stack — that's for deliberate user undo only.
if (!EXPERIMENTAL_RESTORE_SNAPSHOT || pendingSnapshot == null) {
return false;
}
pendingSnapshot.restoreGameState(this);
pendingSnapshot = null;
return true;
}

previousGameState.restoreGameState(this);
public boolean undoToLastSnapshot() {
if (!EXPERIMENTAL_RESTORE_SNAPSHOT || undoStack.isEmpty()) return false;
GameSnapshot snapshot = undoStack.pop();
snapshot.restoreGameState(this);
// Re-stash so the pending snapshot reflects the restored state,
// not the pre-undo state that is now stale.
stashGameState();
return true;
}

public boolean canUndoToSnapshot() {
return EXPERIMENTAL_RESTORE_SNAPSHOT && !undoStack.isEmpty();
}

public void copyLastState() {
lastStateBattlefield.clear();
lastStateGraveyard.clear();
Expand Down
260 changes: 236 additions & 24 deletions forge-game/src/main/java/forge/game/GameSnapshot.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,7 @@ public void mainLoopStep() {
// 117.3c If a player has priority when they cast a spell, activate an ability, [play a land]
// that player receives priority afterward.
pFirstPriority = pPlayerPriority; // all opponents have to pass before stack is allowed to resolve
game.commitGameState(); // push pre-action snapshot onto undo stack
} else if (game.EXPERIMENTAL_RESTORE_SNAPSHOT) {
rollback = true;
}
Expand Down
75 changes: 74 additions & 1 deletion forge-game/src/main/java/forge/game/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,9 @@ public int getSurveilThisTurn() {
public void resetSurveilThisTurn() {
surveilThisTurn = 0;
}
public void setSurveilThisTurn(int n) {
surveilThisTurn = n;
}

public boolean canMulligan() {
return !getZone(ZoneType.Hand).isEmpty();
Expand Down Expand Up @@ -1278,12 +1281,18 @@ public final void resetNumDrawnThisTurn() {
public final int getNumDrawnThisTurn() {
return numDrawnThisTurn;
}
public final void setNumDrawnThisTurn(int n) {
numDrawnThisTurn = n;
}
public final int getNumDrawnLastTurn() {
return numDrawnLastTurn;
}
public final int numDrawnThisDrawStep() {
return numDrawnThisDrawStep;
}
public final void setNumDrawnThisDrawStep(int n) {
numDrawnThisDrawStep = n;
}

/**
* Returns PlayerZone corresponding to the given zone of game.
Expand Down Expand Up @@ -1405,6 +1414,9 @@ public final void resetNumRollsThisTurn() {
public final int getNumRollsThisTurn() {
return numRollsThisTurn;
}
public final void setNumRollsThisTurn(int n) {
numRollsThisTurn = n;
}
public void roll() {
numRollsThisTurn++;
}
Expand All @@ -1415,6 +1427,9 @@ public final void resetNumFlipsThisTurn() {
public final int getNumFlipsThisTurn() {
return numFlipsThisTurn;
}
public final void setNumFlipsThisTurn(int n) {
numFlipsThisTurn = n;
}
public void flip() {
numFlipsThisTurn++;
}
Expand Down Expand Up @@ -1463,6 +1478,9 @@ public final void addTokensCreatedThisTurn(Card token) {
public final int getNumTokenCreatedThisTurn() {
return numTokenCreatedThisTurn;
}
public final void setNumTokenCreatedThisTurn(int n) {
numTokenCreatedThisTurn = n;
}

public final void resetNumTokenCreatedThisTurn() {
numTokenCreatedThisTurn = 0;
Expand All @@ -1482,6 +1500,9 @@ public final void addForetoldThisTurn() {
public final void resetNumForetoldThisTurn() {
numForetoldThisTurn = 0;
}
public final void setNumForetoldThisTurn(int n) {
numForetoldThisTurn = n;
}

public final List<Card> getDiscardedThisTurn() {
return discardedThisTurn;
Expand All @@ -1499,6 +1520,9 @@ public final void addExploredThisTurn() {
public final void resetNumExploredThisTurn() {
numExploredThisTurn = 0;
}
public final void setNumExploredThisTurn(int n) {
numExploredThisTurn = n;
}

public int getNumCardsInHandStartedThisTurnWith() {
return numCardsInHandStartedThisTurnWith;
Expand Down Expand Up @@ -1773,7 +1797,7 @@ public final int setNumPowerSurgeLands(final int n) {
public final Card getLastDrawnCard() {
return lastDrawnCard;
}
private Card setLastDrawnCard(final Card c) {
public Card setLastDrawnCard(final Card c) {
lastDrawnCard = c;
return lastDrawnCard;
}
Expand Down Expand Up @@ -1819,6 +1843,9 @@ public final void incrementTurn() {
public final int getLastTurnNr() {
return this.lastTurnNr;
}
public final void setLastTurnNr(int n) {
this.lastTurnNr = n;
}

public boolean hasTappedLandForManaThisTurn() {
return tappedLandForManaThisTurn;
Expand Down Expand Up @@ -1880,6 +1907,9 @@ public final void incrementVenturedThisTurn() {
public final void resetVenturedThisTurn() {
venturedThisTurn = 0;
}
public final void setVenturedThisTurn(int n) {
venturedThisTurn = n;
}

public final List<Card> getCompletedDungeons() {
return completedDungeons;
Expand Down Expand Up @@ -2282,6 +2312,9 @@ public final void addInvestigatedThisTurn() {
public final void resetInvestigatedThisTurn() {
investigatedThisTurn = 0;
}
public final void setInvestigatedThisTurn(int n) {
investigatedThisTurn = n;
}

public final void addSacrificedThisTurn(final Card cpy, final SpellAbility source) {
// Play the Sacrifice sound
Expand Down Expand Up @@ -2340,6 +2373,9 @@ public final void setSpellsCastLastTurn(int num) {
public final int getSpellsCastThisGame() {
return spellsCastThisGame;
}
public final void setSpellsCastThisGame(int n) {
spellsCastThisGame = n;
}
public final void resetSpellCastThisGame() {
spellsCastThisGame = 0;
}
Expand All @@ -2361,6 +2397,12 @@ public final void setLifeGainedThisTurn(final int n) {
public final int getLifeGainedTimesThisTurn() {
return lifeGainedTimesThisTurn;
}
public final void setLifeGainedTimesThisTurn(int n) {
lifeGainedTimesThisTurn = n;
}
public final void setLifeGainedByTeamThisTurn(int n) {
lifeGainedByTeamThisTurn = n;
}

public final int getLifeLostThisTurn() {
return lifeLostThisTurn;
Expand Down Expand Up @@ -3397,6 +3439,31 @@ public CardCollectionView getGainedOwnership() {
return gainedOwnership;
}

// === Snapshot support setters ===

public void setSimultaneousDamage(int n) { simultaneousDamage = n; }
public int getSimultaneousDamage() { return simultaneousDamage; }
public void setTriedToDrawFromEmptyLibrary(boolean b) { triedToDrawFromEmptyLibrary = b; }
public boolean getTriedToDrawFromEmptyLibrary() { return triedToDrawFromEmptyLibrary; }

public void setDiscardedThisTurn(List<Card> cards) { discardedThisTurn = new ArrayList<>(cards); }
public void setSacrificedThisTurn(List<Card> cards) { sacrificedThisTurn = new ArrayList<>(cards); }
public void setDiceRollsThisTurn(List<Integer> rolls) { diceRollsThisTurn = new ArrayList<>(rolls); }
public List<Card> getSpellsCastSinceBeginningOfLastTurn() { return spellsCastSinceBeginningOfLastTurn; }
public void setSpellsCastSinceBeginningOfLastTurn(List<Card> cards) { spellsCastSinceBeginningOfLastTurn = new ArrayList<>(cards); }
public void setAttackedThisTurn(Map<GameEntity, List<Card>> map) { attackedThisTurn = new HashMap<>(map); }
public Map<GameEntity, List<Card>> getAttackedThisTurn() { return attackedThisTurn; }
public void setAttackedPlayersThisCombat(List<Player> players) { attackedPlayersThisCombat = new ArrayList<>(players); }
public void setCompletedDungeons(List<Card> dungeons) { completedDungeons = new ArrayList<>(dungeons); }
public void setLostOwnership(CardCollection cards) { lostOwnership = new CardCollection(cards); }
public void setGainedOwnership(CardCollection cards) { gainedOwnership = new CardCollection(cards); }
public void setElementalBendThisTurn(EnumSet<TriggerType> set) { elementalBendThisTurn = set.isEmpty() ? EnumSet.noneOf(TriggerType.class) : EnumSet.copyOf(set); }
public EnumSet<TriggerType> getElementalBendThisTurn() { return elementalBendThisTurn; }
public void setCurrentPlanes(CardCollection cards) { currentPlanes = new CardCollection(cards); }
public CardCollection getCurrentPlanes() { return currentPlanes; }
public void setPlaneswalkedToThisTurn(CardCollection cards) { planeswalkedToThisTurn = new CardCollection(cards); }
public void setActiveScheme(Card c) { activeScheme = c; }

@Override
public PlayerView getView() {
return view;
Expand Down Expand Up @@ -3971,6 +4038,9 @@ public void rollToVisitAttractions() {
public int getAttractionsVisitedThisTurn() {
return this.attractionsVisitedThisTurn;
}
public void setAttractionsVisitedThisTurn(int n) {
this.attractionsVisitedThisTurn = n;
}

public int getCrankCounter() {
return this.crankCounter;
Expand Down Expand Up @@ -4049,6 +4119,9 @@ public List<String> getUnlockedDoors() {
public int getDevotionMod() {
return devotionMod;
}
public void setDevotionMod(int n) {
devotionMod = n;
}

public void afterStaticAbilityLayer(StaticAbilityLayer layer) {
if (layer != StaticAbilityLayer.TEXT) {
Expand Down
11 changes: 11 additions & 0 deletions forge-game/src/main/java/forge/game/zone/MagicStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -933,9 +933,16 @@ public final boolean hasStateTrigger(final int triggerID) {
public final List<Card> getSpellsCastThisTurn() {
return thisTurnCast;
}
public final void setSpellsCastThisTurn(List<Card> cards) {
thisTurnCast.clear();
thisTurnCast.addAll(cards);
}
public final List<Card> getSpellsCastLastTurn() {
return lastTurnCast;
}
public final void setSpellsCastLastTurn(List<Card> cards) {
lastTurnCast = Lists.newArrayList(cards);
}

public final void onNextTurn() {
final Player active = game.getPhaseHandler().getPlayerTurn();
Expand All @@ -962,6 +969,10 @@ public void addAbilityActivatedThisTurn(SpellAbility sa, final Card source) {
public List<SpellAbility> getAbilityActivatedThisTurn() {
return thisTurnActivated;
}
public void setAbilityActivatedThisTurn(List<SpellAbility> abilities) {
thisTurnActivated.clear();
thisTurnActivated.addAll(abilities);
}

public final boolean hasSourceOnStack(final Card source, final Predicate<SpellAbility> pred) {
if (source == null) {
Expand Down
6 changes: 6 additions & 0 deletions forge-game/src/main/java/forge/game/zone/Zone.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public final void reorder(final Card c, final int index) {
cardList.add(index, c);
}

/** Reorder the zone to match the given card list atomically (no empty-zone window). */
public final void setCardOrder(List<Card> order) {
cardList.clear();
cardList.addAll(order);
}

public final void add(final Card c) {
add(c, null);
}
Expand Down
Loading