Skip to content

Implement passages, the transition the party walks through - #155

Merged
mmacy merged 2 commits into
mainfrom
passage
Oct 16, 2026
Merged

mmacy merged 2 commits into
mainfrom
passage

Conversation

@mmacy

@mmacy mmacy commented Oct 16, 2026

Copy link
Copy Markdown
Owner

This implements the passage contract from pull requests 153 and 154. A passage is a transition the party crosses by walking across one side of its cell with MoveParty, instead of taking it with UseStairs. osr-forge's map tracing and re-pod's level splitting need it, and it ships in osrlib 2.0.

What changed:

  • TransitionSpec refuses a passage with no side, and any other kind with one. LevelSpec.passage_at finds a passage by cell and side, and transition_at and UseStairs ignore passages.
  • MoveParty makes its can't-move checks, then crosses a passage on that side if there is one. The crossing evaluates the gate and takes the toll through the helpers UseStairs uses. It emits the same arrival events with via="passage", then a PartyMovedEvent for the landing cell, and costs what a step onto the landing cell costs.
  • The player view reports a passage's side as "open". Sight, light, and fighting space still treat it as the wall it is.
  • validate_adventure refuses the cases the contract lists, with the exact lines the tests pin.
  • Docstrings, two docs pages, the changelog, and docs/phase-17-plan.md cover passages. The second commit corrects four docstrings a review found outside the first diff, and removes the tests' xfail marker.

SCHEMA_VERSION stays 4, and no golden file or test changed.

Checked locally: uv run pytest tests/test_passages.py (38 passed, unmarked), ruff format --check, ruff check, pyright (0 errors), pytest (2989 passed, 146 skipped), and mkdocs build --strict. A fresh reviewer also ran cases no test covers: a keyed encounter on the landing cell, a gated passage within one level, an overloaded party, a refused gate, arrivals on a passage cell by other means, the far cell under a lit torch, and a replay through a passage pair. Each behaved as the plan says.

mmacy added 2 commits October 15, 2026 18:50
A MoveParty across the side of a cell that a passage stands on lands
the party on the passage's destination. The move makes the usual
can't-move checks, evaluates the passage's gate as UseStairs does, and
costs what a step onto the landing cell costs. It emits what UseStairs
emits for the same destination, with via="passage", then a
PartyMovedEvent for the landing cell. Within one level, doors the
party opened swing shut as they do after any step.

LevelSpec.passage_at finds a passage by cell and side. transition_at
and UseStairs ignore passages, and the player view shows a passage's
side as open. validate_adventure refuses a passage whose side or far
cell breaks the crossing, one that lands on its own cell or on a far
cell, two passages on one cell and side, and a trap whose transition
is a passage, with the exact lines the acceptance tests pin.

SCHEMA_VERSION stays 4. No draw sequence or golden file changed.
A review of the passage chunk found that the LocationEnteredEvent
class docstring, the gates module docstring, the GateSpec docstring,
and the odometer sentence in docs/adaptations.md left passages out.
This corrects them, removes the passage tests' xfail marker, and adds
docs/phase-17-plan.md as the record of the phase.
@mmacy
mmacy merged commit 5c8a818 into main Oct 16, 2026
5 checks passed
@mmacy
mmacy deleted the passage branch October 16, 2026 01:57
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