feat(observation): expose initiative order and totals

This commit is contained in:
2026-09-10 12:04:10 +02:00
parent 4e115f9176
commit c5cfa78f87
4 changed files with 170 additions and 2 deletions
+12 -1
View File
@@ -448,7 +448,9 @@ class Observation:
on the map (sorted by type for determinism), carrying
``{type, move_cost, blocks_los, cover}`` — derived from
``grid.terrain_at((r, c))`` over the entire map (the Grid does not expose
``MapSpec.legend``).
``MapSpec.legend``). ``initiative_order`` lists the combatant ids in the
current round's initiative order (``_build_round_order()``, read-only,
no RNG): the first entry acts next this round.
"""
round: int
@@ -460,6 +462,7 @@ class Observation:
threatened: tuple[tuple[bool, ...], ...] = ()
terrain_semantics: tuple[dict[str, object], ...] = ()
condition_definitions: tuple[tuple[str, dict[str, object]], ...] = ()
initiative_order: tuple[str, ...] = ()
def to_dict(self) -> dict[str, object]:
"""Return a JSON-serializable dict with the pinned top-level keys."""
@@ -473,6 +476,7 @@ class Observation:
"threatened": self.threatened,
"terrain_semantics": self.terrain_semantics,
"condition_definitions": self.condition_definitions,
"initiative_order": self.initiative_order,
}
@@ -710,6 +714,9 @@ class CombatEngine:
(name, _condition_definition_dict(cond))
for name, cond in sorted(CONDITIONS_BY_NAME.items())
)
initiative_order = tuple(
s.combatant.id for s in self._build_round_order()
)
return Observation(
round=self._current_round,
acting_combatant=acting_index,
@@ -720,6 +727,7 @@ class CombatEngine:
threatened=threatened,
terrain_semantics=terrain_semantics,
condition_definitions=condition_definitions,
initiative_order=initiative_order,
)
def _threat_grid(self, state: CombatantState) -> tuple[tuple[bool, ...], ...]:
@@ -809,6 +817,9 @@ class CombatEngine:
"creature_type": state.combatant.creature_type,
"bab": state.combatant.bab,
"initiative_mod": state.combatant.initiative_mod,
"initiative_total": self._initiative_keys.get(
state.combatant.id, (0, 0, -999)
)[0],
"casting_ability": (
state.combatant.casting_ability if state.combatant.spells else None
),