-- 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