# Decisions Answers given, and their consequences. Supersedes the corresponding entries in `04-open-questions.md`. --- ## D1 (was Q1) — Development uses system LÖVE Dev setup: `C:\Program Files\LOVE\love.exe` (11.5, matches `build.lua`'s `love = '11.5'`), running the source folder directly. `lovec.exe` in the same directory gives a console window and is what we'll use for debugging. Tolk DLLs therefore go **next to `love.exe` in `C:\Program Files\LOVE\`** — that's the executable directory the OS loader searches. (Copies available at `D:\code\cafe`, `D:\code\dragonrpg-mapmaker`, `D:\code\DragonWarriorAccess`.) Needs an elevated copy, one time. Distribution deferred until the mod works. Noted for later: whatever we ship must place the DLLs beside the executable, so a plain `.love` file can never be the whole delivery. --- ## D2 (was Q2) — Tracker hierarchy confirmed Three levels, as proposed: | Level | Key | Example | |---|---|---| | Category | `Ctrl+PgUp` / `Ctrl+PgDn` | "Creatures" | | Item (kind) | `PgUp` / `PgDn` | "rat" | | Instance | `Shift+PgUp` / `Shift+PgDn` | that specific rat at (12,7) | | — | `Home` | announce distance + direction of current instance | Categories accepted: **Creatures / Items / Exits / Interactables / Terrain features / Alchemy**, conditional on covering everything. See D4 for the coverage check. --- ## D3 (was Q7 + Q10) — Small patches now, upvalue probing kept as a fallback layer Decision: use **small explicit patches** in game state files where they genuinely simplify things — but keep the option of switching to zero-edit operation, in case the game ships on Steam and editing files stops being practical for end users. ### Why Steam changes things A Steam LÖVE release is normally a **fused executable** (the `.love` archive appended to `love.exe`). Editing game source then means unpack → patch → repack → replace the exe, which Steam's file integrity check may revert on next update. That's a bad end-user story, and it's exactly the scenario the upvalue technique was designed for. ### The design that keeps both doors open Make the **state descriptor** the single abstraction, and let each descriptor declare *how* it gets focus — with two interchangeable backends: ```lua -- accessibility/states/main_menu.lua return { title = "Main menu", -- Backend A (preferred): the patched game file calls access.announce(...) directly. -- Backend B (fallback): read the same values as upvalues, no game edit needed. probe = { fn = "keypressed", focus = "selectedOption", list = "optionsList" }, render = function(entry, i, n) return ("%s, %d of %d%s"):format(entry.title, i, n, entry.isEnabled and "" or ", unavailable") end, } ``` `render` — the part that carries all the per-screen knowledge — is **identical for both backends**. Switching modes is a global config flag, not a rewrite: - `mode = "patched"` → the driver trusts `access.announce(entry, i, n)` calls injected into game files. - `mode = "probe"` → the driver wraps `states.keypressed`, reads upvalues via `debug.getupvalue`, and calls the same `render`. This costs us little now (we write `render` either way, plus a ~40-line probe driver) and means a Steam release is a config change plus fixing whichever probes drifted — not a redesign. ### Consequences - Patches stay **mechanical and uniform**: a single `access.announce(...)` line per focus-change site, nothing else. Easy to read in a diff, easy to re-apply. - The git baseline (`vanilla-0.4.2` tag) becomes load-bearing — it's how we rebase patches onto a new version. Do this in Phase 0. - The **startup self-test** is still required, and now checks both backends: which patch sites are present, and which probes still resolve. This is the version-bump canary either way. - Upstreaming to Spellweaver stays viable, since the patches are small and additive. Worth revisiting once there's something working to show. --- ## D4 — Category coverage check (the condition on D2) I classified all tile templates statically to confirm the six categories don't leave gaps. **Scale is much smaller than the line counts suggested:** | Template set | Count | |---|---| | Tile templates (`modules/tile/templates.lua`, 6745 lines) | **231** | | Actor templates (`models/actor/templates.lua`) | **40** | | Item templates (`modules/item/templates.lua`) | **344** | 231 tile templates is very tractable — the line count is inflated by inline behaviour functions, not by breadth. **Parent inheritance is already flattened for us.** `modules/tile/preprocess.lua` copies every missing field down from `template.parent` at load time. Since `main.lua` runs `preprocessTileTemplates()` during `love.load`, by the time our code runs, `require("modules.tile.templates")` gives fully-resolved templates — we read `template.name` / `template.description` / flags directly with no chain-walking. *(Minor upstream quirk noticed: the flatten is a single pass over `pairs()`, so a grandchild template may inherit from a not-yet-flattened parent depending on hash order. Pre-existing game behaviour, not ours to fix — but if a template ever looks oddly under-specified, this is why.)* **Only 6 templates are genuinely un-nameable** (no name, no description, no parent) — all invisible mechanics that *should* be excluded from tracking: `blank`, `throw_blocker`, `movement_blocker`, `fire_detector`, `wall_features_position_dummy`, `ladder_down_hidden`. Every other template resolves to a name/description and lands in a category. **The six categories cover the space.** **The classifier flags exist and are clean:** | Signal | Count | Maps to | |---|---|---| | `tryInteractFunction` | 139 | Interactables / Exits | | `loot` | 68 | Items (+ Alchemy when the loot is an ingredient) | | `blocksMovement` | 133 | Terrain (walls) | | `blocksVision` | 127 | Terrain | | `isThreatening` | 15 | Terrain features → hazards | | `getPassability` | 11 | Terrain → water/special | | `isLightSource` | 50 | (not a category; useful description detail) | | `isPushable` / `isBreakableByHand` | 17 / 17 | Interactables | | `isWallFeature` | 22 | Terrain | Critically, `modules/tile.lua:234` defines the runtime flag as: ```lua self.tryInteract = (self.loot or self.tryInteractFunction) and function(self,actor) ... end ``` So **`tile.tryInteract` alone cleanly separates "the player can do something here" from scenery** — exactly the flag-first classification proposed in `03-implementation-plan.md`, and it's authoritative rather than heuristic. **Residual hand-maintained list** is therefore small: the 6 exclusions above, the existing `exit` tag set (`way_out`, `building_door`, `horse`, `ladder_up`, `ladder_down`), and an ingredient-node list to split **Alchemy** out of **Items**. Dozens of entries, not hundreds — and new content in future versions falls through to a sensible default category rather than vanishing. --- ## Still open From `04-open-questions.md`: **Q3** (verbosity: relay vs. augment), **Q4** (console auto-speak policy), **Q5** (wall tone triggers), **Q6** (keybinding integration), **Q8** (full keyboard audit as explicit deliverable?), **Q9** (which screen reader; do you want me running the game during dev). None of these block starting Phase 0. Q5 and Q9 become relevant at Phase 2; Q4 at Phase 1.