2026-08-01 21:26:35 +02:00
|
|
|
-- Patch: views/levelCursorPopup.lua and views/inventoryCursorPopup.lua
|
|
|
|
|
--
|
|
|
|
|
-- `inventoryCursorPopup.set` is the convergence point for focus text in four
|
|
|
|
|
-- screens: inventory, craft, refine and trade. Patching it narrates all four
|
|
|
|
|
-- without touching any state file.
|
|
|
|
|
--
|
|
|
|
|
-- inventoryCursorPopup.set(lines, x, y, alignX, alignY) -- lines is a TABLE
|
|
|
|
|
-- of {text=, icon=}
|
|
|
|
|
--
|
|
|
|
|
-- NOTE: levelCursorPopup (the examine cursor) is deliberately NOT handled here.
|
|
|
|
|
-- It is set from an animation-completion callback, so its text lands a keypress
|
|
|
|
|
-- late unless the player enables the game's "quickExaminationPopup" option.
|
|
|
|
|
-- accessibility/states/ingame_observe.lua polls the cursor position instead,
|
|
|
|
|
-- which is immediate and setting-independent.
|
|
|
|
|
|
|
|
|
|
local speech = require("accessibility.speech")
|
|
|
|
|
|
|
|
|
|
-- Suppress repeats: the setter is called on every mouse move, often with
|
|
|
|
|
-- unchanged content.
|
|
|
|
|
local lastInventoryText
|
|
|
|
|
|
|
|
|
|
local function linesToText(lines)
|
|
|
|
|
if type(lines) ~= "table" then
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local parts = {}
|
|
|
|
|
for _, line in ipairs(lines) do
|
|
|
|
|
if type(line) == "table" and line.text then
|
|
|
|
|
parts[#parts + 1] = line.text
|
|
|
|
|
elseif type(line) == "string" then
|
|
|
|
|
parts[#parts + 1] = line
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if #parts == 0 then
|
|
|
|
|
return nil
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return table.concat(parts, ". ")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return function()
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
-- inventoryCursorPopup: inventory, craft, refine, trade
|
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
local inventoryPopup = require("views.inventoryCursorPopup")
|
|
|
|
|
|
|
|
|
|
local realInventorySet = inventoryPopup.set
|
|
|
|
|
if not realInventorySet then
|
|
|
|
|
error("views.inventoryCursorPopup has no set function -- game structure changed")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
inventoryPopup.set = function(lines, ...)
|
|
|
|
|
local result = realInventorySet(lines, ...)
|
|
|
|
|
|
|
|
|
|
pcall(function()
|
2026-08-01 22:00:52 +02:00
|
|
|
-- Stand down for any state that has its own descriptor. A descriptor
|
|
|
|
|
-- knows about empty slots and tab changes, which this popup never sees
|
|
|
|
|
-- (it is only called when there IS an item), so letting both speak
|
|
|
|
|
-- would double up on every item.
|
|
|
|
|
local states = require("states")
|
|
|
|
|
local menus = require("accessibility.menus")
|
|
|
|
|
if menus.loadDescriptor(states.getCurrent()) then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
2026-08-01 21:26:35 +02:00
|
|
|
local text = linesToText(lines)
|
|
|
|
|
|
|
|
|
|
if not text then
|
|
|
|
|
lastInventoryText = nil
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
text = speech.normalise(text)
|
|
|
|
|
if not text or text == lastInventoryText then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
lastInventoryText = text
|
|
|
|
|
|
|
|
|
|
speech.say(text, true)
|
|
|
|
|
end)
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
end
|
|
|
|
|
end
|