Skip to main content

Battle State Machine

GCS ยท Runtime

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.

Game Card Monitor Phase tab showing the current battle phase and transition history during Play Mode

Phase listโ€‹

GCSApi.Phase returns the current BattlePhase. Outside an active battle it returns BattlePhase.None.

PhaseWhat happensPlayer input
NoneNo active battle.No card play.
InitializeBattle state is being built.No card play.
PlayerTurnStartArmor decay, energy refill, draw phase, start-turn status/card timings.No card play until the phase enters PlayerPhase.
PlayerPhasePlayer may play hand cards and end the turn.Card play allowed when affordable and playable.
PlayerTurnEndDiscard phase, end-turn status timings, status decay, hook ticking, delayed triggers.No card play.
EnemyPhaseEach alive enemy resolves start-turn statuses, behavior, end-turn statuses, and gaps.No card play.
BattleWonBattle is won and a reward may be offered.Reward selection may be required.
BattleLostBattle 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:

  1. Create a fresh BattleStateView.
  2. Build player state from the encounter player unit.
  3. Build enemy states from encounter enemies.
  4. Build the initial draw pile from the run master deck or the player starting deck.
  5. Shuffle the draw pile.
  6. Prioritize innate cards for the opening hand.
  7. Bind the optional GES event bridge.
  8. Draw the starting hand.
  9. Raise OnBattleStarted.
  10. Resolve enemy intents.
  11. Run battle-start status behavior.
  12. 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.

info

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 Unplayable keyword.
  • The card playable hook cancels or returns false.

When play succeeds, GCS:

  1. Spends energy.
  2. Adds the card to PlayedThisTurn.
  3. Resolves the target, falling back to the first alive enemy when no target is provided.
  4. Raises OnCardPlayed.
  5. Runs the card's on-play FlowGraph.
  6. Registers card hooks.
  7. Moves the card from hand to discard or exhaust.
  8. Checks pending win/loss transitions.
  9. Ends the player turn if an effect requested it.
tip

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:

  1. Raise OnDiscardPhase.
  2. Run discard-phase status behavior.
  3. Move normal hand cards to discard, keep Retain cards, and exhaust Ethereal cards.
  4. Run player status OnTurnEnd.
  5. Tick per-turn statuses.
  6. Tick active hooks.
  7. Tick card cost rules.
  8. Tick delayed triggers scheduled for PlayerTurnEnd.
  9. Raise OnTurnEnded.
  10. If an extra turn is pending, enter PlayerTurnStart.
  11. 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 stateAPI indicatorMeaning
Effect choiceGCSApi.IsWaitingForChoiceA Choice node is waiting for the player to select or skip.
Battle rewardGCSApi.IsWaitingForRewardA 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:

  1. Battle-end status behavior runs.
  2. Reward candidates are rolled.
  3. OnBattleRewardOffered is raised.
  4. The state machine waits for reward selection or skip.
  5. OnBattleRewardSelected or OnBattleRewardSkipped is raised.
  6. OnBattleEnded is raised with Won = true.

When the battle is lost:

  1. Battle-end status behavior runs.
  2. OnBattleEnded is raised with Won = false.

GCSApi.IsBattleOver is true in BattleWon or BattleLost.


Safe phase checksโ€‹

TaskCheck
Show battle HUDGCSApi.IsBattleActive
Enable card buttonsGCSApi.Phase == BattlePhase.PlayerPhase and GCSApi.CanPlayCard(card)
Show end-turn buttonGCSApi.Phase == BattlePhase.PlayerPhase
Show choice UIGCSApi.IsWaitingForChoice
Show reward UIGCSApi.IsWaitingForReward
Show victory/defeat overlayGCSApi.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.