osrlib.crawl.exploration
The exploration turn: movement, doors, searching, traps, light, rest, wandering checks.
Each handler here is one function (session, command) -> (rejections, events).
Callers never call a handler directly: they build one of the command classes in
osrlib.crawl.commands (MoveParty, Search, Rest,
and so on) and hand it to
GameSession.execute, which dispatches
to the matching handler here. Every handler validates before it draws, mutates, or
advances the clock, so a rejected command costs nothing.
Movement accrues in thirds-of-feet: an unexplored cell costs 30 units, a previously explored cell 10 — the SRD's rule that familiar ground moves three times as fast. Once the accrued total reaches 3 × the party's exploration rate, the clock advances one full turn and the odometer resets; turn-costing actions advance a whole turn outright, absorbing whatever partial move was pending.
Trap resolution (the 2-in-6 spring check, saves, damage, volley counts) draws on the exploration stream, since the procedure owns its own dice; attach-time condition durations draw on the effects stream instead, matching every other effect attachment in the engine.
EXHAUSTED_DEFINITION
module-attribute
EXHAUSTED_DEFINITION = EffectDefinition(
kind=EXHAUSTED_KIND,
condition=Condition.EXHAUSTED,
stacking="ignore",
modifiers=(
ModifierSpec(kind="attack_bonus", value=-2),
ModifierSpec(kind="damage_bonus", value=-2),
ModifierSpec(kind="attack_penalty_of_attackers", value=2),
),
)
HANDLERS
module-attribute
HANDLERS = {
MoveParty: _handle_move_party,
TurnParty: _handle_turn_party,
ReorderParty: _handle_reorder_party,
OpenDoor: _handle_open_door,
CloseDoor: _handle_close_door,
ForceDoor: _handle_force_door,
WedgeDoor: _handle_wedge_door,
ListenAtDoor: _handle_listen_at_door,
PickLock: _handle_pick_lock,
Search: _handle_search,
InspectTreasure: _handle_inspect_treasure,
RemoveTreasureTrap: _handle_remove_treasure_trap,
TakeTreasure: _handle_take_treasure,
DropItems: _handle_drop_items,
LightSource: _handle_light_source,
ExtinguishSource: _handle_extinguish_source,
EquipItem: _handle_equip_item,
UnequipItem: _handle_unequip_item,
Rest: _handle_rest,
PrepareSpells: _handle_prepare_spells,
CastSpell: _handle_cast_spell,
UseItem: _handle_use_item,
UseStairs: _handle_use_stairs,
EnterDungeon: _handle_enter_dungeon,
TravelToTown: _handle_travel_to_town,
PurchaseEquipment: _handle_purchase_equipment,
SellTreasure: _handle_sell_treasure,
PurchaseHealing: _handle_purchase_healing,
}
HEALING_SERVICES
module-attribute
HEALING_SERVICES: dict[str, tuple[str, int]] = {
"cure_light_wounds": ("cure_light_wounds", 25),
"cure_serious_wounds": ("cure_serious_wounds", 100),
"cure_disease": ("cure_disease", 150),
"neutralize_poison": ("neutralize_poison", 150),
"remove_curse": ("remove_curse_c", 200),
"raise_dead": ("raise_dead", 1500),
}
The temple's six services: spell id and price in gp, invented over the SRD's open list (see the adaptations register).
All services are always available — town size and cleric availability are game territory left to your front end.
check_fatigue
Attach the unrested-fatigue penalty once the cadence threshold passes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The running session. |
required |
Returns:
| Type | Description |
|---|---|
list[Event]
|
The events from attaching the fatigue effect, or an empty list before the |
list[Event]
|
threshold or once fatigue is already active on every living member. |
consume_provisions
One day-boundary crossing: rations and water per living member.
Standard rations consume before iron, since fresh food spoils first; a carried
waterskin satisfies the day without per-pint bookkeeping. In town, provisions
consume but never run short. A successful day resets that member's deprivation
track; under the deprivation_penalties flag, the schedule's effects sync
afterwards (see the adaptations register).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The running session. |
required |
Returns:
| Type | Description |
|---|---|
list[Event]
|
One |
list[Event]
|
member per resource (food and water). |
exploration_rate
exploration_rate(session) -> int
The party's exploration rate: slowest living member, deprivation-halved.
Under the deprivation_penalties flag, a member two or more days into the
worse deprivation track moves at half rate.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The running session. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The slowest living member's movement rate, in feet per turn. |
wandering_check
Fire one wandering-monster check; a hit spawns and opens an encounter.
The chance takes +1 for noise since the last check, +1 for daylight-bright light, −1 while resting, clamped to [0, 6]; a clamped 0 skips the roll. The check die draws from the wandering stream; the d20 table roll, count dice, and variant picks follow on the same stream (NPC-party rows spawn a real party, never a re-roll); spawned hit points draw from the monster-spawn stream, NPC composition from the npc-party stream, and the encounter's own dice from the encounter stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The running session. |
required |
resting
|
bool
|
Whether the party is resting: the −1 chance penalty applies. |
False
|
Returns:
| Type | Description |
|---|---|
list[Event]
|
The check's events and whether an encounter opened. The events always |
bool
|
include one |
tuple[list[Event], bool]
|
|
tuple[list[Event], bool]
|
adds the spawn and encounter-opening events too. |
wandering_interval
wandering_interval(session) -> int
The current level's wandering-check interval in turns (RAW default 2).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
GameSession
|
The running session. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The interval in turns, or an effectively unreachable value outside a |
int
|
dungeon. |