Game-file footprint unchanged: still one line in main.lua. All 23 states now have descriptors; 5 patches applied. Final two descriptors: - keybindings_menu has two panels (commands and keys) with independent selection, so it reads currentlyActiveWindow to decide which one has focus. Announcing the wrong panel's row would be actively misleading, since the two lists are unrelated. - research is a 2D node graph rather than a list, and its nodes are drawn as icons with no text label, so the id is the only identifier available. What actually matters is researched / locked / cannot afford / available: without the affordability part the screen is unusable by ear, because pressing enter on an unaffordable node silently does nothing. status.lua adds two pull keys -- ' for health and active effects, , for the current action -- plus unprompted damage alerts. Damage is polled from actor.hp rather than hooked. ingame.checkInterruptions exists but only fires for interruptions during queued movement, so damage taken on your own turn never reaches it. Polling catches every source (combat, poison, fire, falling) with no game edit. Damage speaks immediately and interrupts; healing queues, since it is worth knowing but never urgent. patches/popupstack.lua wraps popupStack.add, the single entry point for loot, recipe, lore, map, quest, tutorial and bestiary popups. None of these go through console.print, so they were entirely silent before. speech.immediate now logs to the console like queued speech does. It bypasses the frame queue and so also bypassed the flush-time logger, which meant damage alerts fired correctly but were invisible in the testbed transcript. Prefixes are now: ! interrupt, * immediate, space queued. 26/26 testbed checks, history unit tests passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
81 lines
2.3 KiB
Lua
81 lines
2.3 KiB
Lua
-- Patch: views/popupStack.lua
|
|
--
|
|
-- Popups are the game's way of telling you something happened without
|
|
-- interrupting play: loot found, a recipe learned, a quest updated, a new beast
|
|
-- recorded, a tutorial offered. They fade in at the side of the screen and none
|
|
-- of them go through console.print, so without this they are entirely silent.
|
|
--
|
|
-- popupStack.add{...} is the single entry point for all of them.
|
|
|
|
local speech = require("accessibility.speech")
|
|
|
|
-- What each popup type is called out loud. Keys match the `popupType` field
|
|
-- passed by states/ingame.lua and states/ingame_observe.lua.
|
|
local typeNames = {
|
|
loot = "Found",
|
|
recipe = "Recipe",
|
|
lore = "Lore",
|
|
map = "Map",
|
|
journal = "Quest",
|
|
tutorial = "Tutorial",
|
|
bestiary = "Bestiary",
|
|
}
|
|
|
|
local function describe(params)
|
|
local parts = {}
|
|
|
|
parts[#parts + 1] = typeNames[params.popupType] or params.popupType or "Notice"
|
|
|
|
-- `header` is the human-readable line the popup itself displays
|
|
-- ("You found:", a quest title), so prefer it over any id.
|
|
if params.header and params.header ~= "" then
|
|
parts[#parts + 1] = params.header
|
|
end
|
|
|
|
if params.loot then
|
|
for _, entry in ipairs(params.loot) do
|
|
local name = (type(entry) == "table") and (entry.name or entry.templateId) or tostring(entry)
|
|
if name then
|
|
local quantity = type(entry) == "table" and entry.quantity
|
|
parts[#parts + 1] = (quantity and quantity > 1)
|
|
and (name .. " x" .. quantity) or name
|
|
end
|
|
end
|
|
end
|
|
|
|
if params.quest and type(params.quest) == "table" and params.quest.title then
|
|
parts[#parts + 1] = params.quest.title
|
|
end
|
|
|
|
if params.templateId and not params.header then
|
|
parts[#parts + 1] = (tostring(params.templateId):gsub("_", " "))
|
|
end
|
|
|
|
return table.concat(parts, ": ", 1, math.min(2, #parts))
|
|
.. (#parts > 2 and (". " .. table.concat(parts, ", ", 3)) or "")
|
|
end
|
|
|
|
return function()
|
|
local popupStack = require("views.popupStack")
|
|
|
|
local realAdd = popupStack.add
|
|
if not realAdd then
|
|
error("views.popupStack has no add function -- game structure changed")
|
|
end
|
|
|
|
popupStack.add = function(params)
|
|
local result = realAdd(params)
|
|
|
|
pcall(function()
|
|
if type(params) ~= "table" then
|
|
return
|
|
end
|
|
-- Queued, not interrupting: a popup is informational, and several can
|
|
-- land in one turn (loot plus a quest update).
|
|
speech.say(describe(params), false)
|
|
end)
|
|
|
|
return result
|
|
end
|
|
end
|