Commit Graph
3 Commits
Author SHA1 Message Date
Accessibility ModandClaude Opus 5 5778e86e8e orientation: the travel map, unexplored ways, the nearest exit
Prompted by "do sighted players have a minimap we don't have access to?"
No widget, but two map surfaces were only half exposed: the world map
screen, and the level view itself, which IS the explored-floor map --
rememberedFoV keeps everything ever seen drawn, and a sighted player pans
over it and reads the fog edge at a glance.

The world map dropped the three things the picture carries: what kind of
place each area is (the icon shape), which way it lies, and how long
travelling there takes. The last is the number the decision turns on and
was only ever in the side panel. states/worldmap.lua is now a read() plus
details() rather than probe/render -- necessarily, because render() gets
no state table and the travel time needs the screen's currentAreaId, an
upvalue like options and selectedOption. Travel time is the game's own
getDistanceMessage, the same call the screen makes. The blurb runs to
several sentences, so it goes on = and the settle timer instead of the
focus line.

Two gotchas: options[i].icon is already an image object, so the icon id
string has to come off the model area; and describe.direction only tests
the sign of each delta, which on the map's floating-point coordinates
called almost everything diagonal, hence a 25% deadzone carrying the same
4/3 x-correction the game's own distance maths uses.

New frontier.lua answers "where haven't I been", which nothing did:
surroundings is local, examine is one tile, and the tracker only finds
things it already knows about. A frontier tile is seen, not blocking
movement, with an in-bounds unseen CARDINAL neighbour -- cardinal because
the diagonal test fires on the unseen tile behind every wall corner, so a
lit corridor would report an unexplored way per corner. Clustered
8-connected and reduced to each cluster's nearest member, so a wide
doorway is one way but two openings that both lie north stay two. Reads
only FoV/rememberedFoV and reports only THAT an opening exists, never
what is beyond it -- exactly what the fog edge shows.

Shift+End names the nearest exit without disturbing the tracker
selection. Note for later: level.exits does not exist at runtime, it is
packed as nil and never assigned. The primitive is the game's own
getListByTag("exit"), which already gates on FoV or rememberedFoV, skips
hidden tiles and sorts by distance. Exits were already a tracker
category, so this is a shortcut rather than a new capability.

"remembered" moves from verbose to normal verbosity. tileFacts reads
actors only when a tile is currently VISIBLE, so a remembered tile is
silent about whatever may be standing on it now, and at normal verbosity
that silence was indistinguishable from "nothing there". A safety flag,
not a nicety.

Also adds descriptor.probeNames: a read() descriptor has no probe table,
so menus.selfTest could only report "custom read()" and the version-bump
canary was lost on exactly the screens reaching for the most upvalues.
worldmap and research now declare their game locals and get the same
PROBE BROKEN check.

120 testbed checks, 0 failures. It caught two things review had not. The
world map said "You are currently here." twice, because
getDistanceMessage and getLastVisitedMessage return the same sentence for
the area you are standing in -- fine as two panels on screen, a stutter
in one utterance. And the first frontier check walked a step east and
demanded the numbers change; it failed on a run where nothing was wrong,
the step landing but both openings keeping the same Chebyshev distance.
Replaced with the deterministic property: every reported distance must
equal Chebyshev from the player's current position, nearest first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 16:05:15 +02:00
Accessibility ModandClaude Opus 5 81a209b600 tracker: sticky selection and Ctrl+Home walk-to; walltones: conventional volume
Tracker selection now survives a rescan. The scan is rebuilt every turn and
both lists are distance-sorted, so holding a bare index meant one step by the
player silently re-pointed the selection at a different rat. The tracker now
remembers identity instead -- the actor table for creatures, coordinates for
tiles -- and re-derives the indices in reanchor() after each rescan. Selection
is dropped only when the thing genuinely stops being reported (out of FoV,
killed, picked up), and the existing clamp then falls back to the nearest.

Ctrl+Home walks to the selection, reusing the exact shape of the game's own
click-to-path in ingame.mousepressed: calculatePathFromPlayer, then either
bufferInput or playerInput for a one-step path. Interruptions and threatened-
player truncation therefore behave the way a mouse click does. Most things
worth tracking stand on impassable tiles, which calculatePathFromPlayer refuses
outright, so we fall back to the cheapest of the 8 neighbours and say "next to"
rather than "to". Guarded on states.getCurrent() == "ingame": elsewhere the
input buffer belongs to another state and feeding it moves would be a bug.

Wall tones now follow the usual proximity convention -- a near wall is loud,
one out at radius is quiet. It was inverted, which read backwards in play since
a nearby wall is the more urgent fact. config.louderWhenFurther still flips it.

Testbed covers both: selection identity across a forced rescan, and that
Ctrl+Home moves the player without breaking out of the map.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 01:00:25 +02:00
Accessibility ModandClaude Opus 5 c17c5277d4 Phase 3: object tracker
Game-file footprint unchanged: still one line in main.lua.

Three-level navigation as specified in D2: Ctrl+PageUp/Down cycles categories,
PageUp/Down cycles kinds, Shift+PageUp/Down cycles instances of that kind, Home
announces where it is.

- categories.lua: flag-first classification into Creatures, Items, Alchemy,
  Interactables, Exits and Terrain features, reusing the game's own 'exit' tag
  and excluding the six nameless mechanical templates.
- tracker.lua: scan, cache, navigation, announcements.

Caching keys on level name, player position and turn: position because
everything sorts by distance to the player, turn because creatures move and
containers empty. Scans run on keypress, never per frame.

FoV is respected throughout -- creatures only when currently visible, tiles when
visible or remembered -- so the tracker never becomes a cheat.

Home reports path length alongside the offset, via the game's own
calculatePathFromPlayer. Straight-line distance is misleading indoors, where a
chest three tiles north through a wall can be twenty steps away.

Corrects D4 in docs/05-decisions.md. I had claimed tile.tryInteract cleanly
separates 'the player can do something here' from scenery. It does not: 'wall'
carries a flavour-only tryInteractFunction ('You touch the wall. It's cold and
rough.'), so every wall segment was promoted into Interactables -- 13 in the
starting house, burying the chest and cauldron. Structural tiles
(blocksMovement and blocksVision) are now excluded before the tryInteract test;
containers and furniture block movement but not sight, so they survive.
12 kinds/19 instances instead of 13/32.

13/13 testbed checks, history unit tests passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 21:34:13 +02:00