loot overlay: speak the contents of visible containers
The tutorial teaches "press s to see the contents of containers", and it was silent. For a sighted player s sets levelWindow.itemsHighlightEnabled, which draws the top item's icon on every loot-bearing tile in FoV. We watch that flag rather than binding s ourselves -- it is game-claimed, and keybindings.getCommand() ignores modifiers -- and speak on its rising edge, so the player's own binding, hold vs toggle mode and the toolbar icon all work. The flag is checked both at end of frame and inside the keypressed wrapper, because in hold mode a quick tap goes up and down within one frame. Reads the whole tile.loot list, not just the icon's top entry, nearest first, with each container pushed to the console history for [ ] review. 71 testbed checks, 0 failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
dddd8ed9de
commit
93ef26349a
@@ -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, **59/59 checks**.
|
||||
verified by the testbed, **71/71 checks**.
|
||||
|
||||
**Not set up yet, or unsure how the two trees relate?** →
|
||||
[00-dev-setup.md](00-dev-setup.md).
|
||||
@@ -684,6 +684,62 @@ indistinguishable from a dropped key).
|
||||
prologue inventory is empty and craft/refine/trade need an alchemy table. The
|
||||
live path is Talon's to confirm.
|
||||
|
||||
### Done — the loot overlay key `s` (Talon request)
|
||||
|
||||
**Symptom:** the tutorial says *"Press 's' to see the contents of containers"*
|
||||
and nothing was spoken. That is the one mechanic the game explicitly teaches for
|
||||
finding reagents, and alchemy starts with finding reagents.
|
||||
|
||||
**What `s` actually does.** It is the `lootOverlay` command, and it sets a single
|
||||
flag — `levelWindow.itemsHighlightEnabled`. `views/levelWindow.lua:818` then
|
||||
draws, over every tile in **current FoV**, the icon of
|
||||
`levelModel.getItemOnCoordinates(x,y)` — i.e. the **topmost item of the topmost
|
||||
loot-bearing tile**. So the sighted affordance is "there is something in that
|
||||
chest", one icon per tile, not a contents list.
|
||||
|
||||
**`accessibility/lootoverlay.lua`** speaks the same set of tiles, nearest first,
|
||||
and reads out the **whole `tile.loot` list** rather than just the top entry —
|
||||
the icon can only show one, we are not so limited. Blocks are pushed into the
|
||||
console history so `[` / `]` / `\` walk a long list back. `No loot in view` when
|
||||
empty: silence would be indistinguishable from a dropped key.
|
||||
|
||||
Live text: `Tiled man-made floor, here, A bunch of ivy leaves, 3`.
|
||||
|
||||
Decisions worth not relitigating:
|
||||
|
||||
- **No key of ours.** `s` is game-claimed and `keybindings.getCommand()` ignores
|
||||
modifiers, so binding it in our layer would collide. We watch the *flag*
|
||||
instead, on its **rising edge**. That covers the key in both hold and toggle
|
||||
mode (`holdForItemHighlight`, which defaults to **hold**), the toolbar icon,
|
||||
the player's own rebinding, and all four ingame states — they share one
|
||||
`levelWindow` instance, created once in `ingame.open` and passed on by params.
|
||||
- **The flag is checked in TWO places.** The end-of-frame poll is not enough on
|
||||
its own: in hold mode a quick tap pushes keypressed and keyreleased inside one
|
||||
frame, so the poll only ever sees it back down. `init.lua`'s `states.keypressed`
|
||||
wrapper therefore also calls `lootoverlay.update()` *between* the game's
|
||||
keypressed and its keyreleased. The poll still earns its place — it is what
|
||||
catches the mouse and the key being released.
|
||||
- **`levelWindow` is reached by `menus.getAnyUpvalue` on `states/ingame`** and
|
||||
cached (it is created once, not per level). Covered by the startup self-test
|
||||
(`[accessibility] loot overlay: ok`) as the version-bump canary.
|
||||
- **Loot entries are item *params*, not items** — `{templateId, quantity}`;
|
||||
`modules/lootPopup.lua` only builds real items when the popup opens. Names come
|
||||
from `modules/item/templates`, so nothing is allocated to say a name.
|
||||
- **Membership matches levelWindow's test exactly**: current `FoV` only, not
|
||||
hidden, `visibilityAlpha ~= 0`, non-empty loot. `conditionalLoot` is *not*
|
||||
read — the overlay does not draw it either. Invariant 4 holds: nothing is said
|
||||
that is not being drawn on screen at that moment.
|
||||
- `unlootable` containers are still reported (the overlay draws them) but tagged
|
||||
`cannot be taken`. Scan is bounded to a 20-tile box and runs on the edge only,
|
||||
never per frame — invariant 5.
|
||||
|
||||
**71 testbed checks, 0 failures** (was 59). The prologue has no loot at all
|
||||
(`chest_tutorial` is empty by design), so the live path is exercised by
|
||||
**planting `ivy_leaves` on the tile under the player, scanning, and putting the
|
||||
tile back** — otherwise only the "nothing here" branch would ever run. Plus the
|
||||
pure phrasing checks, the FoV invariant over the real grid, the `levelWindow`
|
||||
probe, and an end-to-end `s` press.
|
||||
|
||||
### Known gaps (next up)
|
||||
- **Phase 5**: player status key (HP, statuses, current action), popup
|
||||
announcements (`popupStack.add`), damage alerts
|
||||
@@ -748,7 +804,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, 59/59 testbed checks**,
|
||||
Phases 0–5 complete. **23 state descriptors, 6 patches, 71/71 testbed checks**,
|
||||
history unit tests passing, **one line changed in the game** (`main.lua`).
|
||||
|
||||
Everything in the original brief is implemented: console speech with
|
||||
@@ -758,7 +814,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, 59 checks
|
||||
bash accessibility/tests/run-testbed.sh # ~4 min, 71 checks
|
||||
git diff --stat vanilla-0.4.2 -- . ':!accessibility' ':!.gitignore' ':!CLAUDE.md' # must stay: main.lua | 1 +
|
||||
```
|
||||
|
||||
@@ -803,6 +859,9 @@ These are blocked on content the prologue does not contain, **not** known broken
|
||||
`tutorial_bed` has an empty `tryInteractFunction` by design, and
|
||||
craft/refine/trade need an alchemy table and real items. The testbed already
|
||||
has `walkTo()` and tracker-based bed lookup ready for when one is reachable.
|
||||
- **The loot overlay (`s`) against real loot.** The prologue contains none, so
|
||||
live it has only ever said `No loot in view`. The contents path is proven by
|
||||
the testbed's planted-loot check, not by a real chest.
|
||||
- **Popup announcements** (`patches/popupstack.lua`) — loot/quest/recipe popups
|
||||
did not fire during the prologue run.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user