Battle State Machine
Every battle phase, the exact order of operations inside it, and when card play is legal.
Use this when your UI enables buttons at the wrong time, a turn effect fires in an unexpected order, or a battle refuses to end.The battle state machine owns phase transitions. It decides when the player can act, when enemies act, when delayed triggers tick, and when reward selection blocks battle completion. You can watch every transition live in the Phase tab of the Game Card Monitor.

Phase listโ
GCSApi.Phase returns the current BattlePhase. Outside an active battle it returns BattlePhase.None.
| Phase | What happens | Player input |
|---|---|---|
| None | No active battle. | No card play. |
| Initialize | Battle state is being built. | No card play. |
| PlayerTurnStart | Armor decay, energy refill, draw phase, start-turn status/card timings. | No card play until the phase enters PlayerPhase. |
| PlayerPhase | Player may play hand cards and end the turn. | Card play allowed when affordable and playable. |
| PlayerTurnEnd | Discard phase, end-turn status timings, status decay, hook ticking, delayed triggers. | No card play. |
| EnemyPhase | Each alive enemy resolves start-turn statuses, behavior, end-turn statuses, and gaps. | No card play. |
| BattleWon | Battle is won and a reward may be offered. | Reward selection may be required. |
| BattleLost | Battle is lost. | No card play. |
The core loop cycles four phases; win and loss exits are evaluated by the phase machinery on turn transitions and after card plays:
Battle start flowโ
GCSApi.StartBattle(encounter) delegates to the manager and state machine:
- Create a fresh
BattleStateView. - Build player state from the encounter player unit.
- Build enemy states from encounter enemies.
- Build the initial draw pile from the run master deck or the player starting deck.
- Shuffle the draw pile.
- Prioritize innate cards for the opening hand.
- Bind the optional GES event bridge.
- Draw the starting hand.
- Raise
OnBattleStarted. - Resolve enemy intents.
- Run battle-start status behavior.
- Enter
PlayerTurnStart.
If the encounter is null, the state machine logs an error and does not start the battle.
Player turn startโ
At PlayerTurnStart, GCS:
- Marks the battle as player-owned.
- Applies armor decay to the player.
- Resolves max energy and sets current energy.
- Ticks delayed triggers scheduled for
PlayerTurnStart. - Clears
PlayedThisTurn. - Runs player status
OnTurnStart. - Runs hand-card turn-start timing.
- On turns after the first, raises
OnDrawPhase, runs draw-phase behavior, and draws the per-turn cards. - Resets per-turn attack flags.
- Raises
OnTurnStarted. - Skips to player turn end if the player has pending skipped turns.
- Enters
PlayerPhase.
Armor decay follows the encounter's BattleRules.ArmorDecayRule. The default ResetOnTurnStart resets armor at the start of each of the unit's turns, and an Armor Reset Hook can change the reset value. Persist carries armor across turns untouched.
The first turn draws its starting hand during battle start, before PlayerTurnStart. Turn 1 has no separate draw phase.
Player action phaseโ
TryPlayCard can only succeed in PlayerPhase.
A card play attempt fails when:
- The phase is not
PlayerPhase. - The card is null.
- The player unit is missing.
- The card is not currently in hand.
- The card has no active card definition.
- The player cannot pay the effective energy cost.
- The on-play hook cancels the play.
- The card has the
Unplayablekeyword. - The card playable hook cancels or returns false.
When play succeeds, GCS:
- Spends energy.
- Adds the card to
PlayedThisTurn. - Resolves the target, falling back to the first alive enemy when no target is provided.
- Raises
OnCardPlayed. - Runs the card's on-play FlowGraph.
- Registers card hooks.
- Moves the card from hand to discard or exhaust.
- Checks pending win/loss transitions.
- Ends the player turn if an effect requested it.
Call GCSApi.CanPlayCard(card) before enabling a card button. To decide whether your UI should ask for a target, pass the card definition to GCSApi.RequiresTarget(GCSApi.GetActiveCard(instance)).
Player turn endโ
When GCSApi.EndPlayerTurn() is called in PlayerPhase, the state machine enters PlayerTurnEnd:
- Raise
OnDiscardPhase. - Run discard-phase status behavior.
- Move normal hand cards to discard, keep
Retaincards, and exhaustEtherealcards. - Run player status
OnTurnEnd. - Tick per-turn statuses.
- Tick active hooks.
- Tick card cost rules.
- Tick delayed triggers scheduled for
PlayerTurnEnd. - Raise
OnTurnEnded. - If an extra turn is pending, enter
PlayerTurnStart. - Otherwise enter
EnemyPhase.
Enemy phaseโ
At EnemyPhase, GCS:
- Marks the battle as no longer player-owned.
- Raises
OnEnemyTurnStarted. - Iterates alive enemies in order.
- Applies armor decay to the acting enemy.
- Runs enemy start-turn statuses.
- Resolves enemy behavior unless skipped.
- Runs enemy end-turn statuses.
- Ticks per-turn statuses and hooks.
- Waits between visible enemy actions.
- Ticks delayed triggers scheduled for
EnemyPhaseEnd. - Resolves all enemy intents for the next player turn.
- Increments
TurnNumber. - Enters
PlayerTurnStart.
The demo presentation layer can add attack lunges and gaps between enemies, but the phase logic stays owned by the state machine.
Choice and reward waitsโ
Some graph nodes and battle outcomes intentionally pause progression.
| Wait state | API indicator | Meaning |
|---|---|---|
| Effect choice | GCSApi.IsWaitingForChoice | A Choice node is waiting for the player to select or skip. |
| Battle reward | GCSApi.IsWaitingForReward | A won battle has offered a reward and waits for ApplyReward or SkipReward. |
While waiting, queued transitions do not proceed. Custom UI should disable unrelated controls and show the appropriate picker.
Battle endโ
When the battle is won:
- Battle-end status behavior runs.
- Reward candidates are rolled.
OnBattleRewardOfferedis raised.- The state machine waits for reward selection or skip.
OnBattleRewardSelectedorOnBattleRewardSkippedis raised.OnBattleEndedis raised withWon = true.
When the battle is lost:
- Battle-end status behavior runs.
OnBattleEndedis raised withWon = false.
GCSApi.IsBattleOver is true in BattleWon or BattleLost.
Safe phase checksโ
| Task | Check |
|---|---|
| Show battle HUD | GCSApi.IsBattleActive |
| Enable card buttons | GCSApi.Phase == BattlePhase.PlayerPhase and GCSApi.CanPlayCard(card) |
| Show end-turn button | GCSApi.Phase == BattlePhase.PlayerPhase |
| Show choice UI | GCSApi.IsWaitingForChoice |
| Show reward UI | GCSApi.IsWaitingForReward |
| Show victory/defeat overlay | GCSApi.IsBattleOver and the OnBattleEnded payload |
Do not infer the phase from a single event. Events are notifications. The current phase is always the authoritative state.