Skip to main content

Status lifecycle

Guide

Follow the complete runtime path from the first applied stack through active removal or natural expiration, including behavior entries, Hooks, and events

Once a status enters a unit, it has an independent live stack count. Applying, changing, decaying, removing, or expiring it executes the matching Behavior and event. The Unit tab in Game Card Monitor shows the statuses each unit currently holds

The Unit tab in Game Card Monitor showing player and enemy HP, Armor, and current status stacks

The snapshot above places player and enemy HP, Armor, and status stacks together. The status definition determines stacking and decay rules, while unit state records the current count and applier. They are separate data layers

Status definitions and live stacks

GameStatus is an authored definition containing the name, icon, stacking method, stack cap, decay rule, and Behavior. In battle, each UnitState separately records the statuses it holds, current stack counts, and known appliers

Changing a status on one unit does not affect another unit or write back to the GameStatus asset. See status authoring for field settings and the complete authoring flow

NeedAPI
Read the current countGCSApi.GetStatusStacks(unit, status)
Check for at least N stacksGCSApi.HasStatus(unit, status, minStacks)
Read the latest successful applierGCSApi.GetStatusApplier(unit, status)
Read every status on a unitGCSApi.StatusesOn(unit)

When the unit is null or holds no statuses, these methods return 0, false, null, or an empty map. Callers do not need to create placeholder collections

Applying a status

Use GCSApi.ApplyStatus(target, status, stacks, source) or the FlowGraph Change Status node to apply a positive count. Runtime processes it in this order:

  1. Validate the target, status, and incoming count
  2. Let the source unit's OnInflictStatus Hook modify or cancel the count
  3. Let the target unit's OnReceiveStatus Hook modify or cancel it again
  4. Merge it with the current count using Additive, Max, or Replace
  5. Cap the final count when MaxStacks > 0; 0 and negative values are unlimited, though 0 produces an editor warning
  6. Only when the stored count actually changes, save the result and latest applier, then publish OnStatusChanged and OnStatusInflicted
  7. Run the source-side On Status Inflicted, the applied status's runtime OnApplied branch exposed as On Status Received, and the holder's On Status Stacks Changed branches

A capped or Max application that leaves the stored count unchanged completes without either public transition event or any of those application Entries

tip

When source is null, runtime records the holder as the applier. Every later successful application that changes the stored count replaces this record with its source or holder; a capped or Max no-op, ModifyStatusStacks, and RemoveStatus do not replace it

Changing, removing, and retriggering

TaskRecommended entry
Add stacksApplyStatus() or a positive Delta on Change Status
Remove a specific number of stacksRemoveStatus() or a negative Delta on Change Status
Change the count to an exact valueModifyStatusStacks() or Set mode on Change Status
Run the status behavior again without changing stacksReapply Status or RetriggerStatus on the Controller
tip
  • Set from no status to a positive count runs the complete application pipeline, including Hooks, stack rules, applier tracking, events, and OnApplied; Set on an existing status changes the count exactly without another application pipeline or OnApplied
  • Reducing a count to 0 explicitly enters the Removed lifecycle. Natural PerTurn decay to 0 enters Expired, allowing separate presentation and rules

Lifecycle entries and Hooks

A status Behavior has two entry types

TypeExecution timingTypical use
Lifecycle EntryAfter a change has occurredStatus applied, turn start, turn end, or status removed
HookBefore a value is committedModify damage, cost, draw count, Armor, or status stacks
tip
  • A lifecycle Entry continues through regular Action nodes. A Hook must submit its modified value through Write Hook
  • Without a write, runtime keeps the original value
  • Status Hooks and active card Hooks share one priority order, with smaller Priority values resolving first
  • A card Hook with Scope = Turns expires at a future player-turn start; Turns = 1 remains active through the entire intervening enemy phase, so effects such as Aegis protect that enemy turn before expiring
  • Hook control must remain synchronous; direct or nested Wait, Choice, and Delayed Trigger paths cannot supply a deferred Hook result

See Entry nodes and Hook nodes for the available status entries and Hooks

Inside a Status Behavior, Host is the unit carrying the status, Source is the latest successful applier or the holder fallback, and Attacker remains the actual attacker supplied by a damage or reaction context. These roles stay separate so a holder can react to the current attacker without losing the status's recorded source

Turn timing and automatic decay

The diagram above shows the default lifecycle of a PerTurn status. Player statuses decay at the end of the player turn. Enemy statuses decay after that enemy completes its action and turn-end behaviors

TimingStatus work that may run
Battle StartBattle-start behavior
Player Turn StartTurn-start behavior on player statuses
Draw / DiscardDraw-phase or discard-phase behavior
Player Turn EndTurn-end behavior, Tick, and decay on player statuses
Enemy TurnTurn start, action, turn end, Tick, and decay for the active enemy
Battle EndBattle-end behavior on all units

See battle lifecycle for the exact phase order

When several statuses on one holder receive the same timing, GCS uses a captured status order; if one branch opens Wait or Choice, the later status branches wait until it completes, and the timing finishes only once after the captured sequence drains

Status change events

EventUse case
OnStatusChangedRebuild the status list after a stack change
OnStatusTickedPresent an HP or stack change caused by one status resolution
OnStatusInflictedRespond to an application that actually changed the stored stack count
OnStatusExpiredPresent natural expiration
OnStatusRemovedPresent cleansing or active removal
tip
  • OnStatusChanged is the main refresh signal for status UI. Transition events suit animation, audio, statistics, or other one-time handling and should not replace a current-state query
  • When a status deals damage, both the public damage event and the matching damage Entries report the HP actually lost after Hooks, armor, and the target's remaining-HP bound

Refreshing status UI

After receiving OnStatusChanged, a status Widget should read the unit's complete status map again through GCSApi.StatusesOn(unit), then update the interface from Icon, DisplayName, IsDebuff, ShowStackCount, and the current count

tip

Do not change only one value in a local cache. A single FlowGraph can modify multiple statuses, while the event arguments identify only the change that triggered the notification

See custom UI integration for a complete UI example and event reference for event arguments

Common mistakes

MistakeCorrect approach
Edit UnitState.Statuses directlyUse the status API, Controller, or a FlowGraph node
Use a lifecycle Entry to modify a value that has not been committedUse the matching Hook
Expect Set on an existing status to repeat OnAppliedUse a positive Delta or ApplyStatus for another application
Treat Removed and Expired as the same eventHandle active removal and natural expiration separately
Maintain a local status list using only event argumentsRead StatusesOn(unit) again after the event