Files
alchemist-accessibility/accessibility/docs
Accessibility ModandClaude Opus 5 a1ad7f26c2 popups: read the modal recipe, lore and map popups
Learning a recipe spoke the word "Popup" and then nothing.

There are two separate popup mechanisms and only one was covered.
patches/popupstack.lua wraps popupStack.add, but views/fadingPopup.lua
registers just four types -- journal, loot, bestiary and tutorial -- so
the recipe/lore/map keys in that patch could never fire. Those three are
modal instead: states/{ingame,inventory,research} build one from
modules/{recipe,lore,map}Popup.lua and switch to states/ingame_popup.

ingame_popup had a name in stateNames.lua and no descriptor, so
announceScreen said "Popup" and the focus poll had nothing to add.

The popup modules are factories exposing only draw(), so every string is
a closure local. The new descriptor takes popupObject off the state and
reads them as upvalues of popupObject.draw, detecting the type by which
locals exist since the object carries no type tag. Reading order follows
draw order, newlines are flattened (the lore footer is one sentence with
hard breaks in it), and the blocks go into the console history so [ and ]
walk a long fragment back; = re-reads it.

The prologue fires no popup, so the testbed raises them itself via
ingame.recipePopup and ingame.lorePopup -- both display-only, so nothing
is learned and the world is untouched. That check is this descriptor's
version-bump canary: the self-test can only report "custom read()" here,
because the probe is on the popup object rather than the state.

88 testbed checks, 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 02:29:01 +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.