Files
alchemist-accessibility/accessibility/docs
Accessibility ModandClaude Opus 5 a00f327c68 items: read the details of the focused item
The item grids said which item you were on ("a torch, slot 3") but nothing
about what it was -- no description, no condition, and no "what does this
refine into", which is the whole of alchemy.

The game already composes all of it in one place: item.getPopupMessage()
(modules/item.lua:199) returns the cursor popup's line list, so read that
rather than re-deriving anything from templates.

It also already gates it behind a dwell -- the popup only fades in after
examinationPopupFadeInDuration (0.4s), so a sighted player who arrows past a
slot never sees the details either. Mirror that instead of inventing a
modality: stop on an item for 0.6s and the details speak themselves, queued.
"=" reads them on demand, Shift+= keeps the name line too. ("r" was the
obvious mnemonic but is a game-claimed scancode, and getCommand() dispatches
on scancode ignoring modifiers.)

New generic hook descriptor.details() returning a LIST of blocks, so each is
also pushed into the console history and [ ] \ walk a long description back
afterwards -- the journal page trick. Only _itemgrid.lua implements it so far.

The other half is the hint panel craft/refine/trade draw permanently beside
the grid, which the popup never contains: recipe components, and price, which
appears nowhere else in the game. The three panels share a shape, so one
reader covers them. Affordability survives only as a red colour in the hint
table and decides whether enter does anything, so it is decoded and spoken.

59 testbed checks, 0 failures: a synthetic ivy_leaves item as the version-bump
canary for getPopupMessage, hasUpvalue over every hint-panel local, four
assertions on the pure settleStep, and an end-to-end "=" press. Not yet
exercised against a live item -- the prologue inventory is empty.

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

Accessibility mod — developer documentation

Making Sulphur Memories: Alchemist 0.4.2 blind-accessible. Player-facing keymap and setup: accessibility/README.md.

Picking this up mid-stream? Read scratch.md first. It is the living handoff document: current state, every bug found and why, and the traps.

Doc Contents
scratch.md Start here. Working state, bugs found, gotchas, keymap, how to run the testbed.
00-dev-setup.md Setting up. The two trees, prerequisites, building the dev tree, everyday commands, publishing.
01-architecture.md How the game is built: states, console, level model, keybindings.
02-hooking-strategy.md How the mod attaches with (near) zero edits to game files.
03-implementation-plan.md The original six-phase plan, with outcomes.
04-open-questions.md Questions raised; see 05 for answers.
05-decisions.md Decisions made, and the D4 correction.

Status

Phases 0–5 complete. 23 state descriptors, 5 patches, 34/34 testbed checks, history unit tests passing.

Game-file footprint: one line in main.lua. Verify at any time:

git diff --stat vanilla-0.4.2 -- . ':!accessibility' ':!.gitignore' ':!CLAUDE.md'

The three findings that shaped everything

  1. The game was already keyboard-complete. All 28 states implement keypressed, with a full rebindable-keys system. The gap was that nothing announced what was focused — so the job was narration, not reimplementation.

  2. The game already writes the text. console.print is a single chokepoint for all log messages; level.getCursorMessage() describes tiles; inventoryCursorPopup.set covers four screens. Mostly we re-route existing strings to speech rather than writing a description engine.

  3. Everything is a required singleton, and debug.getupvalue works. Module functions can be patched in place, and file-local focus variables (selectedOption, …) are readable from outside. Hence the one-line footprint.

Architecture in one breath

main.lua calls require("accessibility").initialize(). That patches module singletons (console.print, popupStack.add, inventoryCursorPopup.set, modules.slider), wraps states.switch / update / keypressed / textinput, and installs a global hotkey layer. Per-screen narration lives in accessibility/states/<statename>.lua descriptors, read via debug.getupvalue. A startup self-test reports which patches and probes still resolve — the canary for a future game version moving something.