hotbar: say what the key you pressed just did

Pressing a hotbar key ran hotkey.action(state), which for an equipment
binding calls an itemActions entry's onActivation directly -- Wield,
Drink, Switch on, Pack away. None of those reach console.print or a
popup; most only play a sound. So the whole hotbar was silent: no item
name, no verb, no confirmation the key had registered at all.

patches/hotbar.lua wraps the hotbar VIEW factory, not models/hotkey:
models/hotbar.lua captures newHotkey as a file-local at require time,
and models/world.lua captures newHotbar the same way, both long before
our entry point. views/hotbar.lua is required only by the three states,
which load lazily -- the same ordering argument as patches/slider.lua.

The verb is computed BEFORE the action runs. The itemAction conditions
key off item.isEquipped, which the action itself flips, so asked
afterwards they return the opposite verb and a press that wielded would
announce "Unwield".

Also narrates the empty slot, the silent no-applicable-action branch,
binding and unbinding (sound-only in game), and action-type slots. Says
nothing when the item is missing from the inventory -- the game prints
that itself and patches/console.lua speaks it.

78 testbed checks, 0 failures. The prologue gives the player no items,
so the live path is exercised by planting a torch, binding it to slot 1,
pressing 1 twice and putting the world back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Accessibility Mod
2026-08-03 02:19:51 +02:00
co-authored by Claude Opus 5
parent 1f48dea550
commit 55b19dd537
4 changed files with 408 additions and 3 deletions
+53 -3
View File
@@ -7,7 +7,7 @@ fixing what Talon hits. Remaining work is polish, mod settings and distribution.
**Game is playable start-to-finish by ear** from main menu through the intro
conversation into gameplay, plus journal, character, actions and inventory —
verified by the testbed, **71/71 checks**.
verified by the testbed, **78/78 checks**.
**Not set up yet, or unsure how the two trees relate?** →
[00-dev-setup.md](00-dev-setup.md).
@@ -740,6 +740,56 @@ tile back** — otherwise only the "nothing here" branch would ever run. Plus th
pure phrasing checks, the FoV invariant over the real grid, the `levelWindow`
probe, and an end-to-end `s` press.
### Done — hotbar keys 1–0 (bug fix, reported by Talon)
**Symptom:** bind an item to the hotbar, press its key, and you hear the sound
the action plays — and nothing else. No item name, no verb, no confirmation that
the key registered at all.
**Cause.** `views/hotbar.lua:checkKeypress` resolves the slot and calls
`hotkey.action(state)` (`models/hotkey.lua`), which for an equipment binding
looks up the applicable entry in `modules/item/itemActions.lua` and calls its
`onActivation` **directly** — Wield, Unwield, Drink, Switch on, Pack away… None
of those go through `console.print` or a popup; most only play a sound. So the
whole hotbar was silent by construction.
**`accessibility/patches/hotbar.lua`** (7th patch) wraps the hotbar **view**
factory. Live text: `Torch, Wield` → `Torch, Unwield`.
Decisions worth not relitigating:
- **The view, not the model.** `models/hotbar.lua` captures `newHotkey` as a
file-local at require time, and `models/world.lua` captures `newHotbar` the
same way — both long before we run, so replacing `package.loaded` for either
would change nothing. `views/hotbar.lua` is required only by
`states/{ingame,inventory,actions_menu}`, which load lazily; same ordering
argument as `patches/slider.lua`.
- **The verb is computed BEFORE the action runs.** The itemAction conditions key
off `item.isEquipped`, which the action itself flips — asked afterwards they
return the opposite verb, so a press that wielded would announce "Unwield".
- **`hotkey.pack()` is the only public view of a hotkey** (`{type, command,
index, itemTemplateId}`); everything else in it is a closure local.
`hotbarModel` is tracked by wrapping `refreshModel` rather than probed, since
it is swapped when the player changes hotbar page.
- **`actionIdsFor` mirrors a file-local table** (`itemActionsByEquipmentCommand`
in `models/hotkey.lua`) that has no getter. It is exported and canaried by the
testbed: every id must still exist and still carry a header.
- **Item not in inventory: we say nothing.** The game already prints
`"<name> not found in your inventory."` and `patches/console.lua` speaks it.
- **Empty slot** is announced (`Hotbar 1, empty`) — silence is indistinguishable
from a dropped key. So is the silent "no applicable action" branch, except
where `autoSwitchToThrow` fires, which reports the new action instead.
- **Binding and unbinding are narrated too** (`self.bind`), since those are
sound-only as well: `Torch bound to hotbar 1` / `Hotbar 1 cleared`.
- Action-type hotkeys (hit/interact/throw/push) report the resulting action with
`status.lua`'s wording: `Action: Throw`.
**78 testbed checks, 0 failures** (was 71). The prologue gives the player no
items, so the live path is exercised by **planting a torch, binding it to slot 1,
pressing `1` twice, and putting the world back** (a lit torch would otherwise
change FoV for the viewwatch and loot checks that follow). Plus the header-table
canary and the `Wield`/`Unwield` flip.
### Known gaps (next up)
- **Phase 5**: player status key (HP, statuses, current action), popup
announcements (`popupStack.add`), damage alerts
@@ -804,7 +854,7 @@ utterance — speech is verifiable from the log, not only by ear. `!` after
## HANDOFF — read this if you are resuming
### State of play
Phases 0–5 complete. **23 state descriptors, 6 patches, 71/71 testbed checks**,
Phases 0–5 complete. **23 state descriptors, 7 patches, 78/78 testbed checks**,
history unit tests passing, **one line changed in the game** (`main.lua`).
Everything in the original brief is implemented: console speech with
@@ -814,7 +864,7 @@ exploration, wall tones, and keyboard+speech access to every screen.
### First thing to do after any change
```
lua accessibility/tests/test_history.lua # pure Lua, instant
bash accessibility/tests/run-testbed.sh # ~4 min, 71 checks
bash accessibility/tests/run-testbed.sh # ~4 min, 78 checks
git diff --stat vanilla-0.4.2 -- . ':!accessibility' ':!.gitignore' ':!CLAUDE.md' # must stay: main.lua | 1 +
```