items: read the details of the focused item
The item grids said which item you were on ("a torch, slot 3") but nothing
about what it was -- no description, no condition, and no "what does this
refine into", which is the whole of alchemy.
The game already composes all of it in one place: item.getPopupMessage()
(modules/item.lua:199) returns the cursor popup's line list, so read that
rather than re-deriving anything from templates.
It also already gates it behind a dwell -- the popup only fades in after
examinationPopupFadeInDuration (0.4s), so a sighted player who arrows past a
slot never sees the details either. Mirror that instead of inventing a
modality: stop on an item for 0.6s and the details speak themselves, queued.
"=" reads them on demand, Shift+= keeps the name line too. ("r" was the
obvious mnemonic but is a game-claimed scancode, and getCommand() dispatches
on scancode ignoring modifiers.)
New generic hook descriptor.details() returning a LIST of blocks, so each is
also pushed into the console history and [ ] \ walk a long description back
afterwards -- the journal page trick. Only _itemgrid.lua implements it so far.
The other half is the hint panel craft/refine/trade draw permanently beside
the grid, which the popup never contains: recipe components, and price, which
appears nowhere else in the game. The three panels share a shape, so one
reader covers them. Affordability survives only as a red colour in the hint
table and decides whether enter does anything, so it is decoded and spoken.
59 testbed checks, 0 failures: a synthetic ivy_leaves item as the version-bump
canary for getPopupMessage, hasUpvalue over every hint-panel local, four
assertions on the pure settleStep, and an end-to-end "=" press. Not yet
exercised against a live item -- the prologue inventory is empty.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c841bcc68d
commit
a00f327c68
@@ -2,12 +2,12 @@
|
||||
|
||||
**Read this first on any handoff.** Living document: update it whenever state changes, before ending a turn.
|
||||
|
||||
Last updated: 2026-08-02 · Currently: **Phases 0–5 COMPLETE. In playtesting** —
|
||||
Last updated: 2026-08-03 · Currently: **Phases 0–5 COMPLETE. In playtesting** —
|
||||
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, **38/38 checks**.
|
||||
verified by the testbed, **59/59 checks**.
|
||||
|
||||
**Not set up yet, or unsure how the two trees relate?** →
|
||||
[00-dev-setup.md](00-dev-setup.md).
|
||||
@@ -152,6 +152,7 @@ and by Talon at the keyboard (wall tones).
|
||||
| `Ctrl+Home` | Tracker: walk to current instance (the left-click path) |
|
||||
| `'` | Health and active effects |
|
||||
| `,` | Current selected action |
|
||||
| `=` / `Shift+=` | Read details of the focused thing (Shift = keep the name line) |
|
||||
|
||||
Game keys worth knowing: **`x`** enters examine mode (not `e`), `f` finds exits
|
||||
while examining, `j` journal, `i` inventory, `c` character, `a` actions menu.
|
||||
@@ -612,6 +613,77 @@ asserts the two upvalue names still resolve — the version-bump canary for this
|
||||
descriptor. **38/38 checks.** The live path (an actual torch, actually equipped)
|
||||
is Talon's to confirm.
|
||||
|
||||
### Done — item details: `=` and the settle timer (Talon request)
|
||||
|
||||
**Symptom:** on the item grids you could hear *which* item you were on
|
||||
(`a torch, slot 3`) but nothing about what it was — no description, no condition,
|
||||
and crucially no "what does this refine into", which is the whole of alchemy.
|
||||
Talon proposed a key. Two findings changed the shape of the answer.
|
||||
|
||||
**1. The game already composes the whole detail text in one place.**
|
||||
`item.getPopupMessage()` (`modules/item.lua:199`) returns the popup's line list:
|
||||
name+quantity, category, condition (only when `canDegrade`), the free-text
|
||||
description, a `Yields:` block with one line per refinement type, and
|
||||
lore-fragment progress. It is a plain method on the item, callable any time — so
|
||||
`accessibility/itemdetails.lua` reads it rather than re-deriving anything from
|
||||
templates. Verified text:
|
||||
`It is a raw material. Some fresh green leaves from ivy. They contain weak
|
||||
poison. Yields: x1 when ground in a mortar. x0.2 when distilled.`
|
||||
|
||||
**2. The game gates this behind a dwell, and so do we.** The popup only fades in
|
||||
after `config.get("examinationPopupFadeInDuration")` (default 0.4 s). Arrow past
|
||||
a slot quickly and a *sighted* player never sees the details either. So details
|
||||
**settle**: stop for `details.config.settleDelay` (0.6 s — longer than 0.4
|
||||
because speech is slower than a glance) and they speak themselves, queued not
|
||||
interrupting. `=` reads them on demand; `Shift+=` keeps the name line too.
|
||||
This mirrors an existing game behaviour instead of inventing a modality.
|
||||
|
||||
⚠ **`r` is unusable** even though it was the obvious mnemonic — it is in the
|
||||
game-claimed scancode list, and `keybindings.getCommand()` dispatches on
|
||||
scancode ignoring modifiers, so any binding on it can fire a game command. `=`
|
||||
is from the verified free list and sits by the `[ ] \` review cluster.
|
||||
|
||||
**New generic hook: `descriptor.details(stateTable, menus, verbose)`**, returning
|
||||
a **list of blocks**, not one string. Blocks are spoken joined *and* pushed
|
||||
individually into the console history, so `[` / `]` / `\` walk a long
|
||||
description back afterwards — the same trick `states/journal.lua` uses for pages.
|
||||
Driven by `accessibility/details.lua` (`get` / `speak` / `update` / the pure
|
||||
`settleStep`). Implemented so far only by `_itemgrid.lua`; `research`,
|
||||
`ingame_observe` and `journal` are the obvious cheap follow-ups.
|
||||
|
||||
**The hint panels are the other half, and one reader covers all three.**
|
||||
craft / refine / trade each draw a permanent panel beside the grid that the
|
||||
cursor popup never contains — and **price exists only there**
|
||||
(`states/trade.lua:156`), as do a recipe's component costs. The three panels turn
|
||||
out to share a shape (`hintDescription`, a `componentHints` list of
|
||||
`{nameText, descriptionText, color}`, and a budget line), so `hintBlocks()` in
|
||||
`_itemgrid.lua` reads all three by upvalue rather than needing per-state
|
||||
callbacks; unused fields are simply nil. **Affordability is carried only by the
|
||||
red `{0.9,0.2,0.2}` colour**, and it decides whether enter does anything at all,
|
||||
so it is decoded from the colour and spoken as `cannot afford`.
|
||||
|
||||
Notes:
|
||||
- The action menu is deliberately **not** excluded from `details` the way it is
|
||||
from `read()`. The menu is modal so the *verb* is what focus announces, but the
|
||||
thing you want described is still the item — which is exactly when you want to
|
||||
know what "Drink" would be drinking.
|
||||
- The settle key is state-qualified and fires **once per focus key**; leaving a
|
||||
screen and returning resets, or the same slot would be "already spoken".
|
||||
- `patches/popups.lua` still stands down on all four grids. That is now
|
||||
correct-by-design rather than a gap: `details` is the sanctioned route to that
|
||||
same content, on demand and with history review.
|
||||
|
||||
**59 testbed checks, 0 failures** (was 38). New: a synthetic `ivy_leaves` item
|
||||
asserting description + `Yields` survive (the version-bump canary for
|
||||
`getPopupMessage`), a `hasUpvalue` canary over all the hint-panel locals in
|
||||
craft/refine/trade, four assertions on the pure `settleStep`, and an end-to-end
|
||||
`=` press in the inventory (`No details` on an empty slot — silence would be
|
||||
indistinguishable from a dropped key).
|
||||
|
||||
⚠ **Not exercised against a real item.** Same bucket as the action menu: the
|
||||
prologue inventory is empty and craft/refine/trade need an alchemy table. The
|
||||
live path is Talon's to confirm.
|
||||
|
||||
### Known gaps (next up)
|
||||
- **Phase 5**: player status key (HP, statuses, current action), popup
|
||||
announcements (`popupStack.add`), damage alerts
|
||||
@@ -676,7 +748,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, 38/38 testbed checks**,
|
||||
Phases 0–5 complete. **23 state descriptors, 6 patches, 59/59 testbed checks**,
|
||||
history unit tests passing, **one line changed in the game** (`main.lua`).
|
||||
|
||||
Everything in the original brief is implemented: console speech with
|
||||
@@ -686,7 +758,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, 38 checks
|
||||
bash accessibility/tests/run-testbed.sh # ~4 min, 59 checks
|
||||
git diff --stat vanilla-0.4.2 -- . ':!accessibility' ':!.gitignore' ':!CLAUDE.md' # must stay: main.lua | 1 +
|
||||
```
|
||||
|
||||
@@ -721,6 +793,11 @@ These are blocked on content the prologue does not contain, **not** known broken
|
||||
- **Inventory item names, the `equipped` suffix and the item ACTION MENU** —
|
||||
inventory is empty in the prologue, so only the "empty slot" path has run live.
|
||||
The action-menu upvalue names are covered by a synthetic testbed check.
|
||||
- **Item details** (`=`, `Shift+=`, and the 0.6 s settle) against a real item,
|
||||
and the craft / refine / trade hint panels. The synthetic `ivy_leaves` check
|
||||
covers `getPopupMessage`, and `hasUpvalue` covers the panel locals, but no
|
||||
live item has been focused. The settle delay in particular wants tuning by
|
||||
ear — `details.config.settleDelay`.
|
||||
- **The quantity slider** (`patches/slider.lua`). Confirmed *applied* by the
|
||||
self-test but never exercised: `ingame_wait` is unreachable because
|
||||
`tutorial_bed` has an empty `tryInteractFunction` by design, and
|
||||
|
||||
Reference in New Issue
Block a user