Skip to content

osrlib.crawl.events

The crawl event types, the combined registry, and the any-event parser.

Crawl events subclass the core Event base, inheriting the emission contract (frozen, extra="ignore", dotted snake_case codes, declared outcome-bearing code sets, visibility). CRAWL_EVENT_CLASSES joins the kernel tuple in ALL_EVENT_CLASSES and the AnyEvent union; parse_any_event covers both and the session log uses it.

Visibility follows B/X's hidden-roll doctrine: referee-rolled dice (detection, surprise, reaction, wandering checks) are referee events, and the player-facing events carry behavior and outcomes only — a silent listen is genuinely ambiguous.

ALL_EVENT_CLASSES module-attribute

ALL_EVENT_CLASSES: tuple[type[Event], ...] = (*KERNEL_EVENT_CLASSES, *CRAWL_EVENT_CLASSES)

Every event class the library emits — kernel then crawl, in declaration order.

AnyEvent module-attribute

AnyEvent = Annotated[Union[*ALL_EVENT_CLASSES,], Field(discriminator='event_type')]

Any library event, discriminated by event_type.

AdventureCompletedEvent

Bases: Event

The adventure is over in triumph: the session is in victory.

Follows the QuestCompletedEvent of the quest that concludes the adventure, and carries the same completion beat. name is that quest's authored display name, defaulting empty only because an event logged before the field existed still parses; the engine always fills it. The transition happens once and only from a session still in play — a party that finishes the job after it has already fallen completes the quest and gets no ending event.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.adventure.completed'})

event_type class-attribute instance-attribute

event_type: Literal['adventure_completed'] = 'adventure_completed'

code class-attribute instance-attribute

code: str = 'session.adventure.completed'

visibility class-attribute instance-attribute

quest_id instance-attribute

quest_id: str

name class-attribute instance-attribute

name: str = ''

narrative class-attribute instance-attribute

narrative: str | None = None

AdventureXpAwardEvent

Bases: Event

The end-of-adventure XP award: the totals and the per-head share.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.xp.adventure_award'})

event_type class-attribute instance-attribute

event_type: Literal['adventure_xp_award'] = 'adventure_xp_award'

code class-attribute instance-attribute

code: str = 'session.xp.adventure_award'

visibility class-attribute instance-attribute

monster_xp instance-attribute

monster_xp: int

treasure_xp instance-attribute

treasure_xp: int

share instance-attribute

share: int

survivors instance-attribute

survivors: tuple[str, ...]

BattleEndedEvent

Bases: Event

The battle ended: victory, the party fled, or defeat.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.ended.victory', 'battle.ended.fled', 'battle.ended.defeat'})

event_type class-attribute instance-attribute

event_type: Literal['battle_ended'] = 'battle_ended'

visibility class-attribute instance-attribute

BattleRoundEvent

Bases: Event

A battle round began.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.round.started'})

event_type class-attribute instance-attribute

event_type: Literal['battle_round'] = 'battle_round'

code class-attribute instance-attribute

code: str = 'battle.round.started'

visibility class-attribute instance-attribute

round instance-attribute

round: int

BattleStartedEvent

Bases: Event

Battle began: the range-track machine takes over.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.started'})

event_type class-attribute instance-attribute

event_type: Literal['battle_started'] = 'battle_started'

code class-attribute instance-attribute

code: str = 'battle.started'

visibility class-attribute instance-attribute

CharacterLeveledUpEvent

Bases: Event

One character gained a level — the award's threshold crossing made visible.

Fires immediately after the member's own XpAwardedEvent whenever an XP award crosses a level threshold, whichever surface awarded it (the end-of-adventure award, the immediate timing, or the referee's AwardXP). While the Hit Dice count still grows, hp_roll is the raw die; past name level the gain is the flat-bonus delta with no die, so hp_roll is None and con_applied is false. title is the class's level title at level_after, None past the printed title list (the SRD's lists run only through name level).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.level.gained'})

event_type class-attribute instance-attribute

event_type: Literal['leveled_up'] = 'leveled_up'

code class-attribute instance-attribute

code: str = 'session.level.gained'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

level_before instance-attribute

level_before: int

level_after instance-attribute

level_after: int

hp_gained instance-attribute

hp_gained: int

hp_roll instance-attribute

hp_roll: int | None

con_applied instance-attribute

con_applied: bool

title instance-attribute

title: str | None

CurseRevealedEvent

Bases: Event

A cursed item revealed its true nature — and pins itself to its bearer.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'items.curse.revealed'})

event_type class-attribute instance-attribute

event_type: Literal['curse_revealed'] = 'curse_revealed'

code class-attribute instance-attribute

code: str = 'items.curse.revealed'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

instance_id instance-attribute

instance_id: str

template_id instance-attribute

template_id: str

DetectionRolledEvent

Bases: Event

A referee-rolled detection die: search, listen, and trap-spring checks.

Rolled whether or not anything is there (the no-leak convention); roll is None when a zero chance consumed no die.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.detection.rolled'})

event_type class-attribute instance-attribute

event_type: Literal['detection_rolled'] = 'detection_rolled'

code class-attribute instance-attribute

code: str = 'exploration.detection.rolled'

visibility class-attribute instance-attribute

character_id class-attribute instance-attribute

character_id: str | None = None

kind instance-attribute

kind: str

chance instance-attribute

chance: int

roll class-attribute instance-attribute

roll: int | None = None

passed instance-attribute

passed: bool

DiceRolledEvent

Bases: Event

An authorial dice roll resolved (referee — the referee's hidden adjudication rolls).

Emitted by RollDice when a referee resolves a freeform chance outcome by rolling through the seeded session. Carries the expression that was rolled, the total, and each individual die result in rolls.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'adjudication.dice_rolled'})

event_type class-attribute instance-attribute

event_type: Literal['dice_rolled'] = 'dice_rolled'

code class-attribute instance-attribute

code: str = 'adjudication.dice_rolled'

visibility class-attribute instance-attribute

expression instance-attribute

expression: str

total instance-attribute

total: int

rolls instance-attribute

rolls: tuple[int, ...]

DoorEvent

Bases: Event

A door changed state; the edge is named by its cell and direction.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {
        "exploration.door.opened",
        "exploration.door.closed",
        "exploration.door.forced",
        "exploration.door.stuck",
        "exploration.door.wedged",
        "exploration.door.swung_shut",
        "exploration.door.unlocked",
    }
)

event_type class-attribute instance-attribute

event_type: Literal['door'] = 'door'

visibility class-attribute instance-attribute

x instance-attribute

x: int

y instance-attribute

y: int

direction instance-attribute

direction: str

character_id class-attribute instance-attribute

character_id: str | None = None

narrative class-attribute instance-attribute

narrative: str | None = None

The authored success text of the door's gate, when the opening satisfied one. Authored text on an event is content data in a structured field, not engine-baked English: the event still carries its message code and its facts, and the default formatter appends this line verbatim after the templated one.

EncounterEndedEvent

Bases: Event

The encounter concluded; the clock owes at least one full turn.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.ended'})

event_type class-attribute instance-attribute

event_type: Literal['encounter_ended'] = 'encounter_ended'

code class-attribute instance-attribute

code: str = 'encounter.ended'

visibility class-attribute instance-attribute

outcome instance-attribute

outcome: str

EncounterStartedEvent

Bases: Event

An encounter opened: visible monster names and counts only.

The surprise rolls are referee events; the outcomes ride here — being surprised is felt in the fiction.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.started'})

event_type class-attribute instance-attribute

event_type: Literal['encounter_started'] = 'encounter_started'

code class-attribute instance-attribute

code: str = 'encounter.started'

visibility class-attribute instance-attribute

monster_name instance-attribute

monster_name: str

count instance-attribute

count: int

distance_feet instance-attribute

distance_feet: int

party_surprised class-attribute instance-attribute

party_surprised: bool = False

monsters_surprised class-attribute instance-attribute

monsters_surprised: bool = False

EvasionEvent

Bases: Event

An evasion attempt resolved: immediate success, or a pursuit begins.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.evasion.succeeded', 'encounter.evasion.pursuit'})

event_type class-attribute instance-attribute

event_type: Literal['evasion'] = 'evasion'

visibility class-attribute instance-attribute

ExhaustionEvent

Bases: Event

The party gained or recovered from running exhaustion (30 rounds, −2s).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.exhaustion.gained', 'encounter.exhaustion.recovered'})

event_type class-attribute instance-attribute

event_type: Literal['exhaustion'] = 'exhaustion'

visibility class-attribute instance-attribute

FatigueEvent

Bases: Event

The party gained or recovered from the unrested-fatigue penalty.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.fatigue.gained', 'exploration.fatigue.recovered'})

event_type class-attribute instance-attribute

event_type: Literal['fatigue'] = 'fatigue'

visibility class-attribute instance-attribute

FlagSetEvent

Bases: Event

A session flag changed (referee — content wiring is the game's secret).

The flag store a FlagEqualsCondition reads is the same one this event reports being written.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.flag.set'})

event_type class-attribute instance-attribute

event_type: Literal['flag_set'] = 'flag_set'

code class-attribute instance-attribute

code: str = 'session.flag.set'

visibility class-attribute instance-attribute

key instance-attribute

key: str

value instance-attribute

value: str | int | bool

GameOverEvent

Bases: Event

The session ended: the party was wiped out, however it happened.

A lost battle, a save-or-die trap, a fall, starvation, or a poison that finished the last member under the referee's clock all report the same ending, and the session is in game_over when it lands.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.game_over'})

event_type class-attribute instance-attribute

event_type: Literal['game_over'] = 'game_over'

code class-attribute instance-attribute

code: str = 'session.game_over'

visibility class-attribute instance-attribute

reason instance-attribute

reason: str

GroupMovedEvent

Bases: Event

A group's range-track distance changed.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.group.moved'})

event_type class-attribute instance-attribute

event_type: Literal['group_moved'] = 'group_moved'

code class-attribute instance-attribute

code: str = 'battle.group.moved'

visibility class-attribute instance-attribute

group_id instance-attribute

group_id: str

distance_feet instance-attribute

distance_feet: int

HealingPurchasedEvent

Bases: Event

A temple healing service purchased and cast.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'town.healing.purchased'})

event_type class-attribute instance-attribute

event_type: Literal['healing_purchased'] = 'healing_purchased'

code class-attribute instance-attribute

code: str = 'town.healing.purchased'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

service instance-attribute

service: str

cost_gp instance-attribute

cost_gp: int

HoardGeneratedEvent

Bases: Event

A lair hoard, carried bundle, or area treasure generated (referee).

Referee visibility — contents are itemized here and players learn by finding. cache_ref is the engine-created cache's state reference (or the group id for carried bundles); value and counts summarize the generation.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'treasure.hoard.generated'})

event_type class-attribute instance-attribute

event_type: Literal['hoard_generated'] = 'hoard_generated'

code class-attribute instance-attribute

code: str = 'treasure.hoard.generated'

visibility class-attribute instance-attribute

cache_ref instance-attribute

cache_ref: str

treasure_types class-attribute instance-attribute

treasure_types: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

valuable_ids class-attribute instance-attribute

valuable_ids: tuple[str, ...] = ()

magic_item_ids class-attribute instance-attribute

magic_item_ids: tuple[str, ...] = ()

ItemAcquiredEvent

Bases: Event

Items or coins entered a character's inventory.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.acquired'})

event_type class-attribute instance-attribute

event_type: Literal['item_acquired'] = 'item_acquired'

code class-attribute instance-attribute

code: str = 'exploration.item.acquired'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

item_ids class-attribute instance-attribute

item_ids: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

ItemConsumedEvent

Bases: Event

One carried item was used up — a gate's toll paid, a spike driven home.

item_id follows the acquisition masking rule: a mundane consumption carries the catalog id, a magic one the instance's session-scoped instance_id, never its template_id — an unidentified item's true identity never rides a player-visible event.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.consumed'})

event_type class-attribute instance-attribute

event_type: Literal['item_consumed'] = 'item_consumed'

code class-attribute instance-attribute

code: str = 'exploration.item.consumed'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

item_id instance-attribute

item_id: str

ItemIdentifiedEvent

Bases: Event

A magic item identified — a first meaningful use of it is the trigger.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'items.item.identified'})

event_type class-attribute instance-attribute

event_type: Literal['item_identified'] = 'item_identified'

code class-attribute instance-attribute

code: str = 'items.item.identified'

visibility class-attribute instance-attribute

instance_id instance-attribute

instance_id: str

template_id instance-attribute

template_id: str

ItemUsedEvent

Bases: Event

A magic item used: a potion drunk (or mixed), a scroll read, a device activated.

items.device.inert is a rejection code, not an event — activating an exhausted device costs nothing, the same as a blocked move. Charges never appear here: they are referee-only forever (RAW, undiscoverable).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"items.potion.drunk", "items.potion.mixed", "items.scroll.read", "items.scroll.cursed", "items.device.activated"}
)

event_type class-attribute instance-attribute

event_type: Literal['item_used'] = 'item_used'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

instance_id instance-attribute

instance_id: str

manual class-attribute instance-attribute

manual: tuple[str, ...] = ()

ItemsDroppedEvent

Bases: Event

Items or coins dropped onto the party's cell (or the pursuit trail).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.dropped'})

event_type class-attribute instance-attribute

event_type: Literal['items_dropped'] = 'items_dropped'

code class-attribute instance-attribute

code: str = 'exploration.item.dropped'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

item_ids class-attribute instance-attribute

item_ids: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

ItemsGivenEvent

Bases: Event

Items or coins handed from one party member to another.

character_id is the giver, recipient_id the companion who took the goods.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.given'})

event_type class-attribute instance-attribute

event_type: Literal['items_given'] = 'items_given'

code class-attribute instance-attribute

code: str = 'exploration.item.given'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

recipient_id instance-attribute

recipient_id: str

item_ids class-attribute instance-attribute

item_ids: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

ItemsLeftBehindEvent

Bases: Event

Treasure the party could not carry, left where it lay.

Emitted by TakeTreasure when a haul exceeds the carriers' remaining maximum load: the remainder lands in the drop pile on the party's cell, so nothing is destroyed and a lightened party can come back for it.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.left_behind'})

event_type class-attribute instance-attribute

event_type: Literal['items_left_behind'] = 'items_left_behind'

code class-attribute instance-attribute

code: str = 'exploration.item.left_behind'

visibility class-attribute instance-attribute

item_ids class-attribute instance-attribute

item_ids: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

JournalEntryAddedEvent

Bases: Event

A beat was appended to the session journal — the whole entry, as written.

Player-visible: the journal is written for the table. The authored text is content data in a structured field, not engine-baked English — the event still carries its message code and its facts — and rounds is the clock position the entry landed at, the same stamp the stored entry carries.

It is not the only event a growing journal emits. A quest beat appends its entry and reports itself through its own lifecycle event — the whole set is this event plus QuestActivatedEvent, ObjectiveRevealedEvent, ObjectiveCompletedEvent, and QuestCompletedEvent — because emitting both for one beat would report the same line to the table twice. A client that wants the whole journal reads it from the view.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.journal.entry_added'})

event_type class-attribute instance-attribute

event_type: Literal['journal_entry_added'] = 'journal_entry_added'

code class-attribute instance-attribute

code: str = 'session.journal.entry_added'

visibility class-attribute instance-attribute

text instance-attribute

text: str

rounds instance-attribute

rounds: int

LightEvent

Bases: Event

A light source changed state.

source is the item or effect kind (torch, lantern, light); .failed is a failed tinder-box attempt; .expired is the session's player-facing translation of the ledger's referee-visibility expiry.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"exploration.light.lit", "exploration.light.extinguished", "exploration.light.failed", "exploration.light.expired"}
)

event_type class-attribute instance-attribute

event_type: Literal['light'] = 'light'

visibility class-attribute instance-attribute

character_id class-attribute instance-attribute

character_id: str | None = None

source instance-attribute

source: str

ListenedEvent

Bases: Event

What the listener heard — heard-something or silence, genuinely ambiguous.

Undead make no noise, so the referee-side roll (which rides DetectionRolledEvent) happens whether or not anything is there; silence never says which.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.listen.heard', 'exploration.listen.silent'})

event_type class-attribute instance-attribute

event_type: Literal['listened'] = 'listened'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

direction instance-attribute

direction: str

LocationEnteredEvent

Bases: Event

The party crossed a location boundary.

location_kind is area, level, dungeon, or town; location_id is the area or dungeon id ("town" for town). level_number rides level and dungeon entries, and dungeon_id rides area entries — an area id is scoped to its level, so an area entry needs all three to name where the party is, while level and dungeon entries carry the dungeon id in location_id and town has neither.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.location.entered'})

event_type class-attribute instance-attribute

event_type: Literal['location_entered'] = 'location_entered'

code class-attribute instance-attribute

code: str = 'exploration.location.entered'

visibility class-attribute instance-attribute

location_kind instance-attribute

location_kind: str

location_id instance-attribute

location_id: str

level_number class-attribute instance-attribute

level_number: int | None = None

dungeon_id class-attribute instance-attribute

dungeon_id: str | None = None

narrative class-attribute instance-attribute

narrative: str | None = None

The authored success text of the gate on the transition that was taken, when the author wrote one. Authored text on an event is content data in a structured field, not engine-baked English: the event still carries its message code and its facts, and the default formatter appends this line verbatim after the templated one.

MonsterDefeatedEvent

Bases: Event

One monster defeated — feeds the adventure's XP award.

Emitted per monster at battle end with outcome slain, routed, or surrendered; xp is the template's printed award.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.monster.defeated'})

event_type class-attribute instance-attribute

event_type: Literal['monster_defeated'] = 'monster_defeated'

code class-attribute instance-attribute

code: str = 'battle.monster.defeated'

visibility class-attribute instance-attribute

monster_id instance-attribute

monster_id: str

template_id instance-attribute

template_id: str

outcome instance-attribute

outcome: str

xp instance-attribute

xp: int

MonsterFledEvent

Bases: Event

A monster group broke: fled the battle or surrendered.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.side.fled', 'battle.side.surrendered'})

event_type class-attribute instance-attribute

event_type: Literal['monster_fled'] = 'monster_fled'

visibility class-attribute instance-attribute

group_id instance-attribute

group_id: str

MonstersLeftBehindEvent

Bases: Event

A routing group left its helpless members where they lie.

Fleeing is movement, and a member who cannot move (asleep, paralysed, held by a web) cannot run: the runners split off and keep fleeing under the original group id while the helpless stay behind as the new group group_id, at the distance the side broke from.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.group.left_behind'})

event_type class-attribute instance-attribute

event_type: Literal['monsters_left_behind'] = 'monsters_left_behind'

code class-attribute instance-attribute

code: str = 'battle.group.left_behind'

visibility class-attribute instance-attribute

group_id instance-attribute

group_id: str

source_group_id instance-attribute

source_group_id: str

count instance-attribute

count: int

MonstersSpawnedEvent

Bases: Event

Monsters spawned into the session registry (referee bookkeeping).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.monsters.spawned'})

event_type class-attribute instance-attribute

event_type: Literal['monsters_spawned'] = 'monsters_spawned'

code class-attribute instance-attribute

code: str = 'session.monsters.spawned'

visibility class-attribute instance-attribute

template_id instance-attribute

template_id: str

monster_ids instance-attribute

monster_ids: tuple[str, ...]

NoteRecordedEvent

Bases: Event

A referee annotation was recorded (referee — and it changes no state).

The report of a machine-issued record — a dropped consequence, a cascade cut short — or of a referee's own margin note.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.note.recorded'})

event_type class-attribute instance-attribute

event_type: Literal['note_recorded'] = 'note_recorded'

code class-attribute instance-attribute

code: str = 'session.note.recorded'

visibility class-attribute instance-attribute

text instance-attribute

text: str

NpcPartySpawnedEvent

Bases: Event

An NPC adventuring party generated and fielded (referee — the full roster).

The player-facing EncounterStartedEvent names "adventurers" and the count; the roster, classes, and levels are the referee's.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.npc_party.spawned'})

event_type class-attribute instance-attribute

event_type: Literal['npc_party_spawned'] = 'npc_party_spawned'

code class-attribute instance-attribute

code: str = 'encounter.npc_party.spawned'

visibility class-attribute instance-attribute

party_kind instance-attribute

party_kind: str

npc_ids instance-attribute

npc_ids: tuple[str, ...]

class_ids instance-attribute

class_ids: tuple[str, ...]

levels instance-attribute

levels: tuple[int, ...]

alignment instance-attribute

alignment: str

ObjectiveCompletedEvent

Bases: Event

One objective of a quest is done — including one nobody had announced yet.

name is the objective's display label — its authored name, or its id when the document authors none — and quest_name the owning quest's name, both resolved at emission so a renderer holds no document to look them up in. Both default empty only because an event logged before the fields existed still parses; the engine always fills them. narrative is the objective's authored progress beat, None when unauthored, and the journal carries the same line.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.quest.objective_completed'})

event_type class-attribute instance-attribute

event_type: Literal['objective_completed'] = 'objective_completed'

code class-attribute instance-attribute

code: str = 'session.quest.objective_completed'

visibility class-attribute instance-attribute

quest_id instance-attribute

quest_id: str

quest_name class-attribute instance-attribute

quest_name: str = ''

objective_id instance-attribute

objective_id: str

name class-attribute instance-attribute

name: str = ''

narrative class-attribute instance-attribute

narrative: str | None = None

ObjectiveRevealedEvent

Bases: Event

A hidden objective surfaced: the party can see what it is being asked for.

name is the objective's display label — its authored name, or its id when the document authors none — and quest_name the owning quest's name, both resolved at emission so a renderer holds no document to look them up in. Both default empty only because an event logged before the fields existed still parses; the engine always fills them. narrative is the objective's authored offer beat, None when unauthored, and the journal carries the same line.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.quest.objective_revealed'})

event_type class-attribute instance-attribute

event_type: Literal['objective_revealed'] = 'objective_revealed'

code class-attribute instance-attribute

code: str = 'session.quest.objective_revealed'

visibility class-attribute instance-attribute

quest_id instance-attribute

quest_id: str

quest_name class-attribute instance-attribute

quest_name: str = ''

objective_id instance-attribute

objective_id: str

name class-attribute instance-attribute

name: str = ''

narrative class-attribute instance-attribute

narrative: str | None = None

PartyMovedEvent

Bases: Event

The party moved or turned; x/y/facing are the resulting pose.

A blocked move is a rejection (exploration.move.blocked), never an event: moving into a wall is an in-fiction invalid command, not a game state change.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.party.moved', 'exploration.party.turned'})

event_type class-attribute instance-attribute

event_type: Literal['party_moved'] = 'party_moved'

visibility class-attribute instance-attribute

x instance-attribute

x: int

y instance-attribute

y: int

facing instance-attribute

facing: str

ProvisionsEvent

Bases: Event

A day-boundary provision outcome: consumed, or short (food or water).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.provisions.consumed', 'exploration.provisions.short'})

event_type class-attribute instance-attribute

event_type: Literal['provisions'] = 'provisions'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

kind instance-attribute

kind: str

PursuitEvent

Bases: Event

One pursuit beat: the round's gap, a distraction, escape, or capture.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"encounter.pursuit.round", "encounter.pursuit.distracted", "encounter.pursuit.escaped", "encounter.pursuit.caught"}
)

event_type class-attribute instance-attribute

event_type: Literal['pursuit'] = 'pursuit'

visibility class-attribute instance-attribute

round instance-attribute

round: int

gap_feet instance-attribute

gap_feet: int

QuestActivatedEvent

Bases: Event

An authored quest came into play — the table's news, not the wiring behind it.

Player-visible: a quest the party has taken on is theirs to know, while the clause that started it stays behind the screen with the trigger and flag events. narrative is the quest's authored offer beat, None when unauthored — content data in a structured field, not engine-baked English, appended verbatim by the default formatter after the templated line. The same beat is appended to the journal, so this event and its entry are one report of one moment.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.quest.activated'})

event_type class-attribute instance-attribute

event_type: Literal['quest_activated'] = 'quest_activated'

code class-attribute instance-attribute

code: str = 'session.quest.activated'

visibility class-attribute instance-attribute

quest_id instance-attribute

quest_id: str

name instance-attribute

name: str

narrative class-attribute instance-attribute

narrative: str | None = None

QuestCompletedEvent

Bases: Event

A quest is finished, however the ruling was reached.

narrative is the quest's authored completion beat, None when unauthored, and the journal carries the same line. Rewards, when the quest pays any, land as their own commands and their own events after this one.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.quest.completed'})

event_type class-attribute instance-attribute

event_type: Literal['quest_completed'] = 'quest_completed'

code class-attribute instance-attribute

code: str = 'session.quest.completed'

visibility class-attribute instance-attribute

quest_id instance-attribute

quest_id: str

name instance-attribute

name: str

narrative class-attribute instance-attribute

narrative: str | None = None

RestedEvent

Bases: Event

A rest completed or was interrupted; kind is turn, night, or day.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.rest.rested', 'exploration.rest.interrupted'})

event_type class-attribute instance-attribute

event_type: Literal['rested'] = 'rested'

visibility class-attribute instance-attribute

kind instance-attribute

kind: str

SearchCompletedEvent

Bases: Event

A search finished: what it revealed, or nothing (which is ambiguous).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.search.found', 'exploration.search.nothing'})

event_type class-attribute instance-attribute

event_type: Literal['search_completed'] = 'search_completed'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

kind instance-attribute

kind: str

found class-attribute instance-attribute

found: tuple[str, ...] = ()

SpellDeclaredEvent

Bases: Event

A spell declaration posted — table-visible per RAW.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.spell.declared'})

event_type class-attribute instance-attribute

event_type: Literal['spell_declared'] = 'spell_declared'

code class-attribute instance-attribute

code: str = 'battle.spell.declared'

visibility class-attribute instance-attribute

caster_id instance-attribute

caster_id: str

spell_id instance-attribute

spell_id: str

reversed class-attribute instance-attribute

reversed: bool = False

StanceChangedEvent

Bases: Event

The monsters' stance, as behavior — the reaction roll itself is referee.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.stance.changed'})

event_type class-attribute instance-attribute

event_type: Literal['stance_changed'] = 'stance_changed'

code class-attribute instance-attribute

code: str = 'encounter.stance.changed'

visibility class-attribute instance-attribute

stance instance-attribute

stance: str

SurpriseRolledEvent

Bases: Event

One side's surprise die (referee); roll is None when the side never rolls.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.surprise.rolled'})

event_type class-attribute instance-attribute

event_type: Literal['surprise_rolled'] = 'surprise_rolled'

code class-attribute instance-attribute

code: str = 'encounter.surprise.rolled'

visibility class-attribute instance-attribute

side instance-attribute

side: str

threshold instance-attribute

threshold: int

roll class-attribute instance-attribute

roll: int | None = None

surprised instance-attribute

surprised: bool

TimeAdvancedEvent

Bases: Event

The clock advanced (referee bookkeeping); rounds_total is the new position.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.time.advanced'})

event_type class-attribute instance-attribute

event_type: Literal['time_advanced'] = 'time_advanced'

code class-attribute instance-attribute

code: str = 'session.time.advanced'

visibility class-attribute instance-attribute

n instance-attribute

n: int

unit instance-attribute

unit: str

rounds_total instance-attribute

rounds_total: int

TrapEvent

Bases: Event

A trap outcome the party perceives.

.sprung when a trap goes off, .found when a search or inspection reveals one, .removed on a successful removal, .safe when a known trap's trigger resolved without springing — never emitted for unknown traps (the spring die rides the referee-visibility DetectionRolledEvent, no-leak).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"exploration.trap.sprung", "exploration.trap.safe", "exploration.trap.found", "exploration.trap.removed"}
)

event_type class-attribute instance-attribute

event_type: Literal['trap'] = 'trap'

visibility class-attribute instance-attribute

trap_ref instance-attribute

trap_ref: str

character_id class-attribute instance-attribute

character_id: str | None = None

TreasureSoldEvent

Bases: Event

Valuables sold in town at full value (the 1-gp-1-XP identity kept clean).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'town.treasure.sold'})

event_type class-attribute instance-attribute

event_type: Literal['treasure_sold'] = 'treasure_sold'

code class-attribute instance-attribute

code: str = 'town.treasure.sold'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

instance_ids instance-attribute

instance_ids: tuple[str, ...]

gp_value instance-attribute

gp_value: int

TriggerFiredEvent

Bases: Event

An authored trigger fired (referee — trigger wiring is the game's secret).

Emitted for every MarkTriggerFired, a mark of an already-fired trigger included: session state records that a trigger has fired, and these events record each firing.

narrative is the trigger's authored beat for the firing — content data in a structured field, not engine-baked English: the event still carries its message code and its facts, and the default formatter appends the line verbatim after the templated one. It rides a referee-visibility event because trigger wiring is the game's secret; a beat written for the table is a journal entry.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.trigger.fired'})

event_type class-attribute instance-attribute

event_type: Literal['trigger_fired'] = 'trigger_fired'

code class-attribute instance-attribute

code: str = 'session.trigger.fired'

visibility class-attribute instance-attribute

trigger_id instance-attribute

trigger_id: str

narrative class-attribute instance-attribute

narrative: str | None = None

WanderingCheckEvent

Bases: Event

A wandering-monster check fired (referee bookkeeping).

roll is None when the clamped chance was 0 and the roll was skipped.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.wandering.checked'})

event_type class-attribute instance-attribute

event_type: Literal['wandering_check'] = 'wandering_check'

code class-attribute instance-attribute

code: str = 'exploration.wandering.checked'

visibility class-attribute instance-attribute

chance instance-attribute

chance: int

roll class-attribute instance-attribute

roll: int | None = None

encounter instance-attribute

encounter: bool

XpAwardedEvent

Bases: Event

An XP award applied to one character.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.xp.awarded'})

event_type class-attribute instance-attribute

event_type: Literal['xp_awarded'] = 'xp_awarded'

code class-attribute instance-attribute

code: str = 'session.xp.awarded'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

award instance-attribute

award: int

modified_award instance-attribute

modified_award: int

level_after instance-attribute

level_after: int

parse_any_event

parse_any_event(data: Mapping[str, object]) -> Event | None

Parse one serialized event, kernel or crawl, skipping unknown event types.

The session log's parser: an event_type this library doesn't know returns None instead of raising, so a newer producer's log loads under an older consumer (the session preserves the raw record).

Parameters:

Name Type Description Default
data Mapping[str, object]

A mapping previously produced by an event's model_dump.

required

Returns:

Type Description
Event | None

The event, or None when its event_type is unknown.

Raises:

Type Description
ContentValidationError

If the event type is known but the payload is malformed.