Module:Dialogue

From Final Fantasy XIV Online Wiki
Jump to navigation Jump to search

This module implements {{Dialogue}}.


local p = {}

function p.formatBlock(frame)
    local lines = mw.text.split(frame.args[1] or "", "\n")
    local output = ""
    local lastSpeaker = nil
    local inContainer = false
    local inOptionResponse = false
    local inSpeakerBlock = false
    
    for _, line in ipairs(lines) do
        line = mw.text.trim(line)
        
        -- Skip empty lines
        if line == "" then
            -- Do nothing
            
        -- Check if this is an end-of-option delimiter
        elseif line == "---" or line == "–––" or line == "—" then
            -- Close any open containers
            if inContainer then
                output = output .. "</div>\n"
                inContainer = false
            end
            if inSpeakerBlock then
                output = output .. "</div>\n"
                inSpeakerBlock = false
            end
            
            -- Reset state
            lastSpeaker = nil
            inOptionResponse = false
            
        -- Check if this is a wikitext header (skip processing, pass through)
        elseif line:match("^==+.+==+$") then
            -- Close any open containers before the header
            if inContainer then
                output = output .. "</div>\n"
                inContainer = false
            end
            if inSpeakerBlock then
                output = output .. "</div>\n"
                inSpeakerBlock = false
            end
            
            -- Pass the header through as-is
            output = output .. line .. "\n"
            
            -- Reset state
            lastSpeaker = nil
            inOptionResponse = false
            
        -- Check if this is a dialogue option (starts with >)
        elseif line:match("^>%s*(.+)$") then
            local optionText = line:match("^>%s*(.+)$")
            
            -- Close any open speaker containers before showing options
            if inContainer then
                output = output .. "</div>\n"
                inContainer = false
            end
            if inSpeakerBlock then
                output = output .. "</div>\n"
                inSpeakerBlock = false
            end
            
            -- Add the dialogue option with visible >
            output = output .. "<div class='dialogue-option'>'''>''' " .. optionText .. "</div>\n"
            
            -- Reset last speaker and mark that we're now in option response mode
            lastSpeaker = nil
            inOptionResponse = true
            
        else
            -- Match "Speaker: text" format
            local speaker, text = line:match("^(.-):%s*(.+)$")
            
            if speaker and text then
                speaker = mw.text.trim(speaker)
                text = mw.text.trim(text)
                
                -- Remove bold markup (''') from speaker name
                speaker = speaker:gsub("'''", "")
                speaker = mw.text.trim(speaker)
                
                -- Check if we have a new speaker
                if speaker ~= lastSpeaker then
                    -- Close previous speaker's containers if they exist
                    if inContainer then
                        output = output .. "</div>\n"
                        inContainer = false
                    end
                    if inSpeakerBlock then
                        output = output .. "</div>\n"
                        inSpeakerBlock = false
                    end
                    
                    -- Determine if this should be indented (response to option)
                    local blockClass = inOptionResponse and "dialogue-speaker-block dialogue-speaker-block-indented" or "dialogue-speaker-block"
                    
                    -- Start new speaker block
                    output = output .. "<div class='" .. blockClass .. "'>\n"
                    inSpeakerBlock = true
                    
                    -- Add new speaker name
                    output = output .. "<div class='dialogue-speaker'>'''" .. speaker .. "'''</div>\n"
                    
                    -- Start new dialogue container
                    output = output .. "<div class='dialogue-container'>\n"
                    
                    lastSpeaker = speaker
                    inContainer = true
                    -- Reset option response flag after first response
                    inOptionResponse = false
                end
                
                -- Add the dialogue line
                output = output .. "<div class='dialogue-line'>" .. text .. "</div>\n"
            else
                -- Line without a speaker - close any open containers first
                if inContainer then
                    output = output .. "</div>\n"
                    inContainer = false
                end
                if inSpeakerBlock then
                    output = output .. "</div>\n"
                    inSpeakerBlock = false
                end
                
                -- Display as a standalone dialogue line
                output = output .. "<div class='dialogue-line dialogue-line-standalone'>" .. line .. "</div>\n"
                
                -- Reset state
                lastSpeaker = nil
                inOptionResponse = false
            end
        end
    end
    
    -- Close any open containers
    if inContainer then
        output = output .. "</div>\n"
    end
    if inSpeakerBlock then
        output = output .. "</div>\n"
    end
    
    return output
end

return p