Skip to content

feat: page the minimap through levels the player is not standing on - #563

Merged
dmccoystephenson merged 2 commits into
mainfrom
feat/minimap-level-paging
Aug 13, 2026
Merged

feat: page the minimap through levels the player is not standing on#563
dmccoystephenson merged 2 commits into
mainfrom
feat/minimap-level-paging

Conversation

@dmccoystephenson

@dmccoystephenson dmccoystephenson commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary

  • A displayed minimap level, _miniMapViewZ, was added to WorldScreen, distinct from the player's own currentZ and resolved through the new getMiniMapDisplayZ(). None means "follow the player", so a save loaded underground needs no synchronisation, and _descend() / _ascend() reset to None so paging never survives a real move between levels.
  • Three actions were added for paging it: Page Up (level above), Page Down (level below) and Home (back to the player's own level, in one press), each with the ASCII alt binding a terminal player needs — ,, . and / respectively. KeyCode gained the six corresponding members with display names.
  • Both frontends were updated. The graphical minimap loads the displayed level's stitched image (the existing _miniMapCachedZ invalidation compare moved to it unchanged) and gains a caption naming the level and the key that returns; the text minimap shows the displayed level's known rooms, marks the center cell + rather than the facing arrow — the player is not on that level, so the marker means "directly above or below you" — and gains the same caption as a second header line.
  • A level with no stitched map is drawn as an explicit "unexplored" panel rather than nothing. On the player's own level a blank minimap stays as it was, since it fills in on its own as rooms are captured; on a level paged to deliberately, drawing nothing would be indistinguishable from a key that did nothing.
  • The -3 floor _descend() had inline was extracted to DEEPEST_Z and is now shared with the paging clamp.
  • KeyCode.F2 had no _DISPLAY_NAMES entry, so displayName() fell through to str() and the Controls screen rendered KeyCode.F2 in the Toggle NPC/CPC Mode row. The entry was added and the invariant is now covered by a test walking DEFAULT_BINDINGS against the table. It was found while the paging key codes were being added.

Decisions on the issue's open questions

  • Which levels are reachable — the full generated range (0 down to DEEPEST_Z), not only visited levels. The range is small and bounded, and the unexplored panel answers "what is down there" more usefully than a keypress that silently skips.
  • Whether the displayed level persists — it is discarded when the minimap is hidden, so re-enabling it always starts on the player's own level. It also resets on any descent or ascent, as the issue specifies.
  • Paging while no minimap is on screen reports "Minimap is off" and changes nothing, so no invisible state can accumulate. Visibility is judged by _isMiniMapVisible(), which mirrors both branches of the draw call — the text minimap is drawn even while the toggle is off, so a --text player can still page the grid in front of them.

Test plan

  • python3 -m black --check src tests — 276 files unchanged
  • python3 -m pytest tests/ — 1229 passed (+24)
  • python3 -m pytest tests/rendering/ tests/config/ -q — text-interface unit tests pass
  • python3 -m pytest tests/screen/ tests/ui/ -q — pygame-facing screen tests pass
  • python3 tests/integration/roamScript.py tests/integration/scripts/minimap_level_paging.roamscript — PASS; the new script drives , . / end to end in the real text frontend, covering the caption, both clamps and the one-press return
  • ⚠️ Live pygame smoke recommended before merge: page below the surface and confirm the caption and the unexplored panel render where expected

Documentation

README.md's controls table and its terminal-mode tip, the in-game help overlay (both frontends), CHANGELOG.md and the z-aware-minimap entry in docs/cave-generation-design.html were all updated.

Closes #559
Closes #562

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

dmccoystephenson and others added 2 commits August 13, 2026 05:10
Adds a displayed-level field distinct from the player's own level, three
key actions to page it (Page Up / Page Down / Home, alt , . /), and an
unexplored panel for a level with no stitched map yet. Both frontends
read the displayed level; descending, ascending and hiding the minimap
reset it.

Also adds the missing KeyCode.F2 display name, so the Controls screen no
longer renders "KeyCode.F2" for Toggle NPC/CPC Mode.

Closes #559
Closes #562

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ASCII return key

Self-review findings. The text minimap is drawn even while showMiniMap is
false (the elif beside drawMiniMap), so gating the paging keys on the
toggle alone left a --text player with a visible minimap that ignored
them; _isMiniMapVisible mirrors both branches of that draw call instead.
The status message also always named Home, which a terminal cannot send —
it now picks the alt binding in text mode, the way the caption already did.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review

The full diff was read against this repository's conventions (CLAUDE.md's frontend-abstraction and keybinding rules, the Phase 7 documentation table). Two defects were found and have been fixed in 5b61cd3 on this branch; the remaining notes are observations that were deliberately left alone.

Findings fixed on the branch

src/screen/worldScreen.py:1467 — the paging keys were gated on self.config.showMiniMap, but the text minimap is drawn regardless of that toggle: the draw site reads if self.config.showMiniMap and self.minimapScaleFactor > 0: self.drawMiniMap() / elif not self.renderer.supportsImageLoading(): self._drawTextMinimap(). A --text player who had toggled the minimap "off" would therefore still see the room grid while the paging keys answered "Minimap is off" and did nothing. _isMiniMapVisible() was added, mirroring both branches of that draw call, and both _pageMiniMapLevel and _resetMiniMapLevel now consult it. Covered by test_paging_works_in_text_mode_even_with_the_minimap_toggled_off.

src/screen/worldScreen.py:1428 — the status message named the primary reset binding unconditionally, so a terminal player was told "HOME to return" with a key a terminal cannot send. _getMiniMapResetKeyName() now picks alt_minimap_level_reset in text mode, which is what the on-screen caption was already doing through _buildMiniMapViewLabel. The duplicated selection between the two call sites was folded into that one helper. Covered by test_paging_status_names_the_ascii_return_key_in_text_mode.

Reviewed and considered correct

src/screen/worldScreen.py:1573 — the cache-invalidation compare was repointed from self.currentZ to the displayed level, so a stale frame from another level cannot be drawn: _cachedMiniMapImage is cleared on every change of displayed level, which means the "file missing but a cached image exists" branch is unreachable immediately after paging and the unexplored panel is what actually renders.

src/screen/worldScreen.py:1587 — the unexplored panel is drawn only when the displayed level is not the player's own. On their own level a missing map stays silent, which preserves the existing behaviour of a minimap that fills in as rooms are captured.

src/screen/worldScreen.py:1002 and :1029_miniMapViewZ is reset before _loadOrGenerateRoom, so no draw can observe a half-changed level. Storing None rather than the numeric level for "the player's own" is what makes a save loaded underground need no synchronisation at initialize() time.

src/config/keyBindings.py:46,, . and / are ASCII and reach TextInputSource through fromInt(ord(char)), and test_alt_bindings_have_no_conflict_with_primary confirms none of the six new keys collide with an existing binding.

One residual edge case was left as-is: a paged-to level whose map file exists but fails to load (corrupt PNG) still returns without drawing anything rather than showing the unexplored panel, because "unexplored" would be the wrong explanation for a corrupt file. That path already logs a warning once on the good-to-failed transition.

Documentation

README.md (controls table and the terminal-mode tip), the in-game help overlay in both frontends, CHANGELOG.md, and the z-aware-minimap entry in docs/cave-generation-design.html were each checked against the implementation after the fixes above. No further drift was found.

Test counts in the PR body and the changelog row were updated for the two added tests: 1229 passing.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

@dmccoystephenson
dmccoystephenson merged commit 353d002 into main Aug 13, 2026
5 checks passed
@dmccoystephenson
dmccoystephenson deleted the feat/minimap-level-paging branch August 13, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant