90 lines
3.1 KiB
Lua
90 lines
3.1 KiB
Lua
-- 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
|