Skip to main content

Battle Phases and Execution Order

Guide

Every battle phase, the exact order of operations inside it, and when card play is legal

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 battleNo card play
InitializeBattle state is being builtNo card play
PlayerTurnStartArmor decay, energy refill, draw phase, start-turn status/card timingsNo card play until the phase enters PlayerPhase
PlayerPhasePlayer may play hand cards and end the turnCard play allowed when affordable and playable
PlayerTurnEndDiscard phase, end-turn status timings, status decay, hook ticking, delayed triggersNo card play
EnemyPhaseEach alive enemy resolves start-turn statuses, behavior, end-turn statuses, and gapsNo card play
BattleWonBattle is won and a reward may be offeredReward selection may be required
BattleLostBattle is lostNo 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

Events report what happened; read GCSApi.Phase for the current phase so UI stays aligned even when one operation raises several events