Skip to content

Bound encounter distance by the party's sight line - #31

Merged
mmacy merged 1 commit into
mainfrom
fix-encounter-distance-bound
Jul 25, 2026
Merged

Bound encounter distance by the party's sight line#31
mmacy merged 1 commit into
mainfrom
fix-encounter-distance-bound

Conversation

@mmacy

@mmacy mmacy commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Fixes the case reported downstream as osr-web#8: "Monsters placed 80′ away in a room no bigger than 40′ on a side."

The bug

start_encounter rolled 2d6 × 10 with no reference to the space it was rolling in, and none of the three exploration.py callers pass a distance. A cell is 10 feet, so a keyed encounter in a 2×2-cell room could open at 120 feet — six times the room's width.

The reported encounter is locatable: B3 L1 area 5 "Library", cells (9,19)…(10,20), a 20′ × 20′ room keyed with five kobolds. Over 500 seeds on the unfixed engine, 98.0% of encounters there opened farther than the room is wide, and 14.6% landed on the exact reported 80′.

Across all 208 keyed encounters in the eight adventures on hand (842 entry cells), computed against the 2d6 pmf: 96.6% of rolls exceed the room's longest side, 87.0% exceed the party cell's actual sight line. The example adventure in this repo has it too — a 50′ barrow corridor opening encounters at 90′.

Why it's a rules bug, not a missing clamp

srd/Encounters.md § Encounter Distance:

The situation in which the encounter occurs often determines how far away the monster is. If there is uncertainty, the encounter distance may be determined randomly: Dungeon: 2d6 × 10 feet.

The roll is the fallback. The walls the party is standing between are "the situation", so the engine was skipping the primary clause and going straight to the tiebreaker.

The rule now implemented

The rolled distance is capped at the longest unobstructed straight sight line from the party's cell, taken across the four grid directions through edges sight crosses, floored at one cell. An explicitly supplied distance_feet is never bounded.

One rule covers a chamber, a corridor, and a spawned encounter. Ceilings in the survey run 10′–220′; about 8% of cells see far enough to never clamp, so long passages keep the full atmospheric range.

Alternatives rejected:

  • The keyed area's bounding box — every keyed room in the downstream content is 2×2 cells, so this collapses every keyed encounter to a flat 10′, and needs a second rule for wandering.
  • A flood fill of visible cells — leaks around corners; a small room with an open door onto a long hall would reproduce the bug. Straight rays are what "sight line" means.
  • Light radius — distance and visibility are different quantities. Darkness never shortens the line: a monster the party cannot see is still exactly as far away as it is. Surprise already models not seeing them.
  • Rescaling 2d6 into [10, ceiling] — invents a distribution the rules don't have. "The situation determines" is a clamp.

Determinism

The cap reads the roll's result and never the stream, so the draw sequence is identical bounded or not — asserted by test_the_bound_consumes_no_randomness against the exported stream state.

Distances in cramped spaces genuinely change, so phase4_delve.json and phase5_milestone.json are regenerated and MILESTONE_SEED is repinned 203 → 21. The new seed reproduces the same milestone: same quest.idol: recovered, same 45-command script, zero deaths. phase5_hoards.json and phase5_npc_parties.json regenerated byte-identical.

Balance

Encounters in confined spaces now begin closer and are correspondingly deadlier — the party loses a round or two of free missile fire before contact. That is the honest reading of the rule and is intended.

Verification

  • 1588 passed, 58 skipped (baseline 1579, +9 new)
  • ruff format --check, ruff check, pyright 0 errors, mkdocs build --strict clean
  • No public API change
  • Downstream osr-web against this working tree: 193 passed, 6 screenshot shots byte-identical, staleness gate exit 0

https://claude.ai/code/session_01PjCy2kE4R1Yj85rizkkXpV

Encounter distance rolled 2d6 x 10 with no reference to the space it rolled
in, so a keyed encounter in a 2x2-cell room could open at 120 feet — six
times the room's width. Reported against osr-web as five kobolds watching
from eighty feet in a twenty-foot library.

The SRD makes the roll a fallback, not the rule: "The situation in which the
encounter occurs often determines how far away the monster is. If there is
uncertainty, the encounter distance may be determined randomly." The walls
the party stands between are that situation, and the engine was skipping the
primary clause to reach the tiebreaker. Measured across every keyed encounter
in the eight adventures on hand, 96.6% of rolls exceeded the room's own
longest side.

The rolled distance is now capped at the longest unobstructed straight sight
line from the party's cell, taken across the four grid directions through
edges sight crosses and floored at one cell. One rule covers a chamber, a
corridor, and a spawned encounter alike: a small room clamps, a long passage
keeps the full range, and roughly 8% of cells see far enough never to clamp
at all. An explicitly supplied distance is never bounded.

This is geometry, not illumination. Darkness never shortens the line, because
a monster the party cannot see is still exactly as far away as it is; light
governs surprise, which is modelled separately.

The cap reads the roll's result and never the stream, so the draw sequence is
unchanged. Distances in cramped spaces do change, so the phase-4 and phase-5
goldens are regenerated and MILESTONE_SEED is repinned from 203 to 21 — the
same milestone, same recovered quest, same 45-command script. Encounters in
confined spaces now begin closer and are correspondingly deadlier, which is
the honest reading of the rule.

Claude-Session: https://claude.ai/code/session_01PjCy2kE4R1Yj85rizkkXpV
@mmacy
mmacy merged commit cd203cb into main Jul 25, 2026
5 checks passed
@mmacy
mmacy deleted the fix-encounter-distance-bound branch July 25, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant