Files

90 lines
3.1 KiB
Lua
Raw Permalink Normal View History

2026-08-03 01:31:58 +02:00
-- itemdetails.lua — turn an item into speakable detail blocks.
--
-- The game already composes the whole of an item's detail text in one place:
-- `item.getPopupMessage()` (modules/item.lua:199). It returns a list of
-- `{text =, icon =, font =}` lines that the cursor popup renders:
--
-- a torch. <- name, with (quantity) if stackable
-- It is a tool. <- category, from item/categories.lua
-- It is pretty worn (43% condition).<- only when canDegrade
-- <the free-text description>
-- Yields: <- only when the item can be refined
-- x1 when ground in a mortar. <- one per refinement type
-- Contained knowledge: <- only when containedLore
-- (2/5 fragments learned so far)
--
-- That is exactly what a player needs to evaluate an item, including "what does
-- this turn into", and it is already assembled -- so we read it rather than
-- re-deriving any of it from templates.
--
-- The icons are dropped: every icon line's `text` already names the thing in
-- words ("x1 when ground in a mortar"), so nothing is lost by ear.
--
-- Pure formatting, no LÖVE and no game state, so the testbed can exercise it
-- against a synthetic item.
local itemdetails = {}
-- The game wraps its own text and joins the wrapped lines with "\n", so those
-- newlines are pure layout. Left in, the reader pauses mid-sentence at every
-- wrap point. Same treatment as journal pages (states/journal.lua).
local function flatten(text)
return (tostring(text):gsub("%s*\n%s*", " "):gsub("^%s+", ""):gsub("%s+$", ""))
end
itemdetails.flatten = flatten
-- Does this popup line merely repeat the name the focus announcement just said?
-- getPopupMessage's first line is "<name>." or "<name> (3)." -- speaking it again
-- immediately after "a torch, slot 1" is pure repetition.
local function isNameLine(text, item)
local name = item and item.name
if type(name) ~= "string" then
return false
end
return text:sub(1, #name) == name
end
-- Returns a list of speakable blocks for `item`, or nil if there is nothing to
-- say. `verbose` keeps the leading name line as well.
function itemdetails.blocks(item, verbose)
if type(item) ~= "table" then
return nil
end
local blocks = {}
local ok, lines = pcall(function()
return item.getPopupMessage and item.getPopupMessage()
end)
if ok and type(lines) == "table" then
for index, line in ipairs(lines) do
local text = type(line) == "table" and line.text or (type(line) == "string" and line)
if type(text) == "string" then
text = flatten(text)
if text ~= "" and not (index == 1 and not verbose and isNameLine(text, item)) then
blocks[#blocks + 1] = text
end
end
end
elseif type(item.description) == "string" then
-- getPopupMessage missing or raising means the game changed shape. Degrade
-- to the raw description rather than going silent.
local text = flatten(item.description)
if text ~= "" then
blocks[#blocks + 1] = text
end
end
if #blocks == 0 then
return nil
end
return blocks
end
return itemdetails