Phase 0: accessibility mod bootstrap and TTS

Establishes the mod skeleton and gets speech working end to end.

Game-file footprint: one line in main.lua.

- accessibility/init.lua: sole entry point. Patch registry, hooks states.switch
  (announces screen changes) and states.update (per-frame speech flush, avoiding
  an edit to love.update). Startup self-test prints what resolved -- the canary
  for when a future game version moves something we hook.
- accessibility/speech.lua: TTS wrapper. pcall-guarded so a missing DLL degrades
  to silence instead of crashing. Queues utterances within a frame and joins them,
  since one game turn emits several console.print calls and speaking each with
  interrupt would leave only the last audible.
- accessibility/stateNames.lua: spoken names for the 28 states.
- accessibility/vendor/love2talk: vendored TTS library, see VENDORED.md.
  Loads Tolk.dll by absolute path via love.filesystem.getSource() and adds that
  directory via SetDllDirectory, so the DLLs live in the project instead of
  needing an elevated copy into Program Files.

Verified with lovec against NVDA: TTS available, speech channel yes,
'Main menu' spoken on startup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Accessibility Mod
2026-08-01 20:28:30 +02:00
co-authored by Claude Opus 5
commit e57350ded3
23 changed files with 1461 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# Vendored: love2talk
Source: `d:\code\love2talk` (by Piotr, MIT — see `LICENSE`).
Vendored 2026-08-01 for the Sulphur Memories accessibility mod.
## What was copied
`Love2talk.lua`, `tolk.lua`, `encoding.lua`, `macspeech.lua`, `LICENSE`.
## What was dropped
- `objc/` — Mac-only submodule, ~4 files. Re-add if a macOS build is ever wanted;
`macspeech.lua` is kept so that only the submodule is missing.
- `main.lua` — upstream's standalone demo, would collide with the game's own `main.lua`.
- `readme.md`, `.gitmodules`, `.gitignore` — not needed here.
## Local modifications
| File | Change | Why |
|---|---|---|
| `Love2talk.lua` | `require "Tolk"` → `require "accessibility.vendor.love2talk.tolk"` | Upstream assumes it sits at the project root. Also fixes a latent case bug: it required `"Tolk"` but the file is `tolk.lua` — works on case-insensitive Windows, would fail elsewhere. |
| `Love2talk.lua` | `backend` made `local` | Upstream leaked it as a global. The game's `.luacheckrc` uses a strict globals list, and a stray global risks colliding with game code. |
| `Love2talk.lua` | `say`/`isSpeaking` guard on `backend == nil` | On an unsupported OS upstream sets no backend, then dereferences it. Now degrades to silence. |
| `Love2talk.lua` | added `silence()` passthrough | `tolk.lua` already exposes `Tolk_Silence`; we need it to cut speech on interrupt. |
| `tolk.lua` | `require "encoding"` → `require "accessibility.vendor.love2talk.encoding"` | Same path reason. |
Nothing else was touched. `encoding.lua` and `macspeech.lua` are byte-identical to upstream.
## Runtime requirement — DLLs
`tolk.lua` does `ffi.load("Tolk")`, which goes through the **Windows loader**, not LÖVE's
virtual filesystem. The DLLs therefore **cannot live inside a `.love` archive or in this
folder** — they must sit next to the executable (`love.exe`) or on `PATH`.
Required next to `love.exe`:
- `Tolk.dll`
- `nvdaControllerClient64.dll` (NVDA — our target screen reader)
- `SAAPI64.dll` (System Access)
- plus any other screen-reader clients Tolk should support
`Love2talk.lua` calls `trySAPI(true)`, so with no screen reader running it falls back to
SAPI 5 and still speaks.
Also note `tolk.lua` calls `Tolk_Load()` **at require time** with no error handling — a
missing DLL raises immediately. Always `pcall` the require; `accessibility/speech.lua`
does this and degrades to a silent no-op.