osrlib.crawl.gates
Authored gates: the condition vocabulary, the gate model, and pure evaluation.
A gate is an authored predicate the engine checks when the party attempts
something — opening a door, taking a stair. Its two carriers are the requires
fields of DoorSpec and
TransitionSpec — a gate hangs nowhere
else. It is a stateless content object:
condition_holds reads live session state
at the moment of the attempt and stores nothing, so a key dropped or sold stops
opening its door and evaluation never drifts from the truth.
The condition vocabulary is a discriminated union that grows additively:
HasItemCondition— some party member's carried inventory holds an item with that catalog id (Inventory.carried_itemis the matching rule, equipped slots and all). Withconsumes=True, the successful command takes one instance from the first holder in marching order.FlagEqualsCondition— a session flag (SetFlag) holds a value.EffectActiveCondition— an active effect of that kind is attached to a party member, so an author can ask for the talisman invoked rather than merely carried.
Gates and locks are orthogonal layers: locked is stateful mechanics with dice
and per-character memory, a gate is a stateless authored predicate, and a door
carrying both requires both. Evaluation is pure — it draws no dice, costs no game
time, and mutates nothing — which is what lets a gate refusal be an ordinary
command rejection.
ConditionSpec
module-attribute
ConditionSpec = Annotated[
HasItemCondition | FlagEqualsCondition | EffectActiveCondition, Field(discriminator="condition_type")
]
The condition union, discriminated on condition_type (has_item, flag_equals,
effect_active). New kinds join it additively; the discriminator values are wire
values and serialize into every document that carries a gate.
EffectActiveCondition
Bases: BaseModel
An active effect of kind is attached to some party member.
Effect kinds are an open, data-driven vocabulary ("fatigue", a custom
EffectDefinition.kind), so the field
is a free string. Effects attached to a location never count — the test is
what the party carries in its bones.
FlagEqualsCondition
Bases: BaseModel
A session flag holds value — the lever that opens the portcullis.
The comparison is
flag_values_equal and it is strict: an
absent key equals nothing (False included), and a stored True never matches
an authored 1.
GateSpec
Bases: BaseModel
A condition guarding an attempt, with the authored text for both outcomes.
The gate is the object the narrative hangs on: a refused attempt returns the
block's refusal beat in its rejection, and a successful one rides the
success beat on the command's event.
HasItemCondition
Bases: BaseModel
The party carries an item with item_id — equipment or magic item.
Any member's carried inventory satisfies it, equipped slots included: carrying
is the whole test. consumes=True makes the item a toll — one instance leaves
the first holder in marching order when the gated command succeeds, per
success, so a door that swings shut wants another key.
condition_holds
condition_holds(
condition: ConditionSpec,
*,
members: Sequence[Character],
flags: Mapping[str, str | int | bool],
ledger: EffectsLedger
) -> bool
Evaluate one condition against live session state.
Pure: no draws, no clock, no mutation, nothing stored. Every walk is over an ordered list — the party in marching order, each inventory in carried order — so the answer is deterministic.
The member domain is every party member, living or dead. The party carries its dead and their packs, so a key that rides a corpse still opens its door.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
condition
|
ConditionSpec
|
The condition to evaluate. |
required |
members
|
Sequence[Character]
|
The party, in marching order. |
required |
flags
|
Mapping[str, str | int | bool]
|
The session flag store. |
required |
ledger
|
EffectsLedger
|
The session's effects ledger. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True when the condition holds right now. |
Examples:
first_holder
The first member in marching order carrying an item with item_id.
The consumption target of a has_item toll, matching exactly what
condition_holds tests.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
members
|
Sequence[Character]
|
The party, in marching order. |
required |
item_id
|
str
|
The catalog id to look for. |
required |
Returns:
| Type | Description |
|---|---|
Character | None
|
The member, or |
flag_values_equal
Compare two flag values the one strict way the engine compares them.
Equality plus matching boolness. True == 1 in Python, so a flag somebody set
to True must not satisfy an authored 1, and the reverse: the two are
different values in an authored document even though Python calls them equal.
Every surface that asks "does this flag hold that value" —
FlagEqualsCondition on a gate, an
authored trigger's flag pattern — asks through here, so a condition and a
pattern can never disagree about what equality means.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stored
|
str | int | bool
|
The value the flag store holds (or the value an event reports written). |
required |
expected
|
str | int | bool
|
The value the author wrote. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True when the two are the same value. |
Examples: