Combat Events
The RPG Companion App fires a set of global events throughout the combat encounter lifecycle.
You can use these event names in a mechanic's event_names parameter to trigger effects at
specific moments in combat — for example, restoring a resource at the start of each turn,
applying a status effect when the round changes, or showing a message when the encounter ends.
When a mechanic is triggered by an event, the event's payload is injected into your stat context
under a special stat called $event. Each key in the payload becomes accessible via the usual
dot notation: $event.<key>.
Only stat managers (resources / characters) and primitives (strings, integers, booleans) in the payload are traversable in formulas. Other internal object types are not usable.
Use null-safe access (?. and ? suffix) when reading from $event, since the payload content
may vary:
mechanic my_mechanic(event_names = "turnAdvanced") =
when {
$event?.combatant?.id? == id -> setStat(stat = "actions_remaining", new_value = 3, aggregation_type = "set"),
};
Lifecycle events
These events mark the start and end of an encounter and are useful as mechanic triggers on their
own. The encounter value they carry is an internal object and is not accessible in formulas.
| Event name | Fires when… |
|---|---|
encounterStarted | A new encounter begins. |
encounterEnded | The encounter finishes (manually or via resolution). |
encounterMinimized | The encounter is hidden (minimized to the FAB). |
encounterResumed | A minimized encounter is brought back. |
encounterEnded payload
| Key | Type | Description |
|---|---|---|
resolution_message | string | (optional) The message returned by encounter_resolution_message if the encounter ended automatically through that formula. Not present when ended manually. |
Turn & round events
turnAdvanced
Fires every time the active turn moves to a different combatant, including when navigating back with "Previous Turn".
| Key | Type | Description |
|---|---|---|
combatant | stat manager | The stat context of the combatant whose turn it now is. May be null if no resource is loaded for that combatant. |
Because combat events are global, every character whose mechanics list turnAdvanced in
event_names will fire — not just the character whose turn it is. Guard with an identity check
if you only want to react on your own turn:
mechanic reset_actions_on_turn_advanced(event_names = "turnAdvanced") =
when {
$event?.combatant?.id? == id -> sequence(
effects = [
setStat(stat = "actions_remaining", new_value = 3, aggregation_type = "set"),
setStat(stat = "reaction_available", new_value = 1, aggregation_type = "set"),
],
),
};
roundAdvanced
Fires when a full round completes and the initiative order resets.
| Key | Type | Description |
|---|---|---|
round | integer | The new round number (starts at 2 on the first roundAdvanced). |
mechanic reset_focus_on_round_advanced(event_names = "roundAdvanced") =
setStat(stat = "focus_points", new_value = focus_points_max, aggregation_type = "set");
Combatant roster events
These events fire when the roster changes mid-encounter.
| Event name | Fires when… |
|---|---|
combatantAdded | A combatant is added to an ongoing encounter. |
combatantRemoved | A combatant is removed from the encounter. |
combatantUpdated | A combatant's initiative or status is changed. |
combatantRemoved payload
| Key | Type | Description |
|---|---|---|
combatantId | string | The record ID of the removed combatant's resource. |
combatantAdded and combatantUpdated carry a combatant payload key, but it is an internal
object and is not accessible in formulas at this time.
The $combatants variable
While an encounter is active, every combatant's stat context has a special temporary stat called
$combatants. This is a list of stat managers — one for each combatant currently in the
encounter, in initiative order. It is available anywhere a formula is evaluated during combat:
in calc stats, mechanic conditions, encounterResolutionMessage, and
isEncounterWithinDifficultyLevel.
Each element in $combatants behaves like any other stat manager: you can read its stats with
the usual dot notation and use it in lambdas.
Filtering combatants by type
The app injects a $combatant_type temp stat on each combatant, containing its combatant type
ID (e.g. "player", "monster", "npc"). Use it to separate allies from enemies:
calc resource<monster>[] enemies(name = "Enemies") =
filter($combatants, filter = ($combatant) => $combatant.$combatant_type? == "monster");
calc resource<player>[] players(name = "Players") =
filter($combatants, filter = ($combatant) => $combatant.$combatant_type? == "player");
Checking for encounter resolution
A common use case for $combatants is driving the encounterResolutionMessage formula:
calc string encounter_resolution_message(name = "Encounter resolution message") =
when {
isEmpty(filter(enemies, filter = ($combatant) => $combatant.current_hp > 0)) ->
"All enemies have been defeated!",
else -> null,
};
Computing values across all combatants
calc integer total_enemy_hp(name = "Total Enemy HP") =
add(map(enemies, mapper = ($enemy) => $enemy.current_hp ?? 0));
$combatants is also available inside isEncounterWithinDifficultyLevel for encounter
building — see CombatSystem for details on those formulas.