Module:Dialogue: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary Tag: Reverted |
||
| Line 242: | Line 242: | ||
-- Start optional collapsible container | -- Start optional collapsible container | ||
output = output .. "<div class='dialogue-optional-collapsible mw-collapsible mw-collapsed'>" | output = output .. "<div class='dialogue-optional-collapsible mw-collapsible mw-collapsed'>" | ||
output = output .. "<div class='dialogue-optional-header | output = output .. "<div class='dialogue-optional-header'>''Optional dialogue''</div>" | ||
output = output .. "<div class='mw-collapsible-content'>" | output = output .. "<div class='mw-collapsible-content'>" | ||
Revision as of 04:34, 4 January 2026
This module implements {{Dialogue}}.
local p = {}
-- Function to process Forename and Surname placeholders
local function processNames(text)
-- Process '''Forename''' and '''Surname'''
text = text:gsub("'''(Forename)'''", "<u>%1</u>")
text = text:gsub("'''(Surname)'''", "<u>%1</u>")
-- Process ''Forename'' and ''Surname''
text = text:gsub("''(Forename)''", "<u>%1</u>")
text = text:gsub("''(Surname)''", "<u>%1</u>")
-- Process [Forename] and [Surname]
text = text:gsub("%[(Forename)%]", "<u>%1</u>")
text = text:gsub("%[(Surname)%]", "<u>%1</u>")
-- Process "Forename" and "Surname"
text = text:gsub('"(Forename)"', "<u>%1</u>")
text = text:gsub('"(Surname)"', "<u>%1</u>")
-- Process bare Forename and Surname (must be capitalized)
-- Use word boundaries to avoid matching partial words
text = text:gsub("(%W)(Forename)(%W)", "%1<u>%2</u>%3")
text = text:gsub("(%W)(Surname)(%W)", "%1<u>%2</u>%3")
-- Handle Forename/Surname at start of line
text = text:gsub("^(Forename)(%W)", "<u>%1</u>%2")
text = text:gsub("^(Surname)(%W)", "<u>%1</u>%2")
-- Handle Forename/Surname at end of line
text = text:gsub("(%W)(Forename)$", "%1<u>%2</u>")
text = text:gsub("(%W)(Surname)$", "%1<u>%2</u>")
-- Handle Forename/Surname as entire line
if text == "Forename" then
text = "<u>Forename</u>"
elseif text == "Surname" then
text = "<u>Surname</u>"
end
return text
end
function p.formatBlock(frame)
local lines = mw.text.split(frame.args[1] or "", "\n")
local output = ""
local lastSpeaker = nil
local lastSpeakerLink = nil
local inContainer = false
local inOptionResponse = false
local inSpeakerBlock = false
local isOptional = false
local inOptionalSection = false
local optionResponseBuffer = ""
local currentOption = nil
local optionCounter = 0
for _, line in ipairs(lines) do
line = mw.text.trim(line)
-- Skip empty lines
if line == "" then
-- Do nothing
-- Check for cutscene start marker
elseif line == "-cutscene start" then
-- Flush any buffered option response first
if inOptionResponse and currentOption then
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
end
-- Close any open containers
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
-- Add cutscene start marker
output = output .. "<div class='dialogue-cutscene-marker'>"
output = output .. "[[File:Cutscene icon.png|24px|link=]] ''Start of cutscene.''"
output = output .. "</div>\n"
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
-- Check for cutscene end marker
elseif line == "-cutscene end" then
-- Flush any buffered option response first
if inOptionResponse and currentOption then
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
end
-- Close any open containers
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
-- Add cutscene end marker
output = output .. "<div class='dialogue-cutscene-marker'>''End of cutscene.''</div>\n"
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
-- Check for quest complete marker
elseif line == "-quest complete" then
-- Flush any buffered option response first
if inOptionResponse and currentOption then
-- Close any open containers in the buffer
if inContainer then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inSpeakerBlock = false
end
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
end
-- Close any open containers
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
-- Add quest complete image
output = output .. "<div class='dialogue-quest-complete'>"
output = output .. "[[File:Quest Complete.png|500px|link=]]"
output = output .. "</div>\n"
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
-- Check for optional start marker
elseif line == "-optional start" then
-- Flush any buffered option response first
if inOptionResponse and currentOption then
-- Close any open containers in the buffer
if inContainer then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inSpeakerBlock = false
end
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
end
-- Close any open containers
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
-- Start optional collapsible container
output = output .. "<div class='dialogue-optional-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-optional-header'>''Optional dialogue''</div>"
output = output .. "<div class='mw-collapsible-content'>"
inOptionalSection = true
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
-- Check for optional end marker
elseif line == "-optional end" then
-- Flush any buffered option response first
if inOptionResponse and currentOption then
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
end
-- Close any open containers
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
-- Close optional collapsible container
output = output .. "</div></div>\n"
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
inOptionalSection = false
isOptional = false
-- Check if this is an end-of-option delimiter
elseif line == "---" or line == "–––" or line == "—" then
-- Flush any buffered option response
if inOptionResponse and currentOption then
-- Close any open containers in the buffer
if inContainer then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inSpeakerBlock = false
end
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
else
-- Close any open containers in main output
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
end
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
-- Check if this is a wikitext header (skip processing, pass through)
elseif line:match("^==+.+==+$") then
-- Flush any buffered option response
if inOptionResponse and currentOption then
-- Close any open containers in the buffer
if inContainer then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inSpeakerBlock = false
end
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
currentOption = nil
inOptionResponse = false
else
-- Close any open containers in main output
if inContainer then
output = output .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
output = output .. "</div>\n"
inSpeakerBlock = false
end
end
-- Pass the header through as-is
output = output .. line .. "\n"
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
-- Check if this is a dialogue option (starts with >)
elseif line:match("^>%s*(.+)$") then
local optionText = line:match("^>%s*(.+)$")
-- Process names in option text
optionText = processNames(optionText)
-- Flush previous option response if exists
if inOptionResponse and currentOption then
-- Close any open containers in the buffer
if inContainer then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inContainer = false
end
if inSpeakerBlock then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
inSpeakerBlock = false
end
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
optionResponseBuffer = ""
end
-- Set current option and start buffering
currentOption = optionText
inOptionResponse = true
-- Reset last speaker
lastSpeaker = nil
lastSpeakerLink = nil
else
-- Match "Speaker: text" format, with optional link override
local speakerPart, text = line:match("^(.-):%s*(.+)$")
if speakerPart and text then
local speaker, linkOverride
-- Check if speaker has a link override (format: "Link@Speaker" or "x@Speaker")
if speakerPart:match("@") then
linkOverride, speaker = speakerPart:match("^(.-)@(.+)$")
linkOverride = mw.text.trim(linkOverride)
speaker = mw.text.trim(speaker)
else
speaker = mw.text.trim(speakerPart)
linkOverride = nil
end
-- Remove bold markup (''') from speaker name
speaker = speaker:gsub("'''", "")
speaker = mw.text.trim(speaker)
-- Process names in dialogue text
text = processNames(text)
-- Determine the link target
local speakerLink
if linkOverride == "x" then
speakerLink = nil -- No link
elseif linkOverride then
speakerLink = linkOverride -- Custom link
elseif speaker == "System" then
speakerLink = nil -- Don't link System
else
speakerLink = speaker -- Default: link to speaker name
end
-- Determine output target (direct output or buffer)
local targetOutput = inOptionResponse and function(str) optionResponseBuffer = optionResponseBuffer .. str end
or function(str) output = output .. str end
-- Check if we have a new speaker OR if we're entering an optional section
if speaker ~= lastSpeaker or (inOptionalSection and not isOptional) then
-- Close previous speaker's containers if they exist
if inContainer then
targetOutput("</div>\n")
inContainer = false
end
if inSpeakerBlock then
targetOutput("</div>\n")
inSpeakerBlock = false
end
-- Determine if this should be indented (response to option)
local blockClass = "dialogue-speaker-block"
if inOptionResponse then
blockClass = blockClass .. " dialogue-speaker-block-indented"
end
if inOptionalSection then
isOptional = true
end
-- Start new speaker block
targetOutput("<div class='" .. blockClass .. "'>\n")
inSpeakerBlock = true
-- Add speaker header
targetOutput("<div class='dialogue-speaker-header'>\n")
-- Create speaker name (linked or not)
if speakerLink then
targetOutput("<div class='dialogue-speaker'>'''[[" .. speakerLink .. "|" .. speaker .. "]]'''</div>\n")
else
targetOutput("<div class='dialogue-speaker'>'''" .. speaker .. "'''</div>\n")
end
targetOutput("</div>\n")
-- Start new dialogue container
targetOutput("<div class='dialogue-container'>\n")
lastSpeaker = speaker
lastSpeakerLink = speakerLink
inContainer = true
end
-- Add the dialogue line
targetOutput("<div class='dialogue-line'>" .. text .. "</div>\n")
else
-- Line without a speaker
local targetOutput = inOptionResponse and function(str) optionResponseBuffer = optionResponseBuffer .. str end
or function(str) output = output .. str end
-- Close any open containers first
if inContainer then
targetOutput("</div>\n")
inContainer = false
end
if inSpeakerBlock then
targetOutput("</div>\n")
inSpeakerBlock = false
end
-- Process names in standalone line
line = processNames(line)
-- Display as a standalone dialogue line
local lineClass = "dialogue-line dialogue-line-standalone"
if inOptionalSection then
lineClass = lineClass .. " dialogue-line-optional"
end
targetOutput("<div class='" .. lineClass .. "'>" .. line .. "</div>\n")
-- Reset state
lastSpeaker = nil
lastSpeakerLink = nil
end
end
end
-- Flush any remaining buffered option response
if inOptionResponse and currentOption then
-- Close any open containers in the buffer
if inContainer then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
end
if inSpeakerBlock then
optionResponseBuffer = optionResponseBuffer .. "</div>\n"
end
optionCounter = optionCounter + 1
-- Check if there's any content in the buffer
if optionResponseBuffer ~= "" then
-- Has content - make it collapsible
output = output .. "<div class='dialogue-option-collapsible mw-collapsible mw-collapsed'>"
output = output .. "<div class='dialogue-option mw-collapsible-toggle'>" .. currentOption .. "</div>"
output = output .. "<div class='mw-collapsible-content'>"
output = output .. optionResponseBuffer
output = output .. "</div></div>\n"
else
-- No content - just display the option
output = output .. "<div class='dialogue-option dialogue-option-no-response'>" .. currentOption .. " <span class='dialogue-no-response-label'>''No unique response''</span></div>\n"
end
else
-- Close any open containers in main output
if inContainer then
output = output .. "</div>\n"
end
if inSpeakerBlock then
output = output .. "</div>\n"
end
end
return output
end
return p