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:
co-authored by
Claude Opus 5
parent
1f48dea550
commit
55b19dd537
@@ -464,6 +464,110 @@ local function scenario()
|
||||
end
|
||||
end
|
||||
|
||||
-- The HOTBAR keys. patches/hotbar.lua has to work out which itemAction a
|
||||
-- hotbar equipment command will resolve to *before* the action runs (the
|
||||
-- item's isEquipped flips, and afterwards the conditions pick the opposite
|
||||
-- verb). That mirrors a file-local table in models/hotkey.lua, so this is
|
||||
-- its version-bump canary: every id must still exist and still have a
|
||||
-- header, and a real item must still resolve to the expected verb.
|
||||
do
|
||||
local hotbarPatch = require("accessibility.patches.hotbar")
|
||||
local itemActions = require("modules.item.itemActions")
|
||||
local newItem = require("modules.item")
|
||||
|
||||
local missing = {}
|
||||
for command, ids in pairs(hotbarPatch.actionIdsFor) do
|
||||
for _, id in ipairs(ids) do
|
||||
local action = itemActions[id]
|
||||
if not (action and action.header) then
|
||||
missing[#missing + 1] = command .. "/" .. id
|
||||
end
|
||||
end
|
||||
end
|
||||
check("every hotbar item action id still has a header",
|
||||
#missing == 0, "missing: " .. table.concat(missing, ", "))
|
||||
|
||||
-- A torch is onWield/onUnequip, so equipUnequip must flip between the
|
||||
-- two verbs with isEquipped -- exactly what the announcement says.
|
||||
local torch = newItem{templateId = "torch", quantity = 1}
|
||||
check("hotbar names the verb for an unequipped item",
|
||||
hotbarPatch.headerFor(torch, "equipUnequip") == "Wield",
|
||||
tostring(hotbarPatch.headerFor(torch, "equipUnequip")))
|
||||
|
||||
torch.isEquipped = true
|
||||
check("hotbar names the opposite verb once equipped",
|
||||
hotbarPatch.headerFor(torch, "equipUnequip") == "Unwield",
|
||||
tostring(hotbarPatch.headerFor(torch, "equipUnequip")))
|
||||
torch.isEquipped = false
|
||||
|
||||
local cannotDrink = hotbarPatch.headerFor(torch, "drink")
|
||||
check("hotbar stays quiet for a command the item cannot do",
|
||||
cannotDrink == nil, tostring(cannotDrink))
|
||||
end
|
||||
|
||||
-- End to end: bind a real item to slot 1 and press it. The prologue gives
|
||||
-- the player nothing, so plant a torch, bind it, press "1" twice (wield,
|
||||
-- then unwield) and put everything back. This is the only check that
|
||||
-- proves the whole chain -- patch applied, keybinding resolved, model
|
||||
-- looked up, pack() read, verb chosen -- rather than one link of it.
|
||||
do
|
||||
local speech = require("accessibility.speech")
|
||||
local player = require("models.player")
|
||||
local world = require("models.world")
|
||||
|
||||
local spoken = {}
|
||||
local realSay = speech.say
|
||||
speech.say = function(text, ...)
|
||||
spoken[#spoken + 1] = tostring(text)
|
||||
return realSay(text, ...)
|
||||
end
|
||||
|
||||
local hotbarModel = world.hotbars and world.hotbars[1]
|
||||
local hadHotkey = hotbarModel and hotbarModel.hotkeys[1]
|
||||
|
||||
-- An empty slot is silent in game -- press before binding anything.
|
||||
if hotbarModel and not hadHotkey then
|
||||
spoken = {}
|
||||
press("1", nil, "hotbar: empty slot")
|
||||
check("hotbar says when a slot is empty",
|
||||
table.concat(spoken, " | "):find("empty", 1, true) ~= nil,
|
||||
table.concat(spoken, " | "))
|
||||
end
|
||||
|
||||
if hotbarModel then
|
||||
player.addItem{templateId = "torch", quantity = 1}
|
||||
hotbarModel.addHotkey{
|
||||
type = "equipment",
|
||||
command = "equipUnequip",
|
||||
itemTemplateId = "torch",
|
||||
index = 1,
|
||||
}
|
||||
|
||||
spoken = {}
|
||||
press("1", nil, "hotbar: wield the torch")
|
||||
local said = table.concat(spoken, " | ")
|
||||
check("hotbar names the item and what it did to it",
|
||||
said:find("Torch", 1, true) ~= nil and said:find("Wield", 1, true) ~= nil,
|
||||
said)
|
||||
|
||||
spoken = {}
|
||||
press("1", nil, "hotbar: put the torch away")
|
||||
local saidAgain = table.concat(spoken, " | ")
|
||||
check("a second press announces the opposite verb",
|
||||
saidAgain:find("Unwield", 1, true) ~= nil, saidAgain)
|
||||
|
||||
-- Put the world back: a lit torch changes FoV and light, which the
|
||||
-- viewwatch and loot-overlay checks below would then inherit.
|
||||
hotbarModel.hotkeys[1] = hadHotkey
|
||||
local planted = player.inventory.findItemByTemplateId("torch")
|
||||
if planted then
|
||||
planted.remove(1)
|
||||
end
|
||||
end
|
||||
|
||||
speech.say = realSay
|
||||
end
|
||||
|
||||
-- Item DETAILS (the `=` key and the settle timer). Same problem as the
|
||||
-- action menu above: the prologue inventory is empty, so there is no real
|
||||
-- item to focus. The fragile parts are (a) item.getPopupMessage still
|
||||
|
||||
Reference in New Issue
Block a user