Files

101 lines
2.9 KiB
Lua
Raw Permalink Normal View History

-- states/ingame_dialogue.lua
--
-- Conversations. Two things must be spoken, and they change independently:
-- * the NPC's line (speaker name + message blocks) -- changes when the node does
-- * the currently selected response -- changes as you press up/down
--
-- So this tracks the node itself and announces the full block on a node change,
-- but only the option on a selection change. Repeating the whole speech every
-- time you move between responses would be unusable.
--
-- Responses can also be picked directly with the number keys 1-0, which the game
-- already supports and which is the fastest way to work through a conversation.
local descriptor = {}
local lastNodeKey
local function nodeKeyFor(speakerName, blocks, options)
local parts = {tostring(speakerName), tostring(#options)}
for _, block in ipairs(blocks or {}) do
parts[#parts + 1] = tostring(block.text)
end
return table.concat(parts, "|")
end
descriptor.read = function(stateTable, menus)
local keypressed = stateTable.keypressed
local options = menus.getUpvalue(keypressed, "options")
local selected = menus.getUpvalue(keypressed, "selectedOption")
if type(options) ~= "table" or type(selected) ~= "number" then
return nil, nil
end
-- speakerName / messageTextBlocks are not touched by keypressed directly --
-- it calls updateText(), so they are upvalues of that function.
local updateText = menus.getUpvalue(keypressed, "updateText")
local speakerName, blocks
if type(updateText) == "function" then
speakerName = menus.getUpvalue(updateText, "speakerName")
blocks = menus.getUpvalue(updateText, "messageTextBlocks")
end
local nodeKey = nodeKeyFor(speakerName, blocks, options)
local option = options[selected]
local optionText = option and option.text or nil
-- Selection moved within the same node: just the response.
if nodeKey == lastNodeKey then
if not optionText then
return nil, nil
end
return string.format("%s, %d of %d", optionText, selected, #options),
nodeKey .. ":" .. selected
end
-- New node: speaker, what they said, then the options.
lastNodeKey = nodeKey
local parts = {}
if speakerName and speakerName ~= "" then
parts[#parts + 1] = speakerName
end
for _, block in ipairs(blocks or {}) do
if block.text then
parts[#parts + 1] = block.text
end
end
if #options > 0 then
parts[#parts + 1] = #options .. (#options == 1 and " response" or " responses")
for i, entry in ipairs(options) do
if entry.text then
-- The game already prefixes response text with its number
-- ("1. (Continue)"), matching the number keys that select it.
-- Adding our own gave "1. 1. (Continue)".
if entry.text:match("^%s*%d+%s*[%.%)]") then
parts[#parts + 1] = entry.text
else
parts[#parts + 1] = i .. ". " .. entry.text
end
end
end
end
if #parts == 0 then
return nil, nil
end
return table.concat(parts, ". "), nodeKey .. ":" .. selected
end
return descriptor