Commit Graph
4 Commits
Author SHA1 Message Date
Accessibility ModandClaude Opus 5 973e62c8fe viewwatch: announce objects entering and leaving view while walking
Walking blind, the hard part is not "what is next to me" -- that is `/` --
but "what changed when I moved". Rounding a corner reveals a room, stepping
back hides it, a rat walks into sight behind you, and none of it is spoken.

Shift+/ cycles three modes:

  settled (default)  changes accumulate while moving, spoken once you have
                     stood still for 0.4s
  always             every step reports its own change
  off

`settled` diffs against the state at the last ANNOUNCEMENT, not the last
step, so a long walk gives one combined summary that is a genuine net
change: something that came into view mid-walk and left again before you
stopped is not mentioned, and something visible throughout is mentioned
once. Summing per-step diffs would report both. A single step is the
degenerate case -- you are already standing still.

Arrivals and departures are deliberately asymmetric. Membership is current
FoV only (never rememberedFoV), but furniture drops out of FoV constantly
indoors as walls come between you and it, and "the bed is out of view"
every other step is noise. Arrivals are reported for every watched
category; departures only for creatures.

Terrain is unwatched by default -- water arrives dozens of tiles at a time.
Step detection polls playerActor.x/y as walltones does, so pathing, portals
and pushes are covered with no game function wrapped; the scan is a 14-tile
box and runs only on a step or a due settle, never per frame.

snapshot() and diffText() are pure and exposed so the testbed can assert
the wording and the FoV invariant directly instead of capturing speech.
34/34 testbed checks, 0 failures. Game footprint unchanged: main.lua | 1 +.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 02:03:32 +02:00
Accessibility ModandClaude Opus 5 e1d9995c9f journal: speak the page text, not just the entry title
Opening the journal and selecting an entry announced its title and stopped,
which is an index into content the player could never reach. Page flipping was
silent too.

The screen is three widgets at once: the tab row (q/e), the list of entry
titles down the left (up/down), and the page on the right holding the actual
text (left/right). The descriptor read only the first two.

It now reads options[selectedOption].pages[selectedPageId] and speaks its text
blocks. A page is bounded by what fits on screen, so it is safe as a single
utterance. Layout newlines are flattened first -- splitTextByPages joins the
game's own wrapped lines with \n, so left in they make the reader pause
mid-sentence at every wrap point. Page position is announced only when there is
more than one page, and the blocks are pushed into the console history so [ and
] walk back through a long lore entry after the utterance has gone by.

The tab leads only when the tab changed and the title only when the entry
changed, so flipping pages does not bury the new page under a repeated heading.

That suppression made read() stateful, which is a trap: it is called for the
same focus from four places -- the per-frame poll, the post-keypress announce,
repeatFocus, and the testbed's before/after probes -- so a poll would consume
the "changed" flag and the real announcement would come out headless. The
descriptor now caches its last (key, text) and replays it whenever the key is
unchanged, making read() idempotent; the suppression state advances exactly
once per genuine focus change.

Adds a generic descriptor.reset hook, called from menus.announceScreen. The
memory lives in file-locals that outlive the screen, so without it reopening
the journal on the entry you left it on would read the page with no title.

Testbed asserts the spoken text contains the page body rather than merely being
non-nil. 29 checks, 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 01:04:03 +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 6380998376 docs: final handoff pass
Adds accessibility/README.md, the player-facing setup and keymap reference --
the thing to read before playtesting.

Brings the developer docs in line with what was actually built:

- docs/README.md rewritten as an index with current status and a one-paragraph
  architecture summary.
- docs/scratch.md gains a HANDOFF section: state of play, the commands to run
  after any change, the five traps that each cost real debugging time, what is
  NOT verified and why, deliberate omissions, and how to rebase onto a future
  game version.
- docs/03-implementation-plan.md gains a status banner listing where the
  implementation deliberately diverged from the plan (menu narration pulled
  forward as a blocker, levelCursorPopup not patched, tryInteract insufficient
  as a tracker filter, testbed added, no settings UI built).
- docs/04-open-questions.md gains a resolution banner: Q6 and Q8 remain open.

Also records that the testbed check count varies between runs (23-26) because
some blocks are conditional, so what matters is zero failures rather than the
total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 22:25:09 +02:00