Game-file footprint unchanged: still one line in main.lua. The slider turned out to be genuinely generic: modules/slider.lua is a factory, so one wrapper on it narrates all four users -- craft, refine, trade and ingame_wait (the wait-N-turns screen). Same leverage as the console.print and inventoryCursorPopup patches. The options screens do NOT share it, though: config_menu changes values through config.increase/config.decrease, so it needed its own descriptor. It reports tab, option name and value, and its focus key includes the value so left/right re-announce even though the focused option has not moved. states/_itemgrid.lua is now shared by inventory, craft, refine and trade, all of which build a views/inventoryWindow. It stays silent while confirmationWindowEnabled is set so the slider owns speech during quantity selection instead of the two fighting. testbed gains walkTo(), which navigates using the game's own pathfinder rather than hardcoded direction sequences -- those drift as soon as anything moves the player. The config test only adjusts a value when the focused option is demonstrably safe, and puts it straight back. The slider patch is applied and confirmed by the self-test but not exercised live: ingame_wait is unreachable in the prologue because tutorial_bed overrides bed's tryInteractFunction with an empty function by design, and the crafting screens need an alchemy table and real items. Recorded as a NOTE rather than a failing check, since it is a property of the game and not of the mod. 25/25 testbed checks, history unit tests passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
68 lines
2.2 KiB
Lua
68 lines
2.2 KiB
Lua
-- Patch: modules/slider.lua
|
|
--
|
|
-- The slider is genuinely generic: `modules/slider.lua` is a factory
|
|
-- (`newSlider(params)`) and every quantity slider in the game comes from it.
|
|
-- Four states use it:
|
|
--
|
|
-- craft -- how many items to process
|
|
-- refine -- same
|
|
-- trade -- how many to buy/sell
|
|
-- ingame_wait -- how many turns to wait
|
|
--
|
|
-- So one wrapper on the factory narrates all four, with no per-state work. This
|
|
-- is the same leverage as the console.print and inventoryCursorPopup patches.
|
|
--
|
|
-- NOTE: the options screens (config_menu / settings_menu) do NOT use this. They
|
|
-- adjust values through config.increase / config.decrease instead, and are
|
|
-- handled by accessibility/states/config_menu.lua.
|
|
|
|
-- ORDERING: this replaces package.loaded, so it only affects requires that happen
|
|
-- AFTER it runs. That is safe here because states are loaded lazily by states.lua
|
|
-- (each `require("states/"..name)` happens on first switch), and these four are
|
|
-- the only requirers. applyPatches() also runs before the self-test, which is the
|
|
-- one thing that loads state files early.
|
|
|
|
local speech = require("accessibility.speech")
|
|
|
|
return function()
|
|
local newSlider = require("modules.slider")
|
|
|
|
if type(newSlider) ~= "function" then
|
|
error("modules.slider is not a factory function -- game structure changed")
|
|
end
|
|
|
|
package.loaded["modules.slider"] = function(params)
|
|
local slider = newSlider(params)
|
|
|
|
local realSetValue = slider.setValue
|
|
if not realSetValue then
|
|
return slider
|
|
end
|
|
|
|
-- Announce only on an actual change. setValue is called during
|
|
-- construction, on every frame of a mouse drag, and on each repeat of a
|
|
-- held key -- speaking unconditionally would stutter badly.
|
|
local lastSpoken = slider.currentValue
|
|
|
|
slider.setValue = function(newValue, noFlip)
|
|
local result = realSetValue(newValue, noFlip)
|
|
|
|
pcall(function()
|
|
local current = slider.currentValue
|
|
if current == lastSpoken then
|
|
return
|
|
end
|
|
lastSpoken = current
|
|
|
|
-- Interrupt: the value is the answer to the key just pressed, and
|
|
-- a backlog of intermediate numbers is worse than useless.
|
|
speech.say(tostring(current), true)
|
|
end)
|
|
|
|
return result
|
|
end
|
|
|
|
return slider
|
|
end
|
|
end
|