维护提醒

BWIKI 全站将于 9 月 3 日(全天)进行维护,期间无法编辑任何页面或发布新的评论。

全站通知:

模块:Talk

来自星露谷物语维基
跳到导航 跳到搜索
[ 创建 | 刷新 ]文档页面
当前模块文档缺失,需要扩充。
local Helper = require("Module:Helper")
local NPC = require("Module:NPC")
local ItemNames = require("Module:ItemNames")
local TalkData
local p = {}

-- 前向声明事件相关函数
local formatOtherDialogueKey
local formatDialogueText
local removeConsecutiveEmptyStrings
local getItemDisplayName

local squote = "Squote"

local function wrapInText(text)
    return '<div class="choice-text">' .. text .. '</div>'
end

local function wrapInReply(text)
    return '<div class="chat-reply">' .. text .. '</div>'
end

local function choice()
    return '<div class="chat-choice">'
end

local function div()
    return '</div>'
end

-- 对话清理函数
local function cleanText(text, isPlayerChoice)
    if not isPlayerChoice then
        isPlayerChoice = false
    end

    if not text or type(text) ~= "string" then
        return text
    end

    -- 处理${先生^小姐}格式的性别词汇,直接替换为"先生/小姐"
    text = text:gsub("%$%{([^%^}]+)%^([^}]+)%}", "%1/%2")

    text = text:gsub("%%revealtaste:[%a%s]*:%d+", '<span class="trigger-chance">(解锁人物喜好)</span>')

    text = text:gsub("@", '<span class="player-name">玩家名</span>')
    text = text:gsub("%%noturn", '<span class="trigger-chance">(不转身)</span>')
    text = text:gsub("%$[hbslaue]", "")
    text = text:gsub("%$%d+", "")
    text = text:gsub("##", "\n")
    text = text:gsub("%s*%[%s*(%d+)%s*%]", function(itemId)
        local displayName = getItemDisplayName(itemId)
        if not displayName then
            displayName = "物品"
        end
        return '<span class="trigger-chance">(给予 ' .. displayName .. ')</span>'
    end)
    if not text:match("class=\"quotetable\"") then
        if not isPlayerChoice then
            text = text:gsub('“', '‘')
            text = text:gsub('”', '’')
        end
    end
    text = text:gsub("*", "<span>*</span>")

    -- 处理物品给予标记(修复正则表达式)
    text = text:gsub("%[([%d%s]+)%]", '<span class="trigger-chance">(给予物品)</span>')

    -- 清理末尾的#字符
    text = text:gsub("#$", "")

    -- 处理玩家名称占位符
    text = text:gsub("@", '<span class="player-name">玩家名</span>')
    text = text:gsub("%%kid1", '<span class="player-name">第一个孩子</span>')
    text = text:gsub("%%kid2", '<span class="player-name">第二个孩子</span>')
    text = text:gsub("%%spouse", '<span class="player-name">配偶名</span>')
    text = text:gsub("%%farm", '<span class="player-name">农场名</span>')
    text = text:gsub("%%time", '<span class="player-name">当前时间</span>')
    text = text:gsub("%%noun", '<span class="player-name">随机名词</span>')
    text = text:gsub("%%firstnameletter%%name", '<span class="refuse-item">带有错误的玩家名</span>')
    text = text:gsub("%%season", '<span class="player-name">季节</span>')
    text = text:gsub("%%year", '<span class="player-name">年份</span>')
    text = text:gsub("%%fork", '<span class="trigger-chance">(分支切换)</span>')
    text = text:gsub("%%name", '<span class="trigger-chance">(随机名词)</span>')
    text = text:gsub("%%pet", '<span class="player-name">宠物名</span>')
    -- 占位符
    text = text:gsub("{0}", '<span class="trigger-chance">[  ]</span>')
    text = text:gsub("{1}", '<span class="trigger-chance">[  ]</span>')
    text = text:gsub("{2}", '<span class="trigger-chance">[  ]</span>')
    text = text:gsub("{3}", '<span class="trigger-chance">[  ]</span>')
    text = text:gsub("{4}", '<span class="trigger-chance">[  ]</span>')

    text = text:gsub("^%%", '<span class="refuse-item">(不回应)</span>')
    text = text:gsub("\n%%", '\n<span class="refuse-item">(不回应)</span>')

    return text
end

local function createPlayerChoice(text, friendship, character)
    if character then
        return Helper.ExpandTemplate("PlayerChoice", {"> " .. text, friendship, character})
    else
        return Helper.ExpandTemplate("PlayerChoice", {"> " .. text, friendship})
    end
end

-- 创建引用模板的辅助函数
local function createQuote(text, template)
    if not template then
        template = squote
    end
    -- 按\n分割对话
    local parts = {}
    for part in text:gmatch("[^\n]+") do
        local trimmed = part:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            table.insert(parts, trimmed)
        end
    end

    -- 如果只有一句话,直接返回
    if #parts <= 1 then
        return Helper.ExpandTemplate(template, {text})
    end

    -- 如果只有两句话,用\n直接隔开
    if #parts == 2 then
        local result = parts[1] .. "\n" .. parts[2]
        return Helper.ExpandTemplate(template, {result})
    end

    -- 三句或以上:第一句和最后一句不包裹,中间的句子用<p></p>包裹
    local result = parts[1]
    for i = 2, #parts - 1 do
        result = result .. "<p>" .. parts[i] .. "</p>"
    end
    result = result .. parts[#parts]

    return Helper.ExpandTemplate(template, {result})
end

local function getCharacterName(name)
    return NPC.getChineseName(name)
end

-- 全局NPC数据变量(用于$q对话处理)
local globalNpcData = nil

getItemDisplayName = function(itemId)
    if itemId == "(O)126" or itemId == "126" then
        return Helper.ExpandTemplate("Name", {
            "Strange Doll (green)",
            class = "inline"
        })
    elseif itemId == "(O)127" or itemId == "127" then
        return Helper.ExpandTemplate("Name", {
            "Strange Doll (yellow)",
            class = "inline"
        })
    end

    local chineseName = ItemNames.getEnglishName("(O)" .. itemId) or ItemNames.getEnglishName(itemId)
    if chineseName and chineseName ~= "" then
        return Helper.ExpandTemplate("Name", {
            chineseName,
            class = "inline"
        })
    end

    return nil
end

-- 全局已处理对话键变量(用于避免重复显示)
local globalProcessedKeys = nil

-- 设置全局NPC数据的辅助函数
local function setGlobalNpcData(npcData)
    globalNpcData = npcData
    globalProcessedKeys = {} -- 重置已处理键列表
end

-- 处理概率对话
local function processProbabilityText(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 先移除开头可能存在的#字符
    local cleanedText = text:gsub("^#*", "")

    local probability, text1, text2 = cleanedText:match("%$c%s+([%d%.]+)#(.-)#(.*)")

    if probability and text1 and text2 then
        local mainPercent = tonumber(probability)
        local results = {}
        
        -- 处理第一句(直接显示)
        text1 = text1:gsub("#%$e#", "\n"):gsub("%$[hse]", "")
        text1 = cleanText(text1)
        local percent1 = math.floor(mainPercent * 100 + 0.5) -- 四舍五入
        if percent1 > 100 then percent1 = 100 end
        if percent1 < 1 then percent1 = 1 end
        
        local option1 = createQuote(text1 .. '<span class="trigger-chance">(' .. percent1 .. '% 的概率触发)</span>')
        table.insert(results, option1)
        
        -- 检查第二句是否包含嵌套的概率判断
        if text2:match("%$c%s+") then
            -- 处理嵌套概率
            local nestedProb, nestedText1, nestedText2 = text2:match("%$c%s+([%d%.]+)#(.-)#(.*)")
            
            if nestedProb and nestedText1 and nestedText2 then
                local nestedPercent = tonumber(nestedProb)
                local remainingPercent = 1.0 - mainPercent -- 剩余概率
                
                -- 计算嵌套文本的概率
                local nested1Percent = math.floor(remainingPercent * nestedPercent * 100 + 0.5) -- 四舍五入
                local nested2Percent = 100 - percent1 - nested1Percent
                
                -- 确保概率合理
                if nested1Percent > 100 then nested1Percent = 100 end
                if nested1Percent < 1 then nested1Percent = 1 end
                if nested2Percent > 100 then nested2Percent = 100 end
                if nested2Percent < 1 then nested2Percent = 1 end
                
                -- 处理嵌套的第一句
                nestedText1 = nestedText1:gsub("#%$e#", "\n"):gsub("%$[hse]", "")
                nestedText1 = cleanText(nestedText1)
                local nestedOption1 = createQuote(nestedText1 .. '<span class="trigger-chance">(' .. nested1Percent .. '% 的概率触发)</span>')
                table.insert(results, nestedOption1)
                
                -- 处理嵌套的第二句
                nestedText2 = nestedText2:gsub("^%$e#", ""):gsub("#%$e#", "\n"):gsub("%$[hse]", "")
                nestedText2 = cleanText(nestedText2)
                local nestedOption2 = createQuote(nestedText2 .. '<span class="trigger-chance">(' .. nested2Percent .. '% 的概率触发)</span>')
                table.insert(results, nestedOption2)
                
                return table.concat(results, "\n")
            end
        end
        
        -- 如果没有嵌套概率,按原逻辑处理第二句
        text2 = text2:gsub("^%$e#", ""):gsub("#%$e#", "\n"):gsub("%$[hse]", "")
        text2 = cleanText(text2)
        local percent2 = 100 - percent1
        if percent2 < 1 then percent2 = 1 end
        
        local option2 = createQuote(text2 .. '<span class="trigger-chance">(' .. percent2 .. '% 的概率触发)</span>')
        table.insert(results, option2)
        
        return table.concat(results, "\n")
    end

    return text
end

-- 处理性别分隔的对话(处理"^"分隔符)
local function processGenderDialogue(text, template, characterName)
    -- mw.log(text)
    if not template then
        template = squote
    end

    if not text or type(text) ~= "string" then
        return text
    end

    text = text:gsub("%$%{([^%^}]+)%^([^}]+)%}%$", "%1/%2")

    -- 先处理分行符号,但保留其他游戏标记用于后续cleanText处理
    local cleanedText = text

    -- 替换#$b#为换行
    cleanedText = string.gsub(cleanedText, "#%$b#", "\n")

    -- 清理$l
    cleanedText = string.gsub(cleanedText, "%$l", "")

    -- 替换#$e#为换行(表情符号后换行)
    cleanedText = string.gsub(cleanedText, "#%$e#", "\n")

    if not text:find("%^") then
        return createQuote(cleanText(cleanedText), template)
    end

    -- 现在检查处理后的文本是否有^
    if cleanedText:find("%^") then
        -- 按行分割文本
        local lines = {}
        for line in cleanedText:gmatch("[^\r\n]+") do
            -- 清理每行的前后空白
            local trimmedLine = line:match("^%s*(.-)%s*$")
            if trimmedLine and trimmedLine ~= "" then
                table.insert(lines, trimmedLine)
            end
        end

        -- 找到第一个包含^的行的位置
        local firstGenderLineIndex = nil
        for i, line in ipairs(lines) do
            if line:find("%^") then
                firstGenderLineIndex = i
                break
            end
        end

        if firstGenderLineIndex then
            -- 收集共同前缀行(第一个^之前的所有行)
            local commonPrefix = {}
            for i = 1, firstGenderLineIndex - 1 do
                table.insert(commonPrefix, lines[i])
            end

            -- 处理所有包含^的行
            local maleSpecificLines = {}
            local femaleSpecificLines = {}

            for i = firstGenderLineIndex, #lines do
                local line = lines[i]
                if line:find("%^") then
                    -- 这行包含性别分隔符
                    local caretPos = line:find("%^")
                    local beforeCaret = line:sub(1, caretPos - 1)
                    local afterCaret = line:sub(caretPos + 1)

                    -- 检查是否包含角色名字(格式:'''角色名:'''或普通格式:角色名:)
                    if not characterName then
                        characterName = beforeCaret:match("^%s*('''[^']+:''')")
                        if not characterName then
                            -- 尝试匹配普通格式的角色名字
                            characterName = beforeCaret:match("^%s*([^:]+:)")
                            if characterName then
                                characterName = beforeCaret:sub(#characterName + 1)
                            end
                        end
                    end
                    -- 调试信息
                    -- mw.log("处理行: " .. line)
                    -- mw.log("beforeCaret: " .. beforeCaret)
                    -- mw.log("afterCaret: " .. afterCaret)
                    -- mw.log("characterName: " .. (characterName or "nil"))

                    if characterName then
                        -- 提取角色名字后的内容
                        local maleContent = beforeCaret:match("^%s*(.-)%s*$")
                        local femaleContent = afterCaret:match("^%s*(.-)%s*$")

                        -- 构建完整的男性和女性对话行(都包含角色名字)
                        local maleLine = (maleContent or "") -- 名字都移到最后面
                        local femaleLine = (femaleContent or "")

                        -- 检查是否为空内容(对于三个单引号格式和普通格式都适用)
                        local isEmptyMale = maleLine:match("^'''[^']+:'''%s*$") or maleLine:match("^[^:]+:%s*$")
                        local isEmptyFemale = femaleLine:match("^'''[^']+:'''%s*$") or
                                                  femaleLine:match("^[^:]+:%s*$")

                        if not isEmptyMale then
                            table.insert(maleSpecificLines, maleLine)
                        end

                        if not isEmptyFemale then
                            table.insert(femaleSpecificLines, femaleLine)
                        end
                    else
                        -- 普通的性别分隔行处理
                        local malePart = beforeCaret:match("^%s*(.-)%s*$")
                        local femalePart = afterCaret:match("^%s*(.-)%s*$")

                        -- 添加到对应的性别数组中
                        if malePart and malePart ~= "" then
                            table.insert(maleSpecificLines, malePart)
                        end
                        if femalePart and femalePart ~= "" then
                            table.insert(femaleSpecificLines, femalePart)
                        end
                    end
                else
                    -- 性别行之后的共同行,添加到两边
                    table.insert(maleSpecificLines, line)
                    table.insert(femaleSpecificLines, line)
                end
            end

            -- 构建完整的男性和女性对话
            local maleLines = {}
            local femaleLines = {}

            -- 添加共同前缀
            for _, line in ipairs(commonPrefix) do
                table.insert(maleLines, line)
                table.insert(femaleLines, line)
            end

            -- 添加性别特定内容
            for _, line in ipairs(maleSpecificLines) do
                table.insert(maleLines, line)
            end
            for _, line in ipairs(femaleSpecificLines) do
                table.insert(femaleLines, line)
            end

            -- 合并为完整文本
            local maleText = table.concat(maleLines, "\n")
            local femaleText = table.concat(femaleLines, "\n")
            if characterName then
                maleText = characterName .. ':' .. maleText
                femaleText = characterName .. ':' .. femaleText
            end

            -- 清理文本中的游戏标记
            maleText = cleanText(maleText)
            femaleText = cleanText(femaleText)

            local maleQuote = createQuote(maleText .. '<span class="trigger-chance">(男)</span>', template)
            local femaleQuote = createQuote(femaleText .. '<span class="trigger-chance">(女)</span>', template)

            return maleQuote .. "\n" .. femaleQuote
        end
    end

    return text
end

-- 处理斜杠分隔的对话
local function processSlashDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    if text:find("/") then
        local parts = {}
        for part in text:gmatch("[^/]+") do
            table.insert(parts, part:match("^%s*(.-)%s*$"))
        end

        if #parts == 2 and parts[1] == parts[2] then
            return parts[1]
        else
            if #parts == 2 then
                local male = cleanText(parts[1])
                local female = cleanText(parts[2])
                local maleQuote = createQuote(male .. '<span class="trigger-chance">(男)</span>')
                local femaleQuote = createQuote(female .. '<span class="trigger-chance">(女)</span>')
                return maleQuote .. "\n" .. femaleQuote
            else
                return table.concat(parts, "/")
            end
        end
    end

    return text
end

-- 处理多斜杠分隔的对话(普通文本分隔,不涉及性别)
local function processMultipleSlashDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    if text:find("/") then
        local parts = {}
        for part in text:gmatch("[^/]+") do
            local trimmed = part:match("^%s*(.-)%s*$")
            if trimmed and trimmed ~= "" then
                table.insert(parts, trimmed)
            end
        end

        -- 对于多个部分,创建分隔的引用
        if #parts > 1 then
            local quotes = {}
            for i, part in ipairs(parts) do
                -- 清理文本
                local cleanPart = cleanText(part)
                local quote = createQuote(cleanPart)
                table.insert(quotes, quote)
            end
            return table.concat(quotes, "\n")
        end
    end

    return text
end

-- 处理$d条件对话
local function processConditionalDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否以$d开头
    if not text:match("^%$d%s*") then
        return text
    end

    -- 定义条件状态映射
    local conditionMap = {
        ["bus"] = {"巴士修复后", "巴士修复前"},
        ["joja"] = {"超市未停业", "超市已停业"},
        ["cc"] = {"社区中心已重建", "社区中心未重建"},
        ["communitycenter"] = {"社区中心已重建", "社区中心未重建"},
        ["kent"] = {"肯特回到星露谷后", "肯特回到星露谷前"}
    }

    -- 移除$d前缀,获取剩余部分
    local remaining = text:match("^%$d%s*(.+)$")
    if not remaining then
        return text
    end

    -- 查找#分隔符
    local hashPos = remaining:find("#")
    if not hashPos then
        return text
    end

    -- 提取条件名称
    local condition = remaining:sub(1, hashPos - 1):match("^%s*(.-)%s*$")

    -- 获取条件后的文本
    local afterHash = remaining:sub(hashPos + 1)

    -- 查找|分隔符
    local pipePos = afterHash:find("|")
    if not pipePos then
        return text
    end

    -- 提取两个文本部分
    local text1 = afterHash:sub(1, pipePos - 1):match("^%s*(.-)%s*$")
    local text2 = afterHash:sub(pipePos + 1):match("^%s*(.-)%s*$")

    -- 处理#$e#为换行符,然后清理其他特殊字符
    text1 = text1:gsub("#%$e#", "\n"):gsub("%$[hse]", "")
    text2 = text2:gsub("#%$e#", "\n"):gsub("%$[hse]", "")

    -- 获取条件说明
    local conditionLabels = conditionMap[condition:lower()]
    if not conditionLabels then
        -- 如果没有预定义的条件,使用默认标签
        conditionLabels = {"条件满足", "条件不满足"}
    end

    -- 清理文本并格式化两个对话
    text1 = cleanText(text1)
    text2 = cleanText(text2)

    local quote1 = createQuote(text1 .. '<span class="trigger-chance">(' .. conditionLabels[1] .. ')</span>')
    local quote2 = createQuote(text2 .. '<span class="trigger-chance">(' .. conditionLabels[2] .. ')</span>')

    -- 根据条件类型调整输出顺序:未然在前,已然在后
    -- bus、cc、communitycenter、kent:第一个是已然,第二个是未然
    -- joja:第一个是未然,第二个是已然
    if condition:lower() == "joja" then
        -- joja:未然在前,已然在后(保持原顺序)
        return quote1 .. "\n" .. quote2
    else
        -- bus、cc、communitycenter、kent:未然在前,已然在后(交换顺序)
        return quote2 .. "\n" .. quote1
    end
end

-- 处理#$1一次性对话
local function processOnceOnlyDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否以#$1开头
    if not text:match("^#%$1%s*") then
        return text
    end

    -- 移除#$1前缀,获取剩余部分
    local remaining = text:match("^#%$1%s*(.+)$")
    if not remaining then
        return text
    end

    -- 查找第一个#分隔符(letter ID后面的)
    local firstHashPos = remaining:find("#")
    if not firstHashPos then
        return text
    end

    -- 提取letter ID(虽然我们不需要显示它)
    local letterID = remaining:sub(1, firstHashPos - 1):match("^%s*(.-)%s*$")

    -- 获取第一个#后的内容
    local afterFirstHash = remaining:sub(firstHashPos + 1)

    -- 查找#$e#分隔符
    local separatorPos = afterFirstHash:find("#%$e#")
    if not separatorPos then
        return text
    end

    -- 提取两个文本部分
    local firstTimeText = afterFirstHash:sub(1, separatorPos - 1):match("^%s*(.-)%s*$")
    local nthTimeText = afterFirstHash:sub(separatorPos + 4):match("^%s*(.-)%s*$") -- +4 to skip "#$e#"

    -- 清理文本内容,移除末尾的特殊字符如$h、$s、$e、$k等
    firstTimeText = firstTimeText:gsub("%$[hsek].*$", "")
    nthTimeText = nthTimeText:gsub("%$[hsek].*$", "")

    -- 进一步清理文本
    firstTimeText = cleanText(firstTimeText)
    nthTimeText = cleanText(nthTimeText)

    -- 格式化两个对话
    local quote1 = createQuote(firstTimeText .. '<span class="trigger-chance">(该对话仅能触发一次)</span>')
    local quote2 = createQuote(nthTimeText ..
                                   '<span class="trigger-chance">(触发过上述对话后,后续如果再次触发,则触发这句)</span>')

    return quote1 .. "\n" .. quote2
end

-- 处理||分隔的多对话轮换
local function processMultipleDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否包含||分隔符
    if not text:find("||") then
        return text
    end

    -- 按||分割对话
    local parts = {}
    local start = 1
    while true do
        local pos = text:find("||", start)
        if not pos then
            -- 添加最后一部分
            local lastPart = text:sub(start):match("^%s*(.-)%s*$")
            if lastPart and lastPart ~= "" then
                table.insert(parts, lastPart)
            end
            break
        else
            -- 添加当前部分
            local part = text:sub(start, pos - 1):match("^%s*(.-)%s*$")
            if part and part ~= "" then
                table.insert(parts, part)
            end
            start = pos + 2 -- 跳过||
        end
    end

    -- 如果没有正确分割,返回原文本
    if #parts <= 1 then
        return text
    end

    -- 定义季节标签
    local seasonLabels = {}
    if #parts == 2 then
        seasonLabels = {"春季、秋季和冬季", "夏季"}
    elseif #parts == 3 then
        seasonLabels = {"春季和冬季", "夏季", "秋季"}
    elseif #parts == 4 then
        seasonLabels = {"春季", "夏季", "秋季", "冬季"}
    else
        -- 对于超过4个的情况,使用通用标签
        for i = 1, #parts do
            table.insert(seasonLabels, "第" .. i .. "个对话")
        end
    end

    -- 格式化每个对话部分
    local quotes = {}
    for i, part in ipairs(parts) do
        -- 先处理#$e#转换为换行
        local cleanPart = part:gsub("#%$e#", "\n")

        -- 然后清理末尾的特殊字符
        cleanPart = cleanPart:gsub("%$[uhse].*$", "")

        -- 清理其他特殊字符
        cleanPart = cleanPart:gsub("%$u$", ""):gsub("%$u ", " ")

        local label = seasonLabels[i] or ("第" .. i .. "个对话")
        cleanPart = cleanText(cleanPart)
        local quote = createQuote(cleanPart .. '<span class="trigger-chance">(' .. label .. ')</span>')
        table.insert(quotes, quote)
    end

    return table.concat(quotes, "\n")
end

-- 处理$y快速问答对话
local function processQuickResponseDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否包含$y命令
    if not text:match("%$y%s+") then
        return text
    end

    local result = {}
    local currentPos = 1

    while true do
        -- 查找下一个$y命令
        local yPos = text:find("%$y%s+", currentPos)
        if not yPos then
            -- 没有更多$y命令,添加剩余文本
            local remaining = text:sub(currentPos)
            if remaining and remaining ~= "" then
                remaining = cleanText(remaining)
                table.insert(result, createQuote(remaining))
            end
            break
        end

        -- 添加$y之前的文本
        local beforeY = text:sub(currentPos, yPos - 1)
        if beforeY and beforeY ~= "" and beforeY ~= "#" then
            beforeY = cleanText(beforeY)
            table.insert(result, createQuote(beforeY))
        end

        -- 解析$y命令:$y 'question_answer1_reply1_answer2_reply2...'
        local afterY = text:sub(yPos)
        local yStart, yEnd, yContentOnly = afterY:find("%$y%s+'([^']+)'")

        if yContentOnly and yStart and yEnd then
            -- 分割问答对
            local parts = {}
            for part in yContentOnly:gmatch("[^_]+") do
                table.insert(parts, part)
            end

            if #parts >= 3 then
                local question = parts[1]

                -- 处理问题
                question = cleanText(question)
                table.insert(result, createQuote(question))

                -- 处理问答对
                table.insert(result, choice())
                for i = 2, #parts, 2 do
                    local answer = parts[i]
                    local reply = parts[i + 1]

                    if answer and reply then
                        answer = cleanText(answer, true)
                        reply = cleanText(reply)

                        -- 格式化选择和回应
                        local choiceTemplate = createPlayerChoice(answer, "0")
                        table.insert(result, wrapInText(choiceTemplate))

                        local replyQuote = reply -- createQuote(reply, "AfterChoice")
                        table.insert(result, wrapInReply(replyQuote))
                    end
                end
                table.insert(result, div())
            end

            -- 移动到下一个位置:使用精确的结束位置
            currentPos = yPos + yEnd
        else
            -- 解析失败,跳过这个$y命令
            currentPos = yPos + 3
        end
    end

    return table.concat(result, "\n")
end

-- 处理$query条件对话
local function processQueryDialogue(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否包含$query命令
    if not text:match("%$query%s+") then
        return text
    end

    local result = {}
    local currentPos = 1

    while true do
        -- 查找下一个$query命令
        local queryPos = text:find("%$query%s+", currentPos)
        if not queryPos then
            -- 没有更多$query命令,添加剩余文本
            local remaining = text:sub(currentPos)
            if remaining and remaining ~= "" then
                remaining = cleanText(remaining)
                table.insert(result, createQuote(remaining))
            end
            break
        end

        -- 添加$query之前的文本
        local beforeQuery = text:sub(currentPos, queryPos - 1)
        if beforeQuery and beforeQuery ~= "" and beforeQuery ~= "#" then
            beforeQuery = cleanText(beforeQuery)
            table.insert(result, createQuote(beforeQuery))
        end

        -- 解析$query命令
        local afterQuery = text:sub(queryPos)
        local queryType, trueText, falseText = afterQuery:match("%$query%s+([^#]+)#([^|]*)|?(.*)")

        if queryType and trueText then
            -- 处理查询类型并生成条件标签
            local conditionLabel = ""

            if queryType:match("PLAYER_NPC_RELATIONSHIP current any married roommate") then
                conditionLabel = "玩家已婚或已有室友"
                local falseLabel = "玩家未婚或没有室友"

                local trueQuote
                local falseQuote

                -- 处理true条件文本
                if trueText and trueText ~= "" then
                    trueText = cleanText(trueText)
                    trueQuote = createQuote(trueText .. '<span class="trigger-chance">(' .. conditionLabel ..
                                                ')</span>')
                end

                -- 处理false条件文本
                if falseText and falseText ~= "" then
                    -- 查找下一个$query命令的位置来确定当前$query命令的结束位置
                    local nextQueryPos = falseText:find("%$query%s+")
                    local currentFalseText
                    if nextQueryPos then
                        currentFalseText = falseText:sub(1, nextQueryPos - 1)
                        currentPos = queryPos + #queryType + #trueText + nextQueryPos + 1
                    else
                        currentFalseText = falseText
                        currentPos = #text + 1
                    end

                    currentFalseText = cleanText(currentFalseText)
                    falseQuote = createQuote(currentFalseText .. '<span class="trigger-chance">(' .. falseLabel ..
                                                 ')</span>')
                else
                    currentPos = queryPos + #queryType + #trueText + 20
                end
                if falseQuote then
                    table.insert(result, falseQuote)
                end
                if falseQuote then
                    table.insert(result, trueQuote)
                end
            elseif queryType:match("PLAYER_NPC_RELATIONSHIP any (%w+) married roommate") then
                local npcName = queryType:match("PLAYER_NPC_RELATIONSHIP any (%w+) married roommate")
                conditionLabel = "玩家已与" .. (npcName == "Shane" and "谢恩" or npcName) .. "结婚"
                local falseLabel = "玩家没有和" .. (npcName == "Shane" and "谢恩" or npcName) .. "结婚"

                local falseQuote
                local trueQuote

                -- 处理true条件文本
                if trueText and trueText ~= "" then
                    trueText = cleanText(trueText)
                    trueQuote = createQuote(trueText .. '<span class="trigger-chance">(' .. conditionLabel ..
                                                ')</span>')
                end

                -- 处理false条件文本
                if falseText and falseText ~= "" then
                    -- 查找下一个$query命令的位置来确定当前$query命令的结束位置
                    local nextQueryPos = falseText:find("%$query%s+")
                    local currentFalseText
                    if nextQueryPos then
                        currentFalseText = falseText:sub(1, nextQueryPos - 1)
                        currentPos = queryPos + #queryType + #trueText + nextQueryPos + 1
                    else
                        currentFalseText = falseText
                        currentPos = #text + 1
                    end

                    currentFalseText = cleanText(currentFalseText)
                    falseQuote = createQuote(currentFalseText .. '<span class="trigger-chance">(' .. falseLabel ..
                                                 ')</span>')
                else
                    currentPos = queryPos + #queryType + #trueText + 20
                end
                if falseQuote then
                    table.insert(result, falseQuote)
                end
                if falseQuote then
                    table.insert(result, trueQuote)
                end
            else
                -- 未知的查询类型,跳过
                currentPos = queryPos + 7 -- 跳过$query
            end
        else
            -- 解析失败,跳过这个$query命令
            currentPos = queryPos + 7
        end
    end

    return table.concat(result, "\n")
end

-- 处理$p条件对话
local function processPrerequisiteDialogue(text, responseMapping)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否包含$p命令
    if not text:match("%$p%s+") then
        return text
    end

    local result = {}
    local currentPos = 1

    while true do
        -- 查找下一个$p命令
        local pPos = text:find("%$p%s+", currentPos)
        if not pPos then
            -- 没有更多$p命令,添加剩余文本
            local remaining = text:sub(currentPos)
            if remaining and remaining ~= "" then
                remaining = cleanText(remaining)
                table.insert(result, createQuote(remaining))
            end
            break
        end

        -- 添加$p之前的文本
        local beforeP = text:sub(currentPos, pPos - 1)
        if beforeP and beforeP ~= "" and beforeP ~= "#" then
            beforeP = cleanText(beforeP)
            table.insert(result, createQuote(beforeP))
        end

        -- 解析$p命令
        local afterP = text:sub(pPos)
        local responseIds, matchText, noMatchText = afterP:match("%$p%s+([^#]+)#([^|]*)|?(.*)")

        if responseIds and matchText then
            -- 检查是否为简单分支(没有responseMapping)
            if not responseMapping then
                -- 简单分支处理:直接按|分割为多个分支
                local branches = {}
                table.insert(branches, matchText)
                if noMatchText and noMatchText ~= "" then
                    for branch in noMatchText:gmatch("[^|]+") do
                        table.insert(branches, branch)
                    end
                end

                -- 输出每个分支
                for i, branch in ipairs(branches) do
                    branch = cleanText(branch)
                    local branchLabel = '<span class="trigger-chance">(分支' ..
                                            ({"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"})[i] ..
                                            ')</span>'
                    table.insert(result, createQuote(branch .. branchLabel))
                end

                -- 跳过这个$p命令:直接跳到文本末尾,避免重复处理
                currentPos = #text + 1
            else
                -- 处理多个response ID(用|分隔)
                local ids = {}
                for id in responseIds:gmatch("[^|]+") do
                    local trimmedId = id:match("^%s*(.-)%s*$")
                    if trimmedId and trimmedId ~= "" then
                        table.insert(ids, trimmedId)
                    end
                end

                -- 生成条件标签:找到所有映射到这些response ID的选项
                local conditionLabels = {}
                local usedOptions = {} -- 避免重复添加相同的选项

                -- 遍历所有response ID
                for _, id in ipairs(ids) do
                    -- 查找映射到当前ID的所有选项
                    if responseMapping and responseMapping[id] then
                        for _, optionNumber in ipairs(responseMapping[id]) do
                            if not usedOptions[optionNumber] then
                                local optionText = optionNumber == 1 and "一" or optionNumber == 2 and "二" or
                                                       optionNumber == 3 and "三" or optionNumber == 4 and "四" or
                                                       optionNumber == 5 and "五" or tostring(optionNumber)
                                table.insert(conditionLabels, "选项" .. optionText)
                                usedOptions[optionNumber] = true
                            end
                        end
                    end
                end

                -- 如果没有找到映射,使用原始ID
                if #conditionLabels == 0 then
                    for _, id in ipairs(ids) do
                        table.insert(conditionLabels, "选项" .. id)
                    end
                end

                -- 格式化条件标签:超过三项时,最后一项使用"或"连接,其他几项用"、"
                local conditionLabel
                if #conditionLabels == 1 then
                    conditionLabel = conditionLabels[1]
                elseif #conditionLabels == 2 then
                    conditionLabel = conditionLabels[1] .. "或" .. conditionLabels[2]
                else
                    local lastLabel = table.remove(conditionLabels)
                    conditionLabel = table.concat(conditionLabels, "、") .. "或" .. lastLabel
                end

                -- 处理匹配文本
                if matchText and matchText ~= "" and matchText ~= "#" then
                    matchText = cleanText(matchText)
                    local matchQuote = createQuote(matchText .. '<span class="trigger-chance">(选择过' ..
                                                       conditionLabel .. ')</span>')
                    table.insert(result, matchQuote)
                end

                -- 处理不匹配文本
                if noMatchText and noMatchText ~= "" then
                    -- 查找下一个$p命令的位置来确定当前$p命令的结束位置
                    local nextPPos = noMatchText:find("%$p%s+")
                    local currentNoMatchText
                    if nextPPos then
                        currentNoMatchText = noMatchText:sub(1, nextPPos - 1)
                        currentPos = pPos + #responseIds + #matchText + (matchText and #matchText or 0) + nextPPos + 1
                    else
                        currentNoMatchText = noMatchText
                        currentPos = #text + 1
                    end

                    currentNoMatchText = cleanText(currentNoMatchText)
                    local noMatchQuote = createQuote(currentNoMatchText ..
                                                         '<span class="trigger-chance">(选择过其它选项时)</span>')
                    table.insert(result, noMatchQuote)
                else
                    -- 移动到下一个位置
                    currentPos = pPos + #responseIds + #matchText + 10 -- 大概的偏移量
                end
            end
        else
            -- 解析失败,跳过这个$p命令
            currentPos = pPos + 3
        end
    end

    return table.concat(result, "\n")
end

local function processDialogueWithChoices(text)
    if true then
        return text
    end
    -- Check if the text contains both required patterns
    if not ((text:find("<dd><dl>") or text:find("<dl><dd>") or text:find("choice")) and
        (text:find("cquotetext") or text:find("zquotetext"))) then
        return text
    end
    -- mw.log(text)
    local result = {}
    local lines = {}

    -- Split text by newlines
    for line in text:gmatch("[^\n]+") do
        table.insert(lines, line)
    end

    local i = 1
    local currentPart = {}

    -- Process lines before the first <dd><dl>
    while i <= #lines do
        if lines[i]:find("<dd><dl>") then
            break
        end
        table.insert(currentPart, lines[i])
        i = i + 1
    end

    -- Process the first part with createQuote if it's not empty
    if #currentPart > 0 then
        local firstPartText = table.concat(currentPart, "\n")
        firstPartText = cleanText(firstPartText)
        table.insert(result, createQuote(firstPartText))
        currentPart = {}
    end

    -- Process the choice section - keep as is without createQuote
    if i <= #lines then
        -- Found <dd><dl>, start collecting until </dl></dd>
        table.insert(currentPart, lines[i]) -- Add the <dd><dl> line
        i = i + 1

        while i <= #lines do
            table.insert(currentPart, lines[i])
            if lines[i]:find("</dl></dd>") then
                i = i + 1
                break
            end
            i = i + 1
        end

        -- Add the choice section as is without processing
        table.insert(result, table.concat(currentPart, "\n"))
        currentPart = {}
    end

    -- Process any remaining content after </dl></dd> with createQuote
    while i <= #lines do
        table.insert(currentPart, lines[i])
        i = i + 1
    end

    if #currentPart > 0 then
        local lastPartText = table.concat(currentPart, "\n")
        lastPartText = cleanText(lastPartText)
        table.insert(result, createQuote(lastPartText))
    end
    -- mw.log(table.concat(result, "\n"))
    return table.concat(result, "\n")
end

-- 处理$q问答对话
local function processQuestionDialogue(text, npcData)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否包含$q命令
    if not text:match("%$q%s+") then
        return text
    end

    local result = {}
    local responseMapping = {} -- 用于映射response ID到选项序号列表
    local optionCounter = 1 -- 选项计数器

    -- 查找$q命令的位置
    local qPos = text:find("%$q%s+")
    local beforeQ = text:sub(1, qPos - 1)
    local afterQ = text:sub(qPos)

    -- 处理$q之前的内容(如果有)
    if beforeQ and beforeQ ~= "" then
        -- 移除末尾可能的#$e#分隔符和单独的#
        beforeQ = beforeQ:gsub("#%$e#$", "")
        beforeQ = beforeQ:gsub("#$", "")

        -- 处理性别分隔的对话
        if beforeQ:find("%^") then
            local processedBefore = processGenderDialogue(beforeQ)
            table.insert(result, processedBefore)
        else
            -- 清理并添加
            beforeQ = cleanText(beforeQ)
            table.insert(result, createQuote(beforeQ))
        end
    end

    -- 解析$q命令
    -- 格式:$q <response IDs> <fallback>#<text>
    local responseIds, fallback, remainingText = afterQ:match("%$q%s+([^%s]+)%s+([^#]+)#(.+)")
    if not responseIds or not fallback or not remainingText then
        return text -- 如果解析失败,返回原文本
    end

    -- 记录fallback键和所有回应键,避免在其他地方重复显示
    if not globalProcessedKeys then
        globalProcessedKeys = {}
    end
    globalProcessedKeys[fallback] = true

    -- 添加提问内容小标题
    table.insert(result, choice())
    table.insert(result, "如果玩家没有触发过该提问")

    -- 分离问题文本和$r命令
    local parts = {}
    for part in remainingText:gmatch("[^#]+") do
        table.insert(parts, part)
    end

    -- 第一部分是问题文本
    local questionText = parts[1] or ""

    -- 处理问题文本(可能包含性别分隔)
    if questionText:find("%^") then
        local processedQuestion = processGenderDialogue(questionText)
        table.insert(result, processedQuestion)
    else
        questionText = cleanText(questionText)
        table.insert(result, createQuote(questionText))
    end

    -- 处理$r回应选项
    for i = 2, #parts do
        local part = parts[i]
        if part:match("^%$r%s+") then
            -- 解析$r命令:$r <id> <friendship_change> <response_key>
            local responseId, friendshipChange, responseKey = part:match("%$r%s+(%d+)%s+([%-]?%d+)%s+(.+)")
            if responseId and friendshipChange and responseKey then
                -- 移除responseKey中可能的#前缀
                responseKey = responseKey:gsub("^#", "")
                -- 记录response ID到选项序号的映射(支持一对多)
                if not responseMapping[responseId] then
                    responseMapping[responseId] = {}
                end
                table.insert(responseMapping[responseId], optionCounter)

                -- 记录这个回应键,避免在其他地方重复显示
                globalProcessedKeys[responseKey] = true

                -- 获取下一部分作为选择文本
                local choiceText = parts[i + 1] or ""
                choiceText = cleanText(choiceText, true)

                -- 使用Helper.ExpandTemplate格式化选择模板(不添加选项序号到显示文本)
                local choiceTemplate = createPlayerChoice(choiceText, friendshipChange)
                table.insert(result, wrapInText(choiceTemplate))

                -- 获取回应文本
                if npcData and npcData[responseKey] then
                    local responseText = npcData[responseKey]

                    -- 检查回应文本是否包含性别分支
                    -- !!!这里处理了性别分支,用做其他地方的参考!!!
                    if responseText:find("%^") then
                        -- 使用processGenderDialogue处理性别分支
                        local processedResponse = processGenderDialogue(responseText, "AfterChoice")
                        table.insert(result, wrapInReply(processedResponse))
                    else
                        -- 普通回应文本处理
                        responseText = cleanText(responseText)
                        table.insert(result, wrapInReply(createQuote(responseText, "AfterChoice")))
                    end
                end

                optionCounter = optionCounter + 1

                -- 跳过下一个部分(因为已经作为choiceText处理了)
                i = i + 1
            end
        end
    end
    table.insert(result, div())

    -- 处理fallback文本(如果玩家已经触发过该提问)
    if npcData and npcData[fallback] then
        table.insert(result, choice())
        table.insert(result, "如果玩家已经触发过该提问")
        local fallbackText = npcData[fallback]

        -- 检查fallback文本是否包含$p命令
        if fallbackText:match("%$p%s+") then
            -- 使用新的processPrerequisiteDialogue处理$p命令
            local processedFallback = processPrerequisiteDialogue(fallbackText, responseMapping)
            table.insert(result, processedFallback)
        elseif fallbackText:find("%^") then
            -- 使用processGenderDialogue处理性别分支
            local processedFallback = processGenderDialogue(fallbackText)
            table.insert(result, processedFallback)
        else
            -- 普通fallback文本处理
            -- 处理#$e#为换行
            fallbackText = fallbackText:gsub("#%$e#", "\n")
            fallbackText = cleanText(fallbackText)
            table.insert(result, createQuote(fallbackText))
        end
        table.insert(result, div())
    end
    return table.concat(result, "\n")
end

-- 格式化二级标题
local function formatLevel2Header(title)
    return "== " .. title .. " =="
end

-- 格式化三级标题
local function formatLevel3Header(title)
    return '<span class="talk-title">' .. title .. '</span>'
end
local function formatLevel3HeaderInline(title)
    return '<span class="talk-title talk-title-inline">' .. title .. '</span>'
end

-- 处理并格式化对话文本
formatDialogueText = function(text, key, npcData)
    -- mw.log(key)
    -- mw.log(text)

    if not text or type(text) ~= "string" then
        return text
    end

    -- 使用传入的npcData或全局npcData
    local currentNpcData = npcData or globalNpcData

    if ((text:find("<dd><dl>") or text:find("<dl><dd>")) and (text:find("cquotetext") or text:find("zquotetext"))) then
        -- mw.log("已拦截---")
        mw.log("特殊对话")
        return processDialogueWithChoices(text)
    end

    if (text:find("quotetext")) then
        return text
    end

    -- 最优先处理$q问答对话(需要最高优先级,但需要npcData)
    if text:match("%$q%s+") and currentNpcData then
        return processQuestionDialogue(text, currentNpcData)
    end

    -- 处理$y快速问答对话
    if text:match("%$y%s+") then
        return processQuickResponseDialogue(text)
    end

    -- 处理$query条件对话
    if text:match("%$query%s+") then
        return processQueryDialogue(text)
    end

    -- 首先处理概率对话(可能前面有#字符)
    if text:match("%$c%s+") or text:match("^#+%$c%s+") then
        return processProbabilityText(text)
    end

    -- 处理$d条件对话
    if text:match("^%$d%s*") then
        return processConditionalDialogue(text)
    end

    -- 处理$p条件对话(日常对话中的简单分支)
    if text:match("%$p%s+") then
        return processPrerequisiteDialogue(text, nil)
    end

    -- 优先处理#$1一次性对话(需要高优先级避免被cleanText清除)
    if text:match("^#%$1%s*") then
        return processOnceOnlyDialogue(text)
    end

    -- 处理||分隔的多对话轮换
    if text:find("||") then
        return processMultipleDialogue(text)
    end

    -- 处理性别分隔的对话("^"分隔符)
    if text:find("%^") then
        return processGenderDialogue(text)
    end

    -- 处理斜杠分隔的对话
    if text:find("/") then
        -- 统计斜杠数量
        local _, slashCount = text:gsub("/", "")

        if slashCount == 1 then
            -- 只有一个斜杠,使用性别分隔处理
            return processSlashDialogue(text)
        else
            -- 多个斜杠,使用普通文本分隔处理
            return processMultipleSlashDialogue(text)
        end
    end

    -- 如果处理后的文本包含引用表格,直接返回
    if text:match("class=\"quotetable\"") then
        return text
    end

    -- 清理文本
    text = cleanText(text)

    -- 使用模板包装
    return createQuote(text)
end

-- 支持的NPC列表
local SUPPORTED_NPCS = {"Abigail", "Alex", "Caroline", "Clint", "Demetrius", "Dwarf", "Elliott", "Emily", "Evelyn",
                        "George", "Gil", "Gus", "Haley", "Harvey", "Jas", "Jodi", "Kent", "Krobus", "Leah", "Leo",
                        "LeoMainland", "Lewis", "Linus", "Marnie", "Maru", "Mister Qi", "Pam", "Penny", "Pierre",
                        "rainy", "Robin", "Sam", "Sandy", "Sebastian", "Shane", "Vincent", "Willy", "Wizard"}

-- 可攻略角色列表(根据游戏逻辑)
local MARRIAGEABLE_CHARACTERS = {"Alex", "Elliott", "Harvey", "Sam", "Sebastian", "Shane", -- 男性
"Abigail", "Emily", "Haley", "Leah", "Maru", "Penny", -- 女性
"Krobus"
}

-- 检查是否为可攻略人物
local function isMarriageableCharacter(npcName)
    for _, character in ipairs(MARRIAGEABLE_CHARACTERS) do
        if character == npcName then
            return true
        end
    end
    return false
end

-- 游戏中的地点名称列表(用于Location dialogue识别)
local GAME_LOCATIONS = { -- 主要地点
"Farm", "Town", "Mountain", "Forest", "Beach", "Desert", "Island", "Volcano", "Caldera", -- 建筑物
"FarmHouse", "Greenhouse", "Coop", "Barn", "Shed", "Silo", "Well", "Cabin", -- 商店和服务
"GeneralStore", "SeedShop", "Blacksmith", "Saloon", "Library", "ArchaeologyHouse", "FishShop", "AnimalShop", "Hospital",
"HarveyRoom", "JojaMart", "MovieTheater", -- 住宅
"HaleyHouse", "SamHouse", "Trailer", "ManorHouse", "ScienceHouse", "Tent", -- 特殊地点
"CommunityCenter", "Mine", "SkullCave", "Sewer", "BugLand", "WitchSwamp", "Railroad", "Backwoods", "BusStop", "Woods",
"Summit", "Ginger", "Leo", -- 节日和临时地点
"Temp", "Festival", "Fair", "FlowerDance", "Luau", "JellyDance", "WinterStar"}

-- 检查是否为Location dialogue(按照Wiki文档的4种键格式)
local function isLocationDialogue(key)
    if not key or type(key) ~= "string" then
        return false
    end

    -- 检查每个地点名称
    for _, location in ipairs(GAME_LOCATIONS) do
        -- 格式1: <location>_<x>_<y> - 特定坐标对话
        if string.match(key, "^" .. location .. "_%d+_%d+$") then
            return true
        end

        -- 格式2: <location>_<dayName> - 地点+星期对话
        local dayNames = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
        for _, day in ipairs(dayNames) do
            if string.match(key, "^" .. location .. "_" .. day .. "$") then
                return true
            end
        end

        -- 格式3: <location><hearts> - 地点+好感度对话
        if string.match(key, "^" .. location .. "%d+$") then
            return true
        end

        -- 格式4: <location> - 通用地点对话(需要确保不是其他类型的对话)
        if key == location then
            return true
        end
    end

    return false
end

-- 检查是否为Roommate dialogue(按照Wiki文档的键格式)
local function isRoommateDialogue(key)
    if not key or type(key) ~= "string" then
        return false
    end

    -- 检查常见的室友对话键格式
    -- 1. 包含"roommate"或"Roommate"关键字
    if string.match(key, "roommate") or string.match(key, "Roommate") then
        return true
    end

    -- 2. RejectRoommateProposal系列对话
    if string.match(key, "^RejectRoommateProposal") then
        return true
    end

    -- 3. 特定NPC的室友对话(主要是Krobus)
    if string.match(key, "^Krobus") and
        (string.match(key, "_roommate") or string.match(key, "Roommate") or string.match(key, "indoor") or
            string.match(key, "outdoor")) then
        return true
    end

    -- 4. 室友相关的节日对话变体
    if string.match(key, "_roommate$") or string.match(key, "_Roommate$") then
        return true
    end

    -- 5. 室友特定的季节对话
    if string.match(key, "roommate_") then
        return true
    end

    return false
end

-- 星期映射
local WEEKDAYS = {
    ["Mon"] = "周一",
    ["Tue"] = "周二",
    ["Wed"] = "周三",
    ["Thu"] = "周四",
    ["Fri"] = "周五",
    ["Sat"] = "周六",
    ["Sun"] = "周日"
}

-- 比较MarriageDialogue的优先级
local function compareMarriageDialoguePriority(keyA, keyB)
    -- 移除Marriage_前缀进行比较
    local cleanKeyA = string.gsub(keyA, "^Marriage_", "")
    local cleanKeyB = string.gsub(keyB, "^Marriage_", "")

    -- 判断对话来源类型
    -- 1. NPC专属文件(MarriageDialogueNPC):没有特殊前缀,直接是对话键
    -- 2. 通用文件中的NPC特定:以_NPC名字结尾
    -- 3. 通用文件中的通用对话:不以任何NPC名字结尾

    local function getDialogueType(cleanKey)
        -- 检查是否以任何NPC名字结尾
        for _, npcName in ipairs(SUPPORTED_NPCS) do
            if string.match(cleanKey, "_" .. npcName .. "$") then
                return 2 -- 通用文件中的NPC特定
            end
        end

        -- 检查是否包含数字后缀(如_0, _1),这通常表示通用对话
        if string.match(cleanKey, "_%d+$") then
            return 3 -- 通用文件中的通用对话
        end

        -- 默认认为是NPC专属文件的对话
        return 1 -- NPC专属文件
    end

    local typeA = getDialogueType(cleanKeyA)
    local typeB = getDialogueType(cleanKeyB)

    -- 如果类型不同,按优先级排序
    if typeA ~= typeB then
        return typeA < typeB
    end

    -- 如果类型相同,按数字排序(如果有的话)
    local function extractNumber(key)
        local num = string.match(key, "_(%d+)$")
        return num and tonumber(num) or 0
    end

    local numA = extractNumber(cleanKeyA)
    local numB = extractNumber(cleanKeyB)

    if numA ~= numB then
        return numA < numB
    end

    -- 最后按字符串排序
    return cleanKeyA < cleanKeyB
end

-- 数字到中文映射(0-15)
local CHINESE_NUMBERS = {
    [0] = "零",
    [1] = "一",
    [2] = "二",
    [3] = "三",
    [4] = "四",
    [5] = "五",
    [6] = "六",
    [7] = "七",
    [8] = "八",
    [9] = "九",
    [10] = "十",
    [11] = "十一",
    [12] = "十二",
    [13] = "十三",
    [14] = "十四",
    [15] = "十五"
}

-- 去除连续的空字符串
removeConsecutiveEmptyStrings = function(output)
    -- mw.logObject(output)
    local cleaned = {}
    local lastWasEmpty = false

    for i, line in ipairs(output) do
        if line == "" then
            if not lastWasEmpty then
                table.insert(cleaned, line)
                lastWasEmpty = true
            end
        else
            table.insert(cleaned, line)
            lastWasEmpty = false
        end
    end

    return cleaned
end

-- 节日映射(按时间顺序)
local FESTIVALS = { -- 春季
{
    key = "spring_12",
    name = "复活节",
    season = "春季"
}, {
    key = "DesertFestival",
    name = "沙漠节",
    season = "春季"
}, {
    key = "spring_23",
    name = "花舞节",
    season = "春季"
}, {
    key = "FlowerDance",
    name = "花舞节",
    season = "春季"
}, -- 夏季
{
    key = "summer_10",
    name = "夏威夷宴会",
    season = "夏季"
}, {
    key = "TroutDerby",
    name = "鳟鱼大赛",
    season = "夏季"
}, {
    key = "summer_27",
    name = "月光水母起舞",
    season = "夏季"
}, -- 秋季
{
    key = "fall_15",
    name = "星露谷展览会",
    season = "秋季"
}, {
    key = "fall_26",
    name = "万灵节",
    season = "秋季"
}, -- 冬季
{
    key = "winter_7",
    name = "冰雪节",
    season = "冬季"
}, {
    key = "SquidFest",
    name = "鱿鱼节",
    season = "冬季"
}, {
    key = "NightMarket",
    name = "夜市",
    season = "冬季"
}, {
    key = "winter_24",
    name = "冬日星盛宴",
    season = "冬季"
}}

-- 婚后对话分类
local MARRIAGE_CATEGORIES = {"白天室内", "晚上室内", "雨天日间", "雨天夜间", "室外", "外出",
                             "关系较好", "关系一般", "关系较差", "特定日期", "季节配偶对话",
                             "孩子", "其他"}

-- 格式化婚后对话的日期
local function formatMarriageDate(key)
    local seasonNames = {
        spring = "春季",
        summer = "夏季",
        fall = "秋季",
        winter = "冬季"
    }

    -- spring_1 -> 春季1日
    local season, day = string.match(key, "^([a-z]+)_(%d+)$")
    if season and day and seasonNames[season] then
        return seasonNames[season] .. " " .. day .. " 日"
    end

    -- fall_Abigail -> 秋季,日常
    local season2, npcName = string.match(key, "^([a-z]+)_([A-Z][a-z]+)$")
    if season2 and npcName and seasonNames[season2] then
        return seasonNames[season2] .. ",日常"
    end

    -- 如果不匹配季节格式,尝试使用通用的formatOtherDialogueKey
    return formatOtherDialogueKey(key)
end

-- 获取标准节日对话优先级
local function getStandardFestivalPriority(key)
    if string.match(key, "_spouse_y2$") then
        return 4
    elseif string.match(key, "_spouse$") then
        return 3
    elseif string.match(key, "_y2$") then
        return 2
    else
        return 1
    end
end

-- 获取通用节日对话优先级
local function getFestivalPriority(key, festivalName)
    -- 预告对话(各节日的预告键名)
    local previewKeys = {
        ["复活节"] = "spring_12",
        ["花舞节"] = "spring_23",
        ["夏威夷宴会"] = "summer_10",
        ["鳟鱼大赛"] = "summer_19",
        ["月光水母起舞"] = "summer_27",
        ["星露谷展览会"] = "fall_15",
        ["万灵节"] = "fall_26",
        ["冰雪节"] = "winter_7",
        ["鱿鱼节"] = "winter_11",
        ["冬日星盛宴"] = "winter_24"
    }

    if previewKeys[festivalName] and string.match(key, previewKeys[festivalName]) then
        return 1 -- 预告
    end

    -- 复活节特殊对话
    if festivalName == "复活节" then
        if string.match(key, "^wonEggHunt$") then
            return 6 -- 玩家胜利
        elseif string.match(key, "^wonEggHunt_memory_oneyear$") then
            return 7 -- 玩家胜利(一年后)
        end
    end

    -- 花舞节特殊对话
    if festivalName == "花舞节" then
        if string.match(key, "FlowerDance_Accept$") then
            return 4 -- 同意邀请
        elseif string.match(key, "FlowerDance_Decline") then
            return 5 -- 拒绝邀请
        elseif string.match(key, "FlowerDance_Accept_Spouse$") then
            return 6 -- 配偶邀请(奇数年)
        end
    end

    -- 鳟鱼大赛特殊对话
    if festivalName == "鳟鱼大赛" then
        if string.match(key, "summer_19") then
            return 1 -- 预告
        elseif string.match(key, "summer_20") then
            return 2 -- 第一天
        elseif string.match(key, "summer_21") then
            return 3 -- 第二天
        else
            return 4 -- 比赛期间(普通对话)
        end
    end

    -- 鱿鱼节特殊对话
    if festivalName == "鱿鱼节" then
        if string.match(key, "winter_11") then
            return 1 -- 预告
        elseif string.match(key, "winter_12") then
            return 2 -- 第一天
        elseif string.match(key, "winter_13") then
            return 3 -- 第二天
        else
            return 4 -- 比赛期间(普通对话)
        end
    end

    -- 通用排序
    if string.match(key, "_spouse_y2$") then
        if festivalName == "花舞节" then
            return 7 -- 花舞节配偶邀请(偶数年)
        else
            return 5 -- 配偶(偶数年)
        end
    elseif string.match(key, "_spouse$") then
        if festivalName == "花舞节" then
            return 6 -- 花舞节配偶邀请(奇数年)
        else
            return 4 -- 配偶(奇数年)
        end
    elseif string.match(key, "_y2$") then
        return 3 -- 偶数年
    else
        return 2 -- 一般对话(奇数年)
    end
end

-- 获取节日对话小标题
local function getFestivalSubtitle(key, festivalName, hasPreview)
    -- 预告对话标注
    local previewKeys = {
        ["复活节"] = "spring_12",
        ["花舞节"] = "spring_23",
        ["夏威夷宴会"] = "summer_10",
        ["鳟鱼大赛"] = "summer_19",
        ["月光水母起舞"] = "summer_27",
        ["星露谷展览会"] = "fall_15",
        ["万灵节"] = "fall_26",
        ["冰雪节"] = "winter_7",
        ["鱿鱼节"] = "winter_11",
        ["冬日星盛宴"] = "winter_24"
    }

    if previewKeys[festivalName] and string.match(key, previewKeys[festivalName]) then
        return "预告"
    end

    -- 复活节特殊标注
    if festivalName == "复活节" then
        if string.match(key, "^wonEggHunt$") then
            return "玩家胜利"
        elseif string.match(key, "^wonEggHunt_memory_oneyear$") then
            return "玩家胜利(一年后)"
        end
    end

    -- 花舞节特殊标注
    if festivalName == "花舞节" then
        if string.match(key, "FlowerDance_Accept$") then
            return "同意邀请"
        elseif string.match(key, "FlowerDance_Decline") then
            return "拒绝邀请"
        elseif string.match(key, "FlowerDance_Accept_Spouse$") then
            return "婚后邀请(奇数年)"
        elseif string.match(key, "_spouse_y2$") then
            return "婚后邀请(偶数年)"
        end
    end

    -- 通用标注
    if string.match(key, "_spouse_y2$") then
        return "婚后(偶数年)"
    elseif string.match(key, "_spouse$") then
        return "婚后(奇数年)"
    elseif string.match(key, "_y2$") then
        return "偶数年"
    else
        -- 一般对话(奇数年):只有存在预告时才标注
        if hasPreview and not string.match(key, "_spouse") and not string.match(key, "_y2") then
            return "奇数年"
        end
    end

    -- 星露谷展览会特殊标注
    if festivalName == "星露谷展览会" then
        if key == "Fair_Judging" then
            return "正在评审中"
        elseif key == "Fair_Judged" then
            return "评审结束"
        elseif key == "Fair_Judged_PlayerWon" then
            return "评审结束(玩家获胜)"
        elseif key == "Fair_Judged_PlayerLost" then
            return "评审结束(玩家失败)"
        elseif key == "Fair_Judged_PlayerLost_PurpleShorts" then
            return "评审结束(紫色短裤)"
        end
    end

    -- 夜市特殊标注
    if festivalName == "夜市" then
        local day = string.match(key, "winter_1([567])")
        if day then
            local dayNames = {"第一天", "第二天", "第三天"}
            local dayName = dayNames[tonumber(day) - 4] or ("第" .. day .. "天")
            local dateInfo = "(冬季 1" .. day .. " 日)"
            return dayName .. dateInfo
        end
    end

    -- 沙漠节特殊标注
    if festivalName == "沙漠节" then
        if string.match(key, "Shop_") then
            return "村民商铺"
        elseif string.match(key, "_marriage$") then
            return "婚后"
        else
            -- 后续补充详细日期
            -- 处理沙漠节的数字后缀
            local dayNum = string.match(key, "DesertFestival_[^%d]*(%d+)$")
            if dayNum then
                local dayNames = {"第一天", "第二天", "第三天"}
                local dayName = dayNames[tonumber(dayNum)] or ("第" .. dayNum .. "天")
                local dateInfo = "(春季 " .. tostring(14 + dayNum) .. " 日)"
                return dayName .. dateInfo
            end
        end
    end

    -- 鳟鱼大赛特殊标注
    if festivalName == "鳟鱼大赛" then
        if string.match(key, "summer_19") then
            return "预告(夏季 19 日)"
        elseif string.match(key, "summer_20") then
            return "第一天(夏季 20 日)"
        elseif string.match(key, "summer_21") then
            return "第二天(夏季 21 日)"
        else
            -- 只有存在预告/第一天/第二天任意一条时,才输出"比赛期间"标题
            -- 这个判断需要在调用处传入额外信息,这里先返回特殊标记
            return "TROUT_DERBY_GENERAL"
        end
    end

    -- 鱿鱼节特殊标注
    if festivalName == "鱿鱼节" then
        if string.match(key, "winter_11") then
            return "预告(冬季 11 日)"
        elseif string.match(key, "winter_12") then
            return "第一天(冬季 12 日)"
        elseif string.match(key, "winter_13") then
            return "第二天(冬季 13 日)"
        else
            -- 只有存在预告/第一天/第二天任意一条时,才输出"比赛期间"标题
            -- 这个判断需要在调用处传入额外信息,这里先返回特殊标记
            return "SQUID_FEST_GENERAL"
        end
    end

    return nil
end

-- 获取NPC对话数据
local function getNpcDialogues(npcName)
    local dialogues = {}

    if not npcName then
        return dialogues
    end

    -- 从主要NPC数据获取
    if TalkData[npcName] then
        for key, dialogue in pairs(TalkData[npcName]) do
            if key == "-winter_Wed" then
                dialogues["winter_Wed"] = dialogue
            elseif key == "divorce" then
                dialogues["divorced"] = dialogue
            else
                dialogues[key] = dialogue
            end
        end
    end

    -- 从Strings目录获取各种字符串对话
    -- Strings/Characters - 角色相关字符串
    if TalkData["Strings/Characters"] then
        for key, dialogue in pairs(TalkData["Strings/Characters"]) do
            -- MovieInvite相关对话
            if string.match(key, "^Phone_" .. npcName .. "$") then
                dialogues["Phone_" .. key] = dialogue
            end
        end
    end

    -- 从原1_6_Strings(现在在Strings/下)获取特殊对话
    if TalkData["Strings/1_6_Strings"] then
        for key, dialogue in pairs(TalkData["Strings/1_6_Strings"]) do
            -- 沙漠节对话
            if string.match(key, "^DesertFestival_" .. npcName) then
                dialogues[key] = dialogue
            elseif string.match(key, "^Shop_" .. npcName) then
                dialogues[key] = dialogue
            elseif string.match(key, "^TroutDerby_" .. npcName) then
                dialogues[key] = dialogue
            elseif string.match(key, "^SquidFest_" .. npcName) then
                dialogues[key] = dialogue
            end
        end
    end

    -- 从节日对话获取
    local festivalKeys = {"Festivals/spring13", "Festivals/spring24", "Festivals/summer11", "Festivals/summer28",
                          "Festivals/fall16", "Festivals/fall27", "Festivals/winter8", "Festivals/winter25"}

    for _, festivalKey in ipairs(festivalKeys) do
        if TalkData[festivalKey] then
            -- 检查无后缀版本
            if TalkData[festivalKey][npcName] then
                dialogues[festivalKey .. "_" .. npcName] = TalkData[festivalKey][npcName]
            end
            -- 检查_spouse后缀版本
            if TalkData[festivalKey][npcName .. "_spouse"] then
                dialogues[festivalKey .. "_" .. npcName .. "_spouse"] = TalkData[festivalKey][npcName .. "_spouse"]
            end
            -- 检查_y2后缀版本
            if TalkData[festivalKey][npcName .. "_y2"] then
                dialogues[festivalKey .. "_" .. npcName .. "_y2"] = TalkData[festivalKey][npcName .. "_y2"]
            end
            -- 检查_spouse_y2后缀版本
            if TalkData[festivalKey][npcName .. "_spouse_y2"] then
                dialogues[festivalKey .. "_" .. npcName .. "_spouse_y2"] =
                    TalkData[festivalKey][npcName .. "_spouse_y2"]
            end
        end
    end

    -- 从MarriageDialogue获取婚后对话
    -- 首先从通用MarriageDialogue文件获取,但要过滤掉特定NPC的对话
    if TalkData["MarriageDialogue"] then
        for key, dialogue in pairs(TalkData["MarriageDialogue"]) do
            -- 检查key是否以某个NPC名字结尾,如果是则跳过(这些不是通用对话)
            local isSpecificNpc = false
            for _, supportedNpc in ipairs(SUPPORTED_NPCS) do
                if string.match(key, "_" .. supportedNpc .. "$") then
                    isSpecificNpc = true
                    break
                end
            end

            -- 只有当前NPC的特定对话才添加
            if not isSpecificNpc or string.match(key, "_" .. npcName .. "$") then
                dialogues["Marriage_" .. key] = dialogue
            end
        end
    end

    -- 然后从特定NPC的MarriageDialogue文件获取(会覆盖通用的)
    local marriageKey = "MarriageDialogue" .. npcName
    if TalkData[marriageKey] then
        for key, dialogue in pairs(TalkData[marriageKey]) do
            dialogues["Marriage_" .. key] = dialogue
        end
    end

    -- 从MarriageDialogue获取室友对话(Roommate相关)
    -- 室友对话通常在MarriageDialogue文件中,但使用不同的键格式
    -- 例如:Krobus的室友对话会在MarriageDialogueKrobus中

    -- 从EngagementDialogue获取订婚对话
    if TalkData["EngagementDialogue"] then
        for key, dialogue in pairs(TalkData["EngagementDialogue"]) do
            -- EngagementDialogue的键格式是 "NPCName0", "NPCName1" 等
            -- 检查键是否以npcName开头并且后面跟数字
            if string.match(key, "^" .. npcName .. "[0-9]+$") then
                -- 保留原始键名,这样可以包含所有对话(Abigail0, Abigail1等)
                dialogues["engagement_" .. key] = dialogue
            end
        end
    end

    -- 从schedules获取特殊行程对话
    local schedulesKey = "schedules/" .. npcName
    if TalkData[schedulesKey] then
        for key, dialogue in pairs(TalkData[schedulesKey]) do
            dialogues["schedule_" .. key] = dialogue
        end
    end

    -- 从rainy获取雨天对话
    if TalkData["rainy"] then
        -- mw.log("DEBUG: Found rainy data, checking for NPC: " .. npcName)
        for key, dialogue in pairs(TalkData["rainy"]) do
            -- mw.log("DEBUG: Checking rainy key: " .. key)
            -- 检查键是否与当前NPC相关
            if key == npcName then
                dialogues["rain"] = dialogue
                -- mw.log("DEBUG: Added rain dialogue for: " .. key)
            elseif string.match(key, "^" .. npcName .. "_") then
                -- 处理带后缀的雨天对话,如 "Alex_married"
                local suffix = string.match(key, "^" .. npcName .. "_(.+)$")
                if suffix then
                    dialogues["rain_" .. suffix] = dialogue
                    -- mw.log("DEBUG: Added rain dialogue with suffix: rain_" .. suffix)
                end
            end
        end
    else

    end

    -- 从ExtraDialogue获取额外对话
    if TalkData["ExtraDialogue"] then
        for key, dialogue in pairs(TalkData["ExtraDialogue"]) do
            if string.match(key, "^SummitEvent_") then
                -- mw.log("DEBUG: Found SummitEvent key: " .. key .. " for NPC: " .. npcName)
                -- 获取所有山顶对话,包括:
                -- 1. 配偶通用对话:SummitEvent_Intro_Spouse, SummitEvent_Dialogue1_Spouse 等
                -- 2. 特定配偶对话:SummitEvent_Dialogue3_[NPC名]
                -- 3. 刘易斯对话:SummitEvent_Intro_Lewis, SummitEvent_Dialogue1_Lewis 等
                -- 4. 莫里斯对话:SummitEvent_Intro_Morris, SummitEvent_Dialogue1_Morris 等
                -- 5. 结束对话:SummitEvent_closingmessage 等

                -- 检查是否与当前NPC相关
                if string.match(key, "^SummitEvent_Dialogue3_" .. npcName .. "$") or -- 特定配偶对话
                    (npcName == "Lewis" and string.match(key, "_Lewis$")) or -- 刘易斯对话
                    (npcName == "Morris" and string.match(key, "_Morris$")) or -- 莫里斯对话
                string.match(key, "_Spouse$") or string.match(key, "_Spouse_Gruff$") then -- 配偶通用对话
                    dialogues["Extra_" .. key] = dialogue
                    -- mw.log("DEBUG: Added SummitEvent dialogue: Extra_" .. key)
                else
                    -- mw.log("DEBUG: Skipped SummitEvent key (not relevant): " .. key)
                end
                -- 其他可能的ExtraDialogue键格式
            elseif string.match(key, npcName) then
                -- 包含NPC名称的其他额外对话
                dialogues["Extra_" .. key] = dialogue
            end
        end
    end

    -- 从WorldMap和Locations获取地点相关对话
    if TalkData["WorldMap"] then
        for key, dialogue in pairs(TalkData["WorldMap"]) do
            if string.match(key, npcName) then
                dialogues["WorldMap_" .. key] = dialogue
            end
        end
    end

    if TalkData["Locations"] then
        for key, dialogue in pairs(TalkData["Locations"]) do
            if string.match(key, npcName) then
                dialogues["Location_" .. key] = dialogue
            elseif string.match(key, "^Saloon") then
                dialogues["Location_" .. key] = dialogue
            end
        end
    else

    end

    return dialogues
end

-- 过滤不需要的对话
local function filterDialogues(dialogues)
    local filtered = {}

    for key, dialogue in pairs(dialogues) do
        -- 过滤掉礼物对话和已在$q问答中处理过的对话
        if not string.match(key, "^AcceptGift_") and not string.match(key, "^AcceptBirthdayGift_") and
            not string.match(key, "^eventSeen_") and not string.match(key, "^Event_") and
            not string.match(key, "^event_") and not (globalProcessedKeys and globalProcessedKeys[key]) then
            filtered[key] = dialogue
        end
    end

    return filtered
end

-- 分类对话
local function categorizeDialogues(dialogues, npcName)
    local categories = {
        daily = {}, -- 日常对话
        festival = {}, -- 节日对话
        relationship = {}, -- 关系变化对话
        romantic = {}, -- 婚后对话
        roommate = {}, -- 室友对话
        rain = {}, -- 雨天对话
        item = {}, -- 物品对话
        special = {}, -- 特殊对话
        events = {}, -- 事件对话
        extra = {}, -- ExtraDialogue额外对话
        location = {} -- 地点相关对话
    }

    -- 定义以category为主键的匹配模式
    local patternCategories = {
        -- 地点相关对话
        location = {"^WorldMap_", "^Location_"},
        -- 婚后对话
        romantic = {"^schedule_marriage_", "^patio_"},
        -- 关系变化对话
        relationship = {"^dating_", "^married_", "^married$", "^engagement_", "^dumped_", "^secondChance_", "^breakUp$",
                        "^divorced"},
        -- 节日对话
        festival = {"^Festivals/", "^DesertFestival_", "^TroutDerby_", "^SquidFest_", "^SquidFest$","FlowerDance", "wonEggHunt",
                    "spring_12", "spring_23", "summer_10", "summer_19", "summer_20", "summer_21", "summer_27", 
                    "fall_15", "fall_26", "winter_7", "winter_11", "winter_12", "winter_13", "winter_24",
                    "^Fair_Judging$", "^Fair_Judged", "^FlowerDance_Accept", "^FlowerDance_Decline$",
                    "^danceRejection$", "^WinterStar_GiveGift", "^WinterStar_ReceiveGift", "^Shop_"
        },
        -- 物品对话
        item = {"^AcceptGift", "^AcceptBirthdayGift", "^AcceptBouquet$", "^MovieInvitation$", "^RejectItem",
                "^RejectMermaidPendant", "^RejectBouquet", "^RejectGift_Divorced$"},
        -- 特殊对话
        special = {"^Extra_SummitEvent_", "^cc_", "^joja_Begin$", "^mineArea_", "^GreenRain$", "^GreenRain_2$",
                   "^GreenRainFinished$", "^HitBySlingshot$", "_Entry$", "^Resort", "^SpouseFarmhouseClutter$",
                   "^SpouseGiftJealous$", "^Spouse_MonstersInHouse$", "^SpouseStardrop$", "^WipedMemory$", "^pamHouse",
                   "^movieTheater", "^rain", "^Rain", "^firstVisit_BathHouse_MensLocker_memory_",
                   "^purchasedAnimal_Chicken_memory_", "^purchasedAnimal_Sheep", "^schedule_", "^JojaMart$",
                   "^SeedShop_Entry$", "^IslandSouth_", "^Trailer_Entry$", "^gotPet$", "^structureBuilt_",
                   "^wonIceFishing$", "^wonGrange$", "^fishCaught_", "^cropMatured_", "^accept_", "^reject_",
                   "^achievement_", "^houseUpgrade_", "^firstVisit_", "^purchasedAnimal_", "^questComplete_",
                   "^willyCrabs$", "^gotPet_", "^FullCrabPond$", "^mineArea_"},
        -- 日常对话
        daily = {"^Saloon"}
        -- ExtraDialogue额外对话
        
    }

    -- 特殊复合条件模式
    local complexPatterns = {
        -- 记忆对话中的关系变化对话
        relationship = {function(key)
            return string.match(key, "_memory_") and (string.match(key, "^dating_") or string.match(key, "^married_"))
        end, function(key)
            return string.match(key, "^Event_") and
                       (string.match(key, "group") or string.match(key, "Group") or string.match(key, "10Heart") or
                           string.match(key, "10_Heart"))
        end},
        -- 特殊对话
        special = {function(key)
            return string.match(key, "^DumpsterDiveComment$") and not string.match(key, "^Extra_")
        end},
        extra = {function(key)
            return not string.match(key, "^Extra_SummitEvent_") and string.match(key, "^Extra_")
        end},
        -- 事件对话
        events = {function(key)
            return string.match(key, "^[MTWFS][a-z][a-z]_") and
                       not (string.match(key, "^Mon$") or string.match(key, "^Tue$") or string.match(key, "^Wed$") or
                           string.match(key, "^Thu$") or string.match(key, "^Fri$") or string.match(key, "^Sat$") or
                           string.match(key, "^Sun$") or string.match(key, "^Mon%d") or string.match(key, "^Tue%d") or
                           string.match(key, "^Wed%d") or string.match(key, "^Thu%d") or string.match(key, "^Fri%d") or
                           string.match(key, "^Sat%d") or string.match(key, "^Sun%d") or string.match(key, "^Mon_%d") or 
                           string.match(key, "^Tue_%d") or string.match(key, "^Wed_%d") or string.match(key, "^Thu_%d") or 
                           string.match(key, "^Fri_%d") or string.match(key, "^Sat_%d") or string.match(key, "^Sun_%d") or 
                           string.match(key, "_inlaw_"))
        end}
    }

    -- 定义体检日期表,用于优先级检查
    local checkupDates = {
        Alex = {"summer_16"},
        Sebastian = {"summer_4"},
        Sam = {"fall_11"},
        Haley = {"winter_9"},
        Penny = {"winter_4"},
        Elliott = {"summer_9"},
        Emily = {"winter_11"},
        Leah = {"spring_16"},
        Abigail = {"spring_4"},
        George = {"spring_23", "summer_23", "fall_23", "winter_23"},
        Jodi = {"spring_11", "spring_18"},
        Clint = {"winter_16"},
        Lewis = {"fall_9"},
        Caroline = {"fall_25"},
        Willy = {"spring_9"},
        Demetrius = {"summer_25"},
        Vincent = {"spring_11"},
        Gus = {"fall_4"},
        Pam = {"spring_25"},
        Marnie = {"fall_18", "winter_18"},
        Robin = {"summer_18"},
        Evelyn = {"spring_2", "summer_2", "fall_2", "winter_2", "spring_23", "summer_23", "fall_23", "winter_23"},
        Jas = {"winter_18"}
    }

    -- 检查是否为体检日对话的辅助函数
    local function isCheckupDialogue(key, npcName)
        if not npcName or not checkupDates[npcName] then
            return false
        end
        
        -- 检查schedule_前缀的键
        local cleanKey = string.gsub(key, "^schedule_", "")
        
        for _, checkupDate in ipairs(checkupDates[npcName]) do
            -- 匹配带季节前缀的键(如"spring_23.000")
            if string.match(cleanKey, "^" .. checkupDate .. "%.") then
                return true
            end
            -- 匹配纯数字键(如"23.000"),用于乔治和艾芙琳等全季节体检的角色
            local dayOnly = string.match(checkupDate, "_(%d+)$")
            if dayOnly and string.match(cleanKey, "^" .. dayOnly .. "%.") then
                return true
            end
        end
        return false
    end

    for key, dialogue in pairs(dialogues) do
        -- 首先检查是否为Location dialogue
        if isLocationDialogue(key) then
            categories.location[key] = dialogue
            -- 然后检查Marriage_前缀的对话
        elseif string.match(key, "^Marriage_") then
            -- 检查是否为室友对话
            local cleanKey = string.gsub(key, "^Marriage_", "")
            if isRoommateDialogue(cleanKey) then
                categories.roommate[key] = dialogue
            else
                -- 婚后对话
                categories.romantic[key] = dialogue
            end
            -- 检查其他室友对话键格式
        elseif isRoommateDialogue(key) then
            categories.roommate[key] = dialogue
        -- 在检查节日对话之前,优先检查体检日对话
        elseif string.match(key, "^schedule_") and isCheckupDialogue(key, npcName) then
            categories.special[key] = dialogue
        else
            -- 检查复合条件模式
            local matched = false

            -- 遍历复合条件模式(以category为主键)
            for category, checkFunctions in pairs(complexPatterns) do
                for _, checkFunction in ipairs(checkFunctions) do
                    if checkFunction(key) then
                        categories[category][key] = dialogue
                        matched = true
                        break
                    end
                end
                if matched then
                    break
                end
            end

            -- 如果复合条件未匹配,检查简单模式
            if not matched then
                -- 遍历简单模式(以category为主键)
                for category, patterns in pairs(patternCategories) do
                    for _, pattern in ipairs(patterns) do
                        if string.match(key, pattern) then
                            categories[category][key] = dialogue
                            matched = true
                            break
                        end
                    end
                    if matched then
                        break
                    end
                end
            end

            -- 如果所有模式都未匹配,归入日常对话
            if not matched then
                categories.daily[key] = dialogue
            end
        end
    end
    mw.logObject(categories)
    return categories
end

-- 格式化其他对话的键名
local function formatOtherDialogueKey(key)
    -- 处理特殊的memory对话(使用table格式便于维护和追加)
    local memoryKeys = {
        ["firstVisit_BathHouse_MensLocker_memory_oneday"] = "进入[[温泉]]男更衣室(1 天后)",
        ["purchasedAnimal_Chicken_memory_oneday"] = "购买了[[鸡]](1 天后)",
        ["purchasedAnimal_Sheep"] = "购买了[[绵羊]]"
    }

    if memoryKeys[key] then
        return memoryKeys[key]
    end

    local seasonNames = {
        fall = "秋季",
        spring = "春季",
        summer = "夏季",
        winter = "冬季"
    }
    local weekdayNames = {
        Mon = "周一",
        Tue = "周二",
        Wed = "周三",
        Thu = "周四",
        Fri = "周五",
        Sat = "周六",
        Sun = "周日"
    }
    local heartNames = {
        ["2"] = CHINESE_NUMBERS[2] or "二",
        ["4"] = CHINESE_NUMBERS[4] or "四",
        ["6"] = CHINESE_NUMBERS[6] or "六",
        ["8"] = CHINESE_NUMBERS[8] or "八",
        ["10"] = CHINESE_NUMBERS[10] or "十"
    }

    -- 处理_inlaw_格式(使用模式匹配和函数处理)
    local inlawPatterns = { -- 处理 Mon_inlaw_Maru 格式
    {
        pattern = "^([A-Z][a-z][a-z])_inlaw_([A-Z][a-z]+)$",
        handler = function(weekday, npcName)
            if weekdayNames[weekday] then
                local chineseName = '[[' .. getCharacterName(npcName) .. ']]'
                return weekdayNames[weekday] .. ",玩家和" .. chineseName .. "结婚后"
            end
            return nil
        end
    }, -- 处理 season_Weekday_inlaw_NPC 格式
    {
        pattern = "^([a-z]+)_([A-Z][a-z][a-z])_inlaw_([A-Z][a-z]+)$",
        handler = function(season, weekday, npcName)
            if seasonNames[season] and weekdayNames[weekday] then
                local chineseName = '[[' .. getCharacterName(npcName) .. ']]'
                return seasonNames[season] .. "," .. weekdayNames[weekday] .. ",玩家和" .. chineseName ..
                           "结婚后"
            end
            return nil
        end
    }}

    if string.find(key, "_inlaw_") then
        for _, patternInfo in ipairs(inlawPatterns) do
            local matches = {string.match(key, patternInfo.pattern)}
            if #matches > 0 and matches[1] then
                local result = patternInfo.handler(unpack(matches))
                if result then
                    return result
                end
            end
        end
    end

    -- 处理季节相关的对话格式(使用模式匹配和函数处理)
    local seasonPatterns = { -- summer_6 -> 夏季6日 (处理季节+数字格式)
    {
        pattern = "^([a-z]+)_(%d+)$",
        handler = function(season, day)
            if seasonNames[season] then
                return seasonNames[season] .. " " .. day .. " 日"
            end
            return nil
        end
    }, -- fall_Fri10 -> 十心(季节+星期+数字格式的心级对话只显示心级)
    {
        pattern = "^([a-z]+)_([A-Z][a-z][a-z])(%d+)_(%d+)$",
        handler = function(season, weekday, hearts, year)
            if seasonNames[season] and weekdayNames[weekday] then
                local heartName = heartNames[hearts] or hearts
                local yearName = Helper.ChineseNumber(tonumber(year)) or year
                return seasonNames[season] .. "," .. weekdayNames[weekday] .. "(" .. heartName .. "心、第" .. yearName .. "年)"
            end
            return nil
        end
    }, -- fall_Fri10 -> 十心(季节+星期+数字格式的心级对话只显示心级)
    {
        pattern = "^([a-z]+)_([A-Z][a-z][a-z])(%d+)$",
        handler = function(season, weekday, hearts)
            if seasonNames[season] and weekdayNames[weekday] then
                local heartName = heartNames[hearts] or hearts
                return "(" .. heartName .. "心)"
            end
            return nil
        end
    }, -- fall_Mon -> 秋季,周一 (处理季节+星期格式)
    {
        pattern = "^([a-z]+)_([A-Z][a-z][a-z])$",
        handler = function(season, weekday)
            if seasonNames[season] and weekdayNames[weekday] then
                return seasonNames[season] .. "," .. weekdayNames[weekday]
            end
            return nil
        end
    }, -- fall_Sun_old -> 秋季,周日 (处理季节+星期+后缀格式)
    {
        pattern = "^([a-z]+)_([A-Z][a-z][a-z])_",
        handler = function(season, weekday)
            if seasonNames[season] and weekdayNames[weekday] then
                return seasonNames[season] .. "," .. weekdayNames[weekday]
            end
            return nil
        end
    }}

    for _, patternInfo in ipairs(seasonPatterns) do
        local matches = {string.match(key, patternInfo.pattern)}
        if #matches > 0 and matches[1] then
            local result = patternInfo.handler(unpack(matches))
            if result then
                return result
            end
        end
    end

    -- 处理简单数字键,如 "4" -> "四心",以及数字_*键,如 "4_*" -> "4 日"(使用模式匹配和函数处理)
    local numberPatterns = {{
        pattern = "^(%d+)_%*$",
        handler = function(number)
            return number .. " 日"
        end
    }, {
        pattern = "^(%d+)$",
        handler = function(number)
            local heartName = heartNames[number] or number
            return "(" .. heartName .. "心)"
        end
    }}

    for _, patternInfo in ipairs(numberPatterns) do
        local matches = {string.match(key, patternInfo.pattern)}
        if #matches > 0 and matches[1] then
            return patternInfo.handler(unpack(matches))
        end
    end

    -- 处理社区升级对话(使用table格式便于维护和追加)
    local communityKeys = {
        ["pamHouseUpgradeAnonymous"] = "社区升级 - 潘姆升级房子(匿名)",
        ["pamHouseUpgrade"] = "社区升级 - 潘姆升级房子(不匿名)"
    }

    if communityKeys[key] then
        return communityKeys[key]
    end

    -- 处理离婚对话(使用table格式便于维护和追加)
    local divorceKeys = {
        ["divorced_once"] = "首次离婚",
        ["divorced_twice"] = "再次离婚",
        ["与once离婚"] = "首次离婚",
        ["与twice离婚"] = "再次离婚",
        ["married_twice"] = "再次结婚",
        ["与twice结婚"] = "再次结婚",
        ["divorced"] = "离婚后"
    }

    if divorceKeys[key] then
        return divorceKeys[key]
    elseif string.match(key, "^divorced_(.+)") then
        local npcName = string.match(key, "^divorced_(.+)")
        -- 特殊处理:once和twice不是人名,而是次数
        if npcName == "once" then
            return "首次离婚"
        elseif npcName == "twice" then
            return "再次离婚"
        else
            -- 获取中文名称
            local chineseName = getCharacterName(npcName)
            return "与" .. chineseName .. "离婚后"
        end
    end

    -- 处理酒吧对话(使用table格式便于维护和追加)
    local saloonKeys = {
        ["Saloon"] = "酒吧",
        ["Saloon4"] = "酒吧(" .. (CHINESE_NUMBERS[4] or "四") .. "心)",
        ["Saloon8"] = "酒吧(" .. (CHINESE_NUMBERS[8] or "八") .. "心)"
    }

    if saloonKeys[key] then
        return saloonKeys[key]
    elseif string.match(key, "^Saloon_([A-Z][a-z][a-z])$") then
        local weekday = string.match(key, "^Saloon_([A-Z][a-z][a-z])$")
        if weekdayNames[weekday] then
            return "酒吧," .. weekdayNames[weekday]
        end
    end

    -- 处理其他地点对话(使用table格式便于维护和追加)
    local locationKeys = {
        ["JojaMart"] = "Joja超市 - 工作中",
        ["SeedShop_Entry"] = "皮埃尔的杂货店"
    }

    if locationKeys[key] then
        return locationKeys[key]
    end

    -- 处理度假村对话
    if string.match(key, "^IslandSouth_") then
        return "[[姜岛#海滩度假村|度假村]]酒吧"
    end

    -- 处理提问对话(使用模式匹配和函数处理)
    local questionPatterns = { -- 处理旧式提问格式
    {
        pattern = "(.*)_0(%d)_old$",
        handler = function(baseKey, questionNum)
            return baseKey .. "(提问" .. questionNum .. ")"
        end
    }, -- 处理带选项的提问格式
    {
        pattern = "(.*)_0(%d)_(%d%d)$",
        handler = function(baseKey, questionNum, optionNum)
            return baseKey .. "(提问" .. questionNum .. "选项" .. optionNum .. ")"
        end
    }}

    for _, patternInfo in ipairs(questionPatterns) do
        local matches = {string.match(key, patternInfo.pattern)}
        if #matches > 0 and matches[1] then
            return patternInfo.handler(unpack(matches))
        end
    end

    -- 处理接受/拒绝物品对话、农作物成熟对话和钓鱼对话(使用模式匹配和函数处理)
    local itemPatterns = {{
        pattern = "^accept_(%d+)$",
        handler = function(id)
            return "收到 " .. getItemDisplayName(id)
        end
    }, {
        pattern = "^reject_(%d+)$",
        handler = function(id)
            return "拒绝 " .. getItemDisplayName(id)
        end
    }, {
        pattern = "^cropMatured_(%d+)$",
        handler = function(id)
            return getItemDisplayName(id) .. "成熟后"
        end
    }, {
        pattern = "^fishCaught_(%d+)$",
        handler = function(id)
            return "钓到 " .. getItemDisplayName(id)
        end
    }}

    for _, patternInfo in ipairs(itemPatterns) do
        local id = string.match(key, patternInfo.pattern)
        if id then
            return patternInfo.handler(id)
        end
    end

    -- 处理特殊事件和建筑相关对话(使用table格式便于维护和追加)
    local specialEventKeys = {
        ["willyCrabs"] = "触发威利蟹笼事件后",
        ["gotPet"] = "获得[[宠物]]后",
        ["structureBuilt_Stable"] = "建造[[马厩]]后",
        ["structureBuilt_Fish Pond"] = "建造[[鱼塘]]后",
        ["structureBuilt_Barn_memory_oneweek"] = "建造[[畜棚]](1 周后)",
        ["structureBuilt_Coop"] = "建造[[鸡舍|栏舍]]后",
        ["structureBuilt_Coop_memory_oneweek"] = "建造[[鸡舍|栏舍]](1 周后)",
        ["structureBuilt_Shed"] = "建造[[小屋]]后",
        ["structureBuilt_Slime Hutch"] = "建造[[史莱姆屋]]后",
        ["structureBuilt_Stable_memory_oneweek"] = "建造[[马厩]](1 周后)",
        ["wonIceFishing"] = "[[冰雪节]]获胜后",
        ["achievement_15"] = "获得[[成就]]“厨子”后",
        ["houseUpgrade_1"] = "[[农舍]]升级 1 次后",
        ["houseUpgrade_2"] = "[[农舍]]升级 2 次后",
        ["houseUpgrade_3"] = "[[农舍]]升级 3 次后",
        ["Trailer_Entry"] = "位于[[拖车]]门口时",
        ["MovieInvitation"] = "[[电影院|观影]]邀请",
        ["wonGrange"] = "[[星露谷展览会]]获胜后",

        ["firstVisit_Desert"] = "前往[[沙漠]]后",
        ["firstVisit_Sewer"] = "进入[[下水道]]后",
        ["firstVisit_Railroad"] = "前往[[铁路]]后",
        ["firstVisit_IslandNorth"] = "进入[[姜岛北部]]后",
        ["firstVisit_Caldera"] = "[[火山地牢]]登顶后",
        ["firstVisit_ElliottHouse"] = "进入[[艾利欧特小屋]]后",

        ["purchasedAnimal_Duck"] = "购买[[鸭子]]后",
        ["purchasedAnimal_Dinosaur"] = "饲养[[恐龙]]后",
        ["questComplete_104_memory_oneday"] = "完成[[任务]]“作物研究”(1 天后)",
        
        ["FullCrabPond"] = "[[鱼塘]]饲养[[螃蟹]]并且鱼塘已满",

        ["gotPet_memory_oneday"] = "获得[[宠物]](1 天后)",
        ["gotPet_memory_oneweek"] = "获得[[宠物]](1 周后)",
        ["gotPet_memory_oneyear"] = "获得[[宠物]](1 年后)",

        ["summer_Town_57_40"] = "夏季,位于花园时", -- 刘易斯
        ["fall_Town_57_40"] = "秋季,位于花园时", 

        ["summer_Mountain_58_35"] = "夏季,位于[[深山]]时", -- 塞巴斯蒂安

        -- ["summer_Thu6_2"] = "夏季,周四(六心、第二年)", -- 山姆(后续需要调整处理方式)
        -- ["fall_Thu6_1"] = "秋季,周四(六心、第一年)",
        -- ["winter_Thu6_1"] = "冬季,周四(六心、第一年)",

        ["mineArea_10_oneday"] = "[[矿井]]抵达第 10 层(1 天后)", --阿比盖尔
        ["mineArea_121"] = "进入[[骷髅洞穴]]后",-- 克林特
        ["mineArea_40"] = "[[矿井]]抵达第 40 层后",
        ["mineArea_80"] = "[[矿井]]抵达第 80 层后",

        ["pamHouseUpgrade_memory_oneweek"] = "不匿名(1 周后)",

        ["school"] = "学习",
        ["tent"] = "帐篷",
        ["dinner"] = "晚餐",
        ["LeoDeparture"] = "搬往[[鹈鹕镇]]"
    }

    if specialEventKeys[key] then
        return specialEventKeys[key]
    end

    -- 处理展览会对话(使用table格式便于维护和追加)
    local fairKeys = {
        ["Fair_Judging"] = "正在评审中",
        ["Fair_Judged"] = "评审结束",
        ["Fair_Judged_PlayerWon"] = "评审结束(玩家获胜)",
        ["Fair_Judged_PlayerLost"] = "评审结束(玩家失败)",
        ["Fair_Judged_PlayerLost_PurpleShorts"] = "评审结束(紫色短裤)"
    }

    if string.match(key, "^Fair_") then
        return fairKeys[key] or key
    end

    -- 处理单独的married键
    if key == "married" then
        return "结婚后"
    end

    -- 处理星期+好感度格式和简单的星期键名(使用模式匹配和函数处理)
    local weekdayPatterns = { -- 星期+好感度格式:Sun2 -> 周日,二心
    {
        pattern = "^([A-Z][a-z][a-z])(%d+)$",
        handler = function(weekday, hearts)
            if weekdayNames[weekday] then
                local heartName = heartNames[hearts] or hearts
                return weekdayNames[weekday] .. "," .. heartName .. "心"
            end
            return nil
        end
    }, -- 简单的星期键名:Wed -> 周三
    {
        pattern = "^([A-Z][a-z][a-z])$",
        handler = function(weekday)
            return weekdayNames[weekday]
        end
    }}

    for _, patternInfo in ipairs(weekdayPatterns) do
        local matches = {string.match(key, patternInfo.pattern)}
        if #matches > 0 and matches[1] then
            local result = patternInfo.handler(unpack(matches))
            if result then
                return result
            end
        end
    end

    -- 默认返回原键名
    return key
end

-- 生成排序键
local function generateSortKey(key)
    -- 按季节、日期、星期、好感度排序
    local seasonOrder = {
        spring = 1,
        summer = 2,
        fall = 3,
        winter = 4
    }
    local weekdayOrder = {
        Mon = 1,
        Tue = 2,
        Wed = 3,
        Thu = 4,
        Fri = 5,
        Sat = 6,
        Sun = 7
    }

    -- 处理季节+星期+好感度格式:fall_Fri10
    local season, weekday, hearts = string.match(key, "^([a-z]+)_([A-Z][a-z][a-z])(%d+)$")
    if season and weekday and hearts and seasonOrder[season] then
        local seasonNum = seasonOrder[season]
        local weekdayNum = weekdayOrder[weekday] or 8
        local heartsNum = tonumber(hearts) or 0
        return string.format("%d_%d_%02d_%s", seasonNum, weekdayNum, heartsNum, key)
    end

    -- 处理季节+星期格式:fall_Mon
    local season2, weekday2 = string.match(key, "^([a-z]+)_([A-Z][a-z][a-z])$")
    if season2 and weekday2 and seasonOrder[season2] then
        local seasonNum = seasonOrder[season2]
        local weekdayNum = weekdayOrder[weekday2] or 8
        return string.format("%d_%d_%02d_%s", seasonNum, weekdayNum, 0, key)
    end

    -- 处理季节+星期+其他格式:fall_Sun_old
    local season3, weekday3 = string.match(key, "^([a-z]+)_([A-Z][a-z][a-z])_")
    if season3 and weekday3 and seasonOrder[season3] then
        local seasonNum = seasonOrder[season3]
        local weekdayNum = weekdayOrder[weekday3] or 8
        return string.format("%d_%d_%02d_%s", seasonNum, weekdayNum, 0, key)
    end

    -- 处理季节+日期格式:summer_6
    local season4, day = string.match(key, "^([a-z]+)_(%d+)$")
    if season4 and day and seasonOrder[season4] then
        local seasonNum = seasonOrder[season4]
        local dayNum = tonumber(day) or 0
        return string.format("%d_8_%02d_%s", seasonNum, dayNum, key) -- 使用8作为星期排序,确保日期在星期后
    end

    -- 处理星期+好感度格式:Sun2
    local weekday4, hearts2 = string.match(key, "^([A-Z][a-z][a-z])(%d+)$")
    if weekday4 and hearts2 and weekdayOrder[weekday4] then
        local weekdayNum = weekdayOrder[weekday4]
        local heartsNum = tonumber(hearts2) or 0
        return string.format("0_%d_%02d_%s", weekdayNum, heartsNum, key)
    end

    -- 处理纯数字键:4
    if string.match(key, "^%d+$") then
        local heartNum = tonumber(key) or 0
        return string.format("0_0_%02d_%s", heartNum, key)
    end

    -- 默认排序
    return "5_0_00_" .. key
end

-- 生成日常对话输出
-- 解析星期对话键的辅助函数
local function parseWeekdayKey(key, day, npcName)
    local hearts, year
    local inlawSpouse = string.match(key, "_inlaw_(.+)")
    
    -- 检查复合格式 Day2_2(心级_年份)
    local complexHearts, complexYear = string.match(key, day .. "(%d+)_(%d+)")
    if complexHearts and complexYear then
        hearts = complexHearts
        year = complexYear
    else
        -- 检查简单下划线格式 Day_3(年份)
        local underscoreYear = string.match(key, day .. "_(%d+)")
        if underscoreYear then
            -- 下划线格式:对于Dwarf是心数,对于其他NPC是年份
            if npcName == "Dwarf" then
                hearts = underscoreYear
                year = nil
            else
                hearts = "0"
                year = underscoreYear
            end
        else
            -- 简单心数格式 Day8
            hearts = string.match(key, day .. "(%d+)") or "0"
            year = nil
        end
    end
    
    -- 检查是否为年份对话
    local isYear = year ~= nil
    
    return {
        key = key,
        hearts = tonumber(hearts),
        year = year and tonumber(year) or nil,
        inlawSpouse = inlawSpouse,
        isYear = isYear
    }
end

local function formatDailyDialogues(dialogues, npcData, npcName)
    local output = {}
    local weekdays = {}
    local locations = {}
    local seasons = {}
    local saloonDialogues = {} -- 酒吧对话
    local questionDialogues = {} -- 提问对话
    local dateDialogues = {} -- 日期对话(Generic dialogue中的dayOfMonth格式)
    local others = {}
    local yearDialogues = {}
    local introduction = nil

    mw.logObject(dialogues)

    -- 定义星期列表
    local weekdayList = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}

    -- 分离不同类型的日常对话
    for key, dialogue in pairs(dialogues) do
        -- 处理初次见面对话
        if key == "Introduction" then
            introduction = dialogue
        -- 处理酒吧对话
        elseif string.match(key, "^Saloon") then
            saloonDialogues[key] = dialogue
        -- 检查是否为提问对话(优先检查)
        elseif string.match(key, "_0%d_old$") or string.match(key, "_0%d_%d%d$") then
            questionDialogues[key] = dialogue
        else
            -- 检查是否为星期对话
            local isWeekday = false
            for _, day in ipairs(weekdayList) do
                if string.match(key, "^" .. day) and (not string.match(key, "^" .. day .. "_") or 
                   string.match(key, "_inlaw_") or string.match(key, "^" .. day .. "_%d+$") or 
                   string.match(key, "^" .. day .. "%d+_%d+$")) then
                    
                    if not weekdays[day] then
                        weekdays[day] = {}
                    end
                    
                    local parsedData = parseWeekdayKey(key, day, npcName)
                    parsedData.dialogue = dialogue
                    table.insert(weekdays[day], parsedData)
                    isWeekday = true
                    break
                end
            end
            
            if not isWeekday then
                -- 匹配季节对话
                if string.match(key, "^[a-z]+_") then
                    local season = string.match(key, "^([a-z]+)_")
                    if season and (season == "spring" or season == "summer" or season == "fall" or season == "winter") then
                        -- 检查是否为复合季节对话(如summer_Thu6_2)
                        local weekday, hearts, year = string.match(key, "^[a-z]+_([A-Z][a-z][a-z])(%d+)_(%d+)$")
                        local inlawSpouse = string.match(key, "_inlaw_(.+)")
                        if weekday and hearts and year then
                            -- 复合季节对话,包含年份和心数,视为年份对话
                            if not yearDialogues[season .. "_" .. weekday] then
                                yearDialogues[season .. "_" .. weekday] = {}
                            end
                            table.insert(yearDialogues[season .. "_" .. weekday], {
                                year = tonumber(year),
                                hearts = tonumber(hearts),
                                dialogue = dialogue,
                                inlawSpouse = inlawSpouse,
                                season = season,
                                weekday = weekday
                            })
                        else
                            -- 正常季节对话
                            if not seasons[season] then
                                seasons[season] = {}
                            end
                            seasons[season][key] = dialogue
                        end
                    else
                        -- 不是季节对话,放入其他
                        others[key] = dialogue
                    end
                elseif string.match(key, "Resort") or string.match(key, "^IslandSouth_") then
                    -- 度假村对话特殊处理
                    if not locations["度假村"] then
                        locations["度假村"] = {}
                    end
                    locations["度假村"][key] = dialogue
                elseif string.match(key, "^%d+$") or string.match(key, "^%d+_%d+$") or string.match(key, "^%d+_%*$") then
                    -- 日期对话:纯数字键(如"10")、数字_数字键(如"2_1")或数字_*键(如"4_*")
                    dateDialogues[key] = dialogue
                else
                    -- 其他地点或通用对话
                    others[key] = dialogue
                end
            end
        end
    end

    -- 另注:山姆、文森特等人的对话年份处理
    -- 输出初次见面对话
    if introduction then
        table.insert(output, formatLevel3Header("初次见面"))
        table.insert(output, "")
        table.insert(output, formatDialogueText(introduction, nil, npcData))
        table.insert(output, "")
    end

    -- 输出星期对话
    if next(weekdays) then
        local output_temp = {}
        local weekOrder = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
        for _, day in ipairs(weekOrder) do
            if weekdays[day] then
                table.insert(output_temp, "'''" .. WEEKDAYS[day] .. "'''")
                table.insert(output_temp, "")

                -- 将对话按基础键分组,同时分离年份对话和心级对话
                local groupedDialogues = {}
                local yearDialogues = {}

                for _, item in ipairs(weekdays[day]) do
                    local baseKey = item.key
                    if item.inlawSpouse then
                        baseKey = string.gsub(item.key, "_inlaw_.*", "")
                    end

                    if item.isYear or item.hearts > 0 then
                        -- 年份对话或有心数的对话统一处理
                        if not yearDialogues[baseKey] then
                            yearDialogues[baseKey] = {}
                        end
                        table.insert(yearDialogues[baseKey], {
                            year = item.year, -- 年份字段,可能为nil
                            hearts = item.hearts,
                            dialogue = item.dialogue,
                            inlawSpouse = item.inlawSpouse
                        })
                    else
                        -- 只有0心的常规对话
                        if not groupedDialogues[baseKey] then
                            groupedDialogues[baseKey] = {
                                baseKey = baseKey,
                                hearts = item.hearts,
                                mainDialogue = nil,
                                inlawDialogues = {}
                            }
                        end

                        if item.inlawSpouse then
                            -- 对于inlaw对话,需要保存心级信息
                            groupedDialogues[baseKey].inlawDialogues[item.inlawSpouse] = {
                                dialogue = item.dialogue,
                                hearts = item.hearts
                            }
                        else
                            groupedDialogues[baseKey].mainDialogue = item.dialogue
                        end
                    end
                end

                -- 按好感度排序分组
                local sortedGroups = {}
                for _, group in pairs(groupedDialogues) do
                    table.insert(sortedGroups, group)
                end
                table.sort(sortedGroups, function(a, b)
                    return a.hearts < b.hearts
                end)

                -- 按顺序输出:1. 常规对话(0心) 2. 年份对话 3. 心级对话(>0心)
                
                -- 1. 先输出常规对话(0心)
                for _, group in ipairs(sortedGroups) do
                    if group.hearts == 0 then
                        -- 输出主对话
                        if group.mainDialogue then
                            table.insert(output_temp, formatDialogueText(group.mainDialogue, nil, npcData))
                            table.insert(output_temp, "")
                        end

                        -- 输出inlaw对话
                        for spouse, dialogueData in pairs(group.inlawDialogues) do
                            local chineseName = getCharacterName(spouse)
                            local dialogue, hearts

                            -- 兼容旧格式(直接是字符串)和新格式(包含hearts的表)
                            if type(dialogueData) == "string" then
                                dialogue = dialogueData
                                hearts = 0
                            else
                                dialogue = dialogueData.dialogue
                                hearts = dialogueData.hearts or 0
                            end

                            local label = "(与" .. '[[' .. chineseName .. ']]' .. "结婚后)"
                            table.insert(output_temp, label)
                            table.insert(output_temp, "")
                            table.insert(output_temp, formatDialogueText(dialogue, nil, npcData))
                            table.insert(output_temp, "")
                        end
                    end
                end

                
                -- 2. 然后输出年份对话
                -- 先收集所有年份对话到一个数组中进行统一排序
                local allYearItems = {}
                for baseKey, yearItems in pairs(yearDialogues) do
                    for _, item in ipairs(yearItems) do
                        table.insert(allYearItems, item)
                    end
                end
                
                if #allYearItems > 0 then
                    mw.logObject("排序前所有年份对话")
                    mw.logObject(allYearItems)
                    
                    -- 按心数排序,心数相同的按年份排序
                    table.sort(allYearItems, function(a, b)
                        local heartsA = a.hearts or 0
                        local heartsB = b.hearts or 0
                        if heartsA ~= heartsB then
                            return heartsA < heartsB
                        end
                        -- 如果心数相同,按年份排序(没有年份的排在前面)
                        local yearA = a.year or 0
                        local yearB = b.year or 0
                        return yearA < yearB
                    end)

                    mw.logObject("排序后所有年份对话")
                    mw.logObject(allYearItems)

                    for _, yearItem in ipairs(allYearItems) do
                        local prefix = ""
                        
                        if yearItem.year and yearItem.hearts and yearItem.hearts > 0 then
                            -- 复合情况:既有年份又有心数
                            local heartsText = CHINESE_NUMBERS[yearItem.hearts] or tostring(yearItem.hearts)
                            local yearText = CHINESE_NUMBERS[yearItem.year] or tostring(yearItem.year)
                            prefix = "(" .. heartsText .. "心、第" .. yearText .. "年"
                        elseif yearItem.year then
                            -- 只有年份
                            local yearText = CHINESE_NUMBERS[yearItem.year] or tostring(yearItem.year)
                            prefix = "(第" .. yearText .. "年"
                        elseif yearItem.hearts and yearItem.hearts > 0 then
                            -- 只有心数
                            local heartsText = CHINESE_NUMBERS[yearItem.hearts] or tostring(yearItem.hearts)
                            prefix = "(" .. heartsText .. "心"
                        else
                            -- 应该不会到这里,但作为保险
                            prefix = "(未知格式"
                        end
                        
                        if yearItem.inlawSpouse then
                            local chineseName = getCharacterName(yearItem.inlawSpouse)
                            prefix = prefix .. ",与" .. '[[' .. chineseName .. ']]' .. "结婚后)"
                        else
                            prefix = prefix .. ")"
                        end
                        
                        table.insert(output_temp, prefix)
                        table.insert(output_temp, "")
                        table.insert(output_temp, formatDialogueText(yearItem.dialogue, nil, npcData))
                        table.insert(output_temp, "")
                    end
                end

                -- 3. 心级对话现在都在第二部分统一处理了,这里不再需要
                -- (原本的心级对话逻辑已合并到年份对话中)
            end
        end
        table.insert(output, Helper.ExpandTemplate("Collapse", {
            formatLevel3HeaderInline("星期对话"),
            content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
        }))
    end

    -- 输出季节对话
    if next(seasons) then
        local output_temp = {}
        local seasonOrder = {"spring", "summer", "fall", "winter"}
        local seasonNames = {
            spring = "春季",
            summer = "夏季",
            fall = "秋季",
            winter = "冬季"
        }

        for _, season in ipairs(seasonOrder) do
            if seasons[season] then
                -- 输出季节
                table.insert(output_temp, "'''" .. Helper.ExpandTemplate("Season", {seasonNames[season]}) .. "'''")
                -- table.insert(output_temp, "''" .. seasonNames[season] .. "''")
                table.insert(output_temp, "")

                -- 格式化并排序季节对话
                local seasonDialogues = {}
                local groupedDialogues = {} -- 用于分组相关对话
                local seasonYearDialogues = {} -- 季节年份对话

                for key, dialogue in pairs(seasons[season]) do
                    local displayKey = formatOtherDialogueKey(key)
                    local sortKey = generateSortKey(key)

                    -- 检查是否为_inlaw_对话
                    local baseKey = key
                    local inlawSpouse = nil
                    if string.match(key, "_inlaw_") then
                        baseKey = string.gsub(key, "_inlaw_.*", "")
                        inlawSpouse = string.match(key, "_inlaw_(.+)")
                    end

                    if not groupedDialogues[baseKey] then
                        groupedDialogues[baseKey] = {
                            baseKey = baseKey,
                            displayKey = formatOtherDialogueKey(baseKey),
                            sortKey = generateSortKey(baseKey),
                            mainDialogue = nil,
                            inlawDialogues = {}
                        }
                    end

                    if inlawSpouse then
                        groupedDialogues[baseKey].inlawDialogues[inlawSpouse] = dialogue
                    else
                        groupedDialogues[baseKey].mainDialogue = dialogue
                    end
                end

                -- 收集本季节的年份对话
                for yearKey, yearItems in pairs(yearDialogues) do
                    local yearSeason, yearWeekday = string.match(yearKey, "^([a-z]+)_([A-Z][a-z][a-z])$")
                    if yearSeason == season then
                        seasonYearDialogues[yearWeekday] = yearItems
                    end
                end

                -- 转换为排序数组
                for _, group in pairs(groupedDialogues) do
                    table.insert(seasonDialogues, group)
                end

                -- 按排序键排序
                table.sort(seasonDialogues, function(a, b)
                    return a.sortKey < b.sortKey
                end)

                -- 初始化已处理星期的记录
                local processedWeekdays = {}

                for _, group in ipairs(seasonDialogues) do
                    -- 如果不是简单的季节键(需要标注),则添加标注
                    if group.baseKey ~= season and not string.match(group.baseKey, "^" .. season .. "$") then
                        -- 检查是否为心级对话,如果是则不加粗
                        if string.match(group.baseKey, "^[a-z]+_[A-Z][a-z][a-z]%d+$") then
                            -- 心级对话:检查是否存在对应的基础对话
                            local baseSeason, baseWeekday = string.match(group.baseKey, "^([a-z]+)_([A-Z][a-z][a-z])%d+$")
                            local baseDialogueKey = baseSeason .. "_" .. baseWeekday
                            local hasBaseDialogue = false
                            
                            -- 检查是否存在基础对话
                            for _, checkGroup in ipairs(seasonDialogues) do
                                if checkGroup.baseKey == baseDialogueKey and checkGroup.mainDialogue then
                                    hasBaseDialogue = true
                                    break
                                end
                            end
                            
                            -- 如果没有基础对话,需要补充季节和星期标题
                            if not hasBaseDialogue and baseSeason and baseWeekday then
                                local seasonNames = {
                                    spring = "春季",
                                    summer = "夏季", 
                                    fall = "秋季",
                                    winter = "冬季"
                                }
                                local weekdayNames = {
                                    Mon = "周一",
                                    Tue = "周二",
                                    Wed = "周三",
                                    Thu = "周四",
                                    Fri = "周五",
                                    Sat = "周六",
                                    Sun = "周日"
                                }
                                
                                if seasonNames[baseSeason] and weekdayNames[baseWeekday] then
                                    table.insert(output_temp, "'''" .. seasonNames[baseSeason] .. "," .. weekdayNames[baseWeekday] .. "'''")
                                    table.insert(output_temp, "")
                                end
                            end
                            
                            -- 心级对话不加粗
                            table.insert(output_temp, group.displayKey)
                        else
                            -- 其他对话加粗
                            table.insert(output_temp, "'''" .. group.displayKey .. "'''")
                        end
                        table.insert(output_temp, "")
                    end

                    -- 输出主对话
                    if group.mainDialogue then
                        table.insert(output_temp, formatDialogueText(group.mainDialogue, group.baseKey))
                        table.insert(output_temp, "")
                    end

                    -- 输出_inlaw_对话
                    for spouse, dialogue in pairs(group.inlawDialogues) do
                        local chineseName = '[[' .. getCharacterName(spouse) .. ']]'
                        table.insert(output_temp, "(与" .. chineseName .. "结婚后)")
                        table.insert(output_temp, "")
                        table.insert(output_temp, formatDialogueText(dialogue, group.baseKey .. "_inlaw_" .. spouse))
                        table.insert(output_temp, "")
                    end
                    
                    -- 处理对应的年份对话
                    local weekday = string.match(group.baseKey, "^" .. season .. "_([A-Z][a-z][a-z])$")
                    if not weekday then
                        -- 如果不是基础键,尝试从心级键中提取星期
                        weekday = string.match(group.baseKey, "^" .. season .. "_([A-Z][a-z][a-z])%d+$")
                    end
                    
                    if weekday and seasonYearDialogues[weekday] then
                        -- 按年份排序,年份相同的按心数排序
                        table.sort(seasonYearDialogues[weekday], function(a, b)
                            if a.year ~= b.year then
                                return a.year < b.year
                            end
                            -- 如果年份相同,按心数排序(hearts为nil的排在前面)
                            local heartsA = a.hearts or 0
                            local heartsB = b.hearts or 0
                            return heartsA < heartsB
                        end)
                        
                        for _, yearItem in ipairs(seasonYearDialogues[weekday]) do
                            local chineseNumber = CHINESE_NUMBERS[yearItem.year] or tostring(yearItem.year)
                            local prefix = ""
                            
                            if yearItem.hearts and yearItem.hearts > 0 then
                                -- 复合情况:既有年份又有心数
                                local heartsText = CHINESE_NUMBERS[yearItem.hearts] or tostring(yearItem.hearts)
                                prefix = "(" .. heartsText .. "心、第" .. chineseNumber .. "年)"
                            else
                                -- 只有年份
                                prefix = "(第" .. chineseNumber .. "年)"
                            end
                            
                            table.insert(output_temp, prefix)
                            table.insert(output_temp, "")
                            table.insert(output_temp, formatDialogueText(yearItem.dialogue, nil, npcData))
                            table.insert(output_temp, "")
                        end
                        
                        -- 标记这个星期的年份对话已经被处理
                        processedWeekdays[weekday] = true
                    end
                    
                    -- 记录已经处理过的星期(包括心级对话的星期)
                    local groupWeekday = string.match(group.baseKey, "^" .. season .. "_([A-Z][a-z][a-z])$")
                    if groupWeekday then
                        processedWeekdays[groupWeekday] = true
                    else
                        -- 检查心级对话的星期
                        local heartWeekday = string.match(group.baseKey, "^" .. season .. "_([A-Z][a-z][a-z])%d+$")
                        if heartWeekday then
                            processedWeekdays[heartWeekday] = true
                        end
                    end
                end
                
                -- 处理那些没有对应季节对话的年份对话
                for weekday, yearItems in pairs(seasonYearDialogues) do
                    if not processedWeekdays[weekday] then
                        -- 需要先输出星期标题
                        local weekdayNames = {
                            Mon = "周一",
                            Tue = "周二", 
                            Wed = "周三",
                            Thu = "周四",
                            Fri = "周五",
                            Sat = "周六",
                            Sun = "周日"
                        }
                        
                        if weekdayNames[weekday] then
                            table.insert(output_temp, "'''" .. seasonNames[season] .. "," .. weekdayNames[weekday] .. "'''")
                            table.insert(output_temp, "")
                        end
                        
                        -- 按年份排序,年份相同的按心数排序
                        table.sort(yearItems, function(a, b)
                            if a.year ~= b.year then
                                return a.year < b.year
                            end
                            local heartsA = a.hearts or 0
                            local heartsB = b.hearts or 0
                            return heartsA < heartsB
                        end)
                        
                        for _, yearItem in ipairs(yearItems) do
                            local chineseNumber = CHINESE_NUMBERS[yearItem.year] or tostring(yearItem.year)
                            local prefix = ""
                            
                            if yearItem.hearts and yearItem.hearts > 0 then
                                -- 复合情况:既有年份又有心数
                                local heartsText = CHINESE_NUMBERS[yearItem.hearts] or tostring(yearItem.hearts)
                                prefix = "(" .. heartsText .. "心、第" .. chineseNumber .. "年)"
                            else
                                -- 只有年份
                                prefix = "(第" .. chineseNumber .. "年)"
                            end
                            
                            table.insert(output_temp, prefix)
                            table.insert(output_temp, "")
                            table.insert(output_temp, formatDialogueText(yearItem.dialogue, nil, npcData))
                            table.insert(output_temp, "")
                        end
                    end
                end
            end
        end
        table.insert(output, Helper.ExpandTemplate("Collapse", {
            formatLevel3HeaderInline("季节对话"),
            content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
        }))
    end

    -- 输出酒吧对话(单独的折叠块)

    if next(saloonDialogues) then
        local saloon_output_temp = {}

        -- 分类酒吧对话
        local saloonGeneral = {}
        local saloonWeekly = {}
        local saloonHearts = {}

        for key, dialogue in pairs(saloonDialogues) do
            if key == "Saloon" then
                saloonGeneral[key] = dialogue
            elseif string.match(key, "^Saloon_([A-Z][a-z][a-z])$") then
                local weekday = string.match(key, "^Saloon_([A-Z][a-z][a-z])$")
                if not saloonWeekly[weekday] then
                    saloonWeekly[weekday] = {}
                end
                saloonWeekly[weekday][key] = dialogue
            elseif string.match(key, "^Saloon%d+$") then
                local hearts = string.match(key, "^Saloon(%d+)$")
                saloonHearts[tonumber(hearts) or 0] = {
                    key = key,
                    dialogue = dialogue
                }
            end
        end

        -- 先输出普通情况
        if saloonGeneral["Saloon"] then
            table.insert(saloon_output_temp, formatDialogueText(saloonGeneral["Saloon"]))
            table.insert(saloon_output_temp, "")
        end

        -- 按星期输出
        local weekOrder = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
        local weekdayNames = {
            Mon = "周一",
            Tue = "周二",
            Wed = "周三",
            Thu = "周四",
            Fri = "周五",
            Sat = "周六",
            Sun = "周日"
        }

        for _, weekday in ipairs(weekOrder) do
            if saloonWeekly[weekday] then
                table.insert(saloon_output_temp, "'''" .. weekdayNames[weekday] .. "'''")
                table.insert(saloon_output_temp, "")
                for key, dialogue in pairs(saloonWeekly[weekday]) do
                    table.insert(saloon_output_temp, formatDialogueText(dialogue))
                    table.insert(saloon_output_temp, "")
                end
            end
        end

        -- 按好感度输出
        local heartsOrder = {4, 8} -- 通常是四心和八心
        local heartsNames = {
            [4] = (CHINESE_NUMBERS[4] or "四") .. "心",
            [8] = (CHINESE_NUMBERS[8] or "八") .. "心"
        }

        for _, hearts in ipairs(heartsOrder) do
            if saloonHearts[hearts] then
                table.insert(saloon_output_temp, "'''" .. heartsNames[hearts] .. "'''")
                table.insert(saloon_output_temp, "")
                table.insert(saloon_output_temp, formatDialogueText(saloonHearts[hearts].dialogue))
                table.insert(saloon_output_temp, "")
            end
        end

        table.insert(output, Helper.ExpandTemplate("Collapse", {
            formatLevel3HeaderInline("[[星之果实餐吧|餐吧]]"),
            content = table.concat(removeConsecutiveEmptyStrings(saloon_output_temp), "\n")
        }))
    end

    -- 输出日期对话(Generic dialogue中的dayOfMonth格式)
    if next(dateDialogues) then
        local date_output_temp = {}

        -- 按日期排序
        local sortedDates = {}
        for key, dialogue in pairs(dateDialogues) do
            local day, year = string.match(key, "^(%d+)_?(%d*)$")
            local dayWithStar = string.match(key, "^(%d+)_%*$")
            
            if dayWithStar then
                -- 处理 4_*、8_*、15_*、18_* 格式
                table.insert(sortedDates, {
                    key = key,
                    dialogue = dialogue,
                    day = tonumber(dayWithStar),
                    year = nil,
                    isAnyYear = true  -- 标记为不限年份
                })
            else
                table.insert(sortedDates, {
                    key = key,
                    dialogue = dialogue,
                    day = tonumber(day),
                    year = year ~= "" and tonumber(year) or nil,
                    isAnyYear = false
                })
            end
        end

        table.sort(sortedDates, function(a, b)
            if a.day ~= b.day then
                return a.day < b.day
            end
            if a.year and b.year then
                return a.year < b.year
            elseif a.year then
                return true -- 有年份的排在前面
            else
                return false
            end
        end)

        for _, item in ipairs(sortedDates) do
            local displayKey
            if item.isAnyYear then
                -- 对于 4_*、8_*、15_*、18_* 格式,显示为 "4 日"、"8 日" 等
                displayKey = item.day .. " 日"
            else
                displayKey = item.day .. " 日"
                if item.year then
                    displayKey = displayKey .. "(第 " .. item.year .. " 年)"
                else
                    displayKey = displayKey .. "(仅第一年)"
                end
            end
            table.insert(date_output_temp, "'''" .. displayKey .. "'''")
            table.insert(date_output_temp, "")
            table.insert(date_output_temp, formatDialogueText(item.dialogue))
            table.insert(date_output_temp, "")
        end

        table.insert(output, Helper.ExpandTemplate("Collapse", {
            formatLevel3HeaderInline("特定日期"),
            content = table.concat(removeConsecutiveEmptyStrings(date_output_temp), "\n")
        }))
    end

    -- 输出其他日常对话(不包括酒吧对话)

    local output_temp = {}
    if true then
        -- 处理其他对话(需要格式化标注)
        if next(others) then
            table.insert(output_temp, "")

            -- 分离Fair_对话和其他对话
            local fairDialogues = {}
            local regularOthers = {}

            for key, dialogue in pairs(others) do
                if string.match(key, "^Fair_") then
                    fairDialogues[key] = dialogue
                else
                    regularOthers[key] = dialogue
                end
            end

            -- 处理Fair_对话(展览会)
            if next(fairDialogues) then
                table.insert(output_temp, "'''展览会'''")
                table.insert(output_temp, "")

                -- 按指定顺序排序Fair_对话
                local fairOrder = {"Fair_Judging", "Fair_Judged", "Fair_Judged_PlayerWon", "Fair_Judged_PlayerLost",
                                   "Fair_Judged_PlayerLost_PurpleShorts"}

                for _, fairKey in ipairs(fairOrder) do
                    if fairDialogues[fairKey] then
                        local displayKey = formatOtherDialogueKey(fairKey)
                        table.insert(output_temp, displayKey)
                        table.insert(output_temp, "")
                        table.insert(output_temp, formatDialogueText(fairDialogues[fairKey], fairKey))
                        table.insert(output_temp, "")
                    end
                end
            end

            -- 处理其他常规对话
            if next(regularOthers) then
                -- 格式化其他对话的标注
                local formattedOthers = {}
                for key, dialogue in pairs(regularOthers) do
                    local displayKey = formatOtherDialogueKey(key)
                    table.insert(formattedOthers, {
                        key = key,
                        dialogue = dialogue,
                        displayKey = displayKey,
                        sortKey = generateSortKey(key)
                    })
                end

                -- 按时间顺序+好感度排序
                table.sort(formattedOthers, function(a, b)
                    return a.sortKey < b.sortKey
                end)

                for _, item in ipairs(formattedOthers) do
                    table.insert(output_temp, "'''" .. item.displayKey .. "'''")
                    table.insert(output_temp, "")
                    table.insert(output_temp, formatDialogueText(item.dialogue, item.key))
                    table.insert(output_temp, "")
                end
            end
        end
        if next(others) then
            table.insert(output, Helper.ExpandTemplate("Collapse", {
                formatLevel3HeaderInline("其他日常对话"),
                content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
            }))
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成节日对话输出
local last_subtitle = ''
local function formatFestivalDialogues(dialogues, specialDialogues, npcName)
    local output = {}
    local festivalGroups = {}

    -- 提取schedule中的夜市对话(排除体检日)
    local nightMarketDialogues = {}
    if specialDialogues then
        -- 定义体检日期表(与formatSpecialDialogues中保持一致)
        local checkupDates = {
            Alex = {"summer_16"},
            Sebastian = {"summer_4"},
            Sam = {"fall_11"},
            Haley = {"winter_9"},
            Penny = {"winter_4"},
            Elliott = {"summer_9"},
            Emily = {"winter_11"},
            Leah = {"spring_16"},
            Abigail = {"spring_4"},
            George = {"spring_23", "summer_23", "fall_23", "winter_23"},
            Jodi = {"spring_11", "spring_18"},
            Clint = {"winter_16"},
            Lewis = {"fall_9"},
            Caroline = {"fall_25"},
            Willy = {"spring_9"},
            Demetrius = {"summer_25"},
            Vincent = {"spring_11"},
            Gus = {"fall_4"},
            Pam = {"spring_25"},
            Marnie = {"fall_18", "winter_18"},
            Robin = {"summer_18"},
            Evelyn = {"spring_2", "summer_2", "fall_2", "winter_2", "spring_23", "summer_23", "fall_23", "winter_23"},
            Jas = {"winter_18"}
        }

        for key, dialogue in pairs(specialDialogues) do
            if string.match(key, "^schedule_winter_1[567]%.") then
                local cleanKey = string.gsub(key, "^schedule_", "")

                -- 检查是否为体检日对话
                local isCheckup = false
                if npcName and checkupDates[npcName] then
                    for _, checkupDate in ipairs(checkupDates[npcName]) do
                        -- 匹配带季节前缀的键(如"spring_23.000")
                        if string.match(cleanKey, "^" .. checkupDate .. "%.") then
                            isCheckup = true
                            break
                        end
                        -- 匹配纯数字键(如"23.000"),用于乔治和艾芙琳等全季节体检的角色
                        local dayOnly = string.match(checkupDate, "_(%d+)$")
                        if dayOnly and string.match(cleanKey, "^" .. dayOnly .. "%.") then
                            isCheckup = true
                            break
                        end
                    end
                end

                -- 只有非体检日的对话才作为夜市对话
                if not isCheckup then
                    nightMarketDialogues[cleanKey] = dialogue
                end
            end
        end
    end
    for key, dialogue in pairs(dialogues) do
        local festivalName = nil

        -- 识别节日类型
        if string.match(key, "^DesertFestival_") then
            festivalName = "沙漠节"
        elseif string.match(key, "^TroutDerby_") then
            festivalName = "鳟鱼大赛"
        elseif string.match(key, "^SquidFest_") then
            festivalName = "鱿鱼节"
        elseif string.match(key, "^SquidFest$") then
            festivalName = "鱿鱼节"
        elseif string.match(key, "FlowerDance") then
            festivalName = "花舞节"
        elseif string.match(key, "wonEggHunt") then
            festivalName = "复活节"
        elseif string.match(key, "spring_12") then
            festivalName = "复活节"
        elseif string.match(key, "spring_23") then
            festivalName = "花舞节"
        elseif string.match(key, "summer_10") then
            festivalName = "夏威夷宴会"
        elseif string.match(key, "summer_19") then
            festivalName = "鳟鱼大赛"
        elseif string.match(key, "summer_20") then
            festivalName = "鳟鱼大赛"
        elseif string.match(key, "summer_21") then
            festivalName = "鳟鱼大赛"
        elseif string.match(key, "summer_27") then
            festivalName = "月光水母起舞"
        elseif string.match(key, "fall_15") then
            festivalName = "星露谷展览会"
        elseif string.match(key, "fall_26") then
            festivalName = "万灵节"
        elseif string.match(key, "winter_7") then
            festivalName = "冰雪节"
        elseif string.match(key, "winter_11") then
            festivalName = "鱿鱼节"
        elseif string.match(key, "winter_12") then
            festivalName = "鱿鱼节"
        elseif string.match(key, "winter_13") then
            festivalName = "鱿鱼节"
        elseif string.match(key, "winter_24") then
            festivalName = "冬日星盛宴"
        elseif string.match(key, "^Festivals/spring13") then
            festivalName = "复活节"
        elseif string.match(key, "^Festivals/spring24") then
            festivalName = "花舞节"
        elseif string.match(key, "^Festivals/summer11") then
            festivalName = "夏威夷宴会"
        elseif string.match(key, "^Festivals/summer28") then
            festivalName = "月光水母起舞"
        elseif string.match(key, "^Festivals/fall16") then
            festivalName = "星露谷展览会"
        elseif string.match(key, "^Festivals/fall27") then
            festivalName = "万灵节"
        elseif string.match(key, "^Festivals/winter8") then
            festivalName = "冰雪节"
        elseif string.match(key, "^Festivals/winter25") then
            festivalName = "冬日星盛宴"
            -- 处理Wiki文档中定义的Festival dialogue键格式
        elseif string.match(key, "^Fair_Judging$") or string.match(key, "^Fair_Judged") then
            festivalName = "星露谷展览会"
        elseif string.match(key, "^FlowerDance_Accept") or string.match(key, "^FlowerDance_Decline$") or
            string.match(key, "^danceRejection$") then
            festivalName = "花舞节"
        elseif string.match(key, "^WinterStar_GiveGift") or string.match(key, "^WinterStar_ReceiveGift") then
            festivalName = "冬日星盛宴"
        elseif string.match(key, "^Shop_") then
            -- Shop_对话需要归到对应的节日,这里先判断是沙漠节
            festivalName = "沙漠节"
        else
            -- 其他节日对话
            festivalName = "其他节日"
        end

        if not festivalGroups[festivalName] then
            festivalGroups[festivalName] = {}
        end
        festivalGroups[festivalName][key] = dialogue
    end

    -- 处理夜市对话
    if next(nightMarketDialogues) then
        if not festivalGroups["夜市"] then
            festivalGroups["夜市"] = {}
        end
        for key, dialogue in pairs(nightMarketDialogues) do
            festivalGroups["夜市"][key] = dialogue
        end
    end

    -- 特殊角色的节日对话过滤逻辑
    if npcName then
        -- 桑迪/Sandy:只保留沙漠节、星露谷展览会(偶数年)
        if npcName == "Sandy" then
            local allowedFestivals = {}
            
            -- 保留沙漠节的所有对话
            if festivalGroups["沙漠节"] then
                allowedFestivals["沙漠节"] = festivalGroups["沙漠节"]
            end
            
            -- 保留星露谷展览会的偶数年对话
            if festivalGroups["星露谷展览会"] then
                local filteredFair = {}
                for key, dialogue in pairs(festivalGroups["星露谷展览会"]) do
                    -- 只保留偶数年对话(_y2后缀)和特殊Fair_对话(不分年份)
                    if string.match(key, "_y2$") or string.match(key, "^Fair_") then
                        filteredFair[key] = dialogue
                    end
                end
                if next(filteredFair) then
                    allowedFestivals["星露谷展览会"] = filteredFair
                end
            end
            
            festivalGroups = allowedFestivals
            
        -- 矮人/Dwarf:只保留星露谷展览会(偶数年)、万灵节(偶数年)
        elseif npcName == "Dwarf" then
            local allowedFestivals = {}
            
            -- 保留星露谷展览会的偶数年对话
            if festivalGroups["星露谷展览会"] then
                local filteredFair = {}
                for key, dialogue in pairs(festivalGroups["星露谷展览会"]) do
                    -- 只保留偶数年对话(_y2后缀)和特殊Fair_对话(不分年份)
                    if string.match(key, "_y2$") or string.match(key, "^Fair_") then
                        filteredFair[key] = dialogue
                    end
                end
                if next(filteredFair) then
                    allowedFestivals["星露谷展览会"] = filteredFair
                end
            end
            
            -- 保留万灵节的偶数年对话
            if festivalGroups["万灵节"] then
                local filteredHalloween = {}
                for key, dialogue in pairs(festivalGroups["万灵节"]) do
                    -- 只保留偶数年对话(_y2后缀)
                    if string.match(key, "_y2$") then
                        filteredHalloween[key] = dialogue
                    end
                end
                if next(filteredHalloween) then
                    allowedFestivals["万灵节"] = filteredHalloween
                end
            end
            
            festivalGroups = allowedFestivals
        end
    end

    -- 按节日时间顺序输出
    for _, festival in ipairs(FESTIVALS) do
        if festivalGroups[festival.name] then
            table.insert(output, formatLevel3Header('[[' .. festival.name .. ']]'))
            table.insert(output, "")

            local items = {}
            for key, dialogue in pairs(festivalGroups[festival.name]) do
                table.insert(items, {
                    key = key,
                    dialogue = dialogue
                })
            end

            -- 检查是否存在预告对话(用于所有节日)
            local hasPreview = false
            local previewKeys = {
                ["复活节"] = "spring_12",
                ["花舞节"] = "spring_23",
                ["夏威夷宴会"] = "summer_10",
                ["鳟鱼大赛"] = "summer_19",
                ["月光水母起舞"] = "summer_27",
                ["星露谷展览会"] = "fall_15",
                ["万灵节"] = "fall_26",
                ["冰雪节"] = "winter_7",
                ["鱿鱼节"] = "winter_11",
                ["冬日星盛宴"] = "winter_24"
            }

            if previewKeys[festival.name] then
                for _, item in ipairs(items) do
                    if string.match(item.key, previewKeys[festival.name]) then
                        hasPreview = true
                        break
                    end
                end
            end

            -- 特殊处理不同节日的排序
            if festival.name == "复活节" or festival.name == "夏威夷宴会" or festival.name ==
                "月光水母起舞" or festival.name == "万灵节" or festival.name == "冰雪节" or festival.name ==
                "冬日星盛宴" or festival.name == "花舞节" or festival.name == "鳟鱼大赛" or festival.name ==
                "鱿鱼节" then
                -- 使用通用节日排序:预告 - 一般对话 - 偶数年 - 配偶 - 特殊对话
                table.sort(items, function(a, b)
                    local aPriority = getFestivalPriority(a.key, festival.name)
                    local bPriority = getFestivalPriority(b.key, festival.name)
                    return aPriority < bPriority
                end)
            elseif festival.name == "沙漠节" then
                table.sort(items, function(a, b)
                    -- 商铺对话排在最后
                    local aIsShop = string.match(a.key, "Shop_")
                    local bIsShop = string.match(b.key, "Shop_")

                    if aIsShop and not bIsShop then
                        return false -- 商铺对话排在后面
                    elseif not aIsShop and bIsShop then
                        return true -- 一般对话排在前面
                    elseif aIsShop and bIsShop then
                        return a.key < b.key -- 商铺对话之间按键名排序
                    else
                        -- 沙漠节特殊排序:一般对话 - 婚后 - 数字后缀
                        local aIsMarriage = string.match(a.key, "_marriage$")
                        local bIsMarriage = string.match(b.key, "_marriage$")

                        if aIsMarriage and not bIsMarriage then
                            return false -- 婚后对话排在一般对话后面
                        elseif not aIsMarriage and bIsMarriage then
                            return true -- 一般对话排在婚后对话前面
                        else
                            -- 处理沙漠节的数字后缀
                            local aNum = string.match(a.key, "DesertFestival_[^%d]*(%d+)$")
                            local bNum = string.match(b.key, "DesertFestival_[^%d]*(%d+)$")

                            if aNum and bNum then
                                return tonumber(aNum) < tonumber(bNum)
                            elseif aNum then
                                return false -- 有数字的排在后面
                            elseif bNum then
                                return true
                            else
                                -- 标准排序:无后缀 - _y2 - _spouse - _spouse_y2
                                local aPriority = getStandardFestivalPriority(a.key)
                                local bPriority = getStandardFestivalPriority(b.key)
                                return aPriority < bPriority
                            end
                        end
                    end
                end)
            elseif festival.name == "夜市" then
                -- 夜市特殊排序:按日期和体检日优先级
                table.sort(items, function(a, b)
                    local aDay = string.match(a.key, "winter_1([567])")
                    local bDay = string.match(b.key, "winter_1([567])")

                    if aDay and bDay then
                        return tonumber(aDay) < tonumber(bDay)
                    end
                    return a.key < b.key
                end)
            elseif festival.name == "星露谷展览会" then
                -- 展览会特殊排序:日常对话在最上面,然后按指定顺序排序非日常对话
                table.sort(items, function(a, b)
                    local aIsFair = string.match(a.key, "^Fair_")
                    local bIsFair = string.match(b.key, "^Fair_")

                    -- 日常对话(非Fair_开头)排在前面
                    if not aIsFair and bIsFair then
                        return true
                    elseif aIsFair and not bIsFair then
                        return false
                    elseif not aIsFair and not bIsFair then
                        -- 两个都是日常对话,使用标准排序
                        local aPriority = getStandardFestivalPriority(a.key)
                        local bPriority = getStandardFestivalPriority(b.key)
                        return aPriority < bPriority
                    else
                        -- 两个都是Fair_对话,按指定顺序排序
                        local fairOrder = {"Fair_Judging", "Fair_Judged", "Fair_Judged_PlayerWon",
                                           "Fair_Judged_PlayerLost", "Fair_Judged_PlayerLost_PurpleShorts"}
                        local aIndex = 999
                        local bIndex = 999

                        for i, fairKey in ipairs(fairOrder) do
                            if a.key == fairKey then
                                aIndex = i
                            end
                            if b.key == fairKey then
                                bIndex = i
                            end
                        end

                        return aIndex < bIndex
                    end
                end)
            else
                -- 其他节日的标准排序:无后缀 - _y2 - _spouse - _spouse_y2
                table.sort(items, function(a, b)
                    local aPriority = getStandardFestivalPriority(a.key)
                    local bPriority = getStandardFestivalPriority(b.key)
                    return aPriority < bPriority
                end)
            end

            for _, item in ipairs(items) do
                -- 添加小标题
                local subtitle
                local supportPreviewFestivals = {
                    ["复活节"] = true,
                    ["夏威夷宴会"] = true,
                    ["月光水母起舞"] = true,
                    ["星露谷展览会"] = true,
                    ["万灵节"] = true,
                    ["冰雪节"] = true,
                    ["冬日星盛宴"] = true,
                    ["花舞节"] = true
                }

                if supportPreviewFestivals[festival.name] then
                    subtitle = getFestivalSubtitle(item.key, festival.name, hasPreview)
                else
                    subtitle = getFestivalSubtitle(item.key, festival.name)
                end

                -- 处理鳟鱼大赛和鱿鱼节的"比赛期间"特殊逻辑
                if subtitle == "TROUT_DERBY_GENERAL" then
                    -- 检查是否存在预告/第一天/第二天任意一条
                    local hasSpecialDays = false
                    for _, checkItem in ipairs(items) do
                        if string.match(checkItem.key, "summer_19") or 
                           string.match(checkItem.key, "summer_20") or 
                           string.match(checkItem.key, "summer_21") then
                            hasSpecialDays = true
                            break
                        end
                    end
                    subtitle = hasSpecialDays and "比赛期间" or nil
                elseif subtitle == "SQUID_FEST_GENERAL" then
                    -- 检查是否存在预告/第一天/第二天任意一条
                    local hasSpecialDays = false
                    for _, checkItem in ipairs(items) do
                        if string.match(checkItem.key, "winter_11") or 
                           string.match(checkItem.key, "winter_12") or 
                           string.match(checkItem.key, "winter_13") then
                            hasSpecialDays = true
                            break
                        end
                    end
                    subtitle = hasSpecialDays and "比赛期间" or nil
                end

                if subtitle then
                    if subtitle ~= last_subtitle then
                        last_subtitle = subtitle
                        table.insert(output, subtitle)
                        table.insert(output, "")
                    end
                else
                    last_subtitle = ""
                end

                table.insert(output, formatDialogueText(item.dialogue))
                table.insert(output, "")
            end

            festivalGroups[festival.name] = nil -- 标记已处理
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成关系变化对话输出
local function formatRelationshipDialogues(dialogues)
    -- mw.log("DEBUG: formatRelationshipDialogues started")
    mw.logObject(dialogues, "DEBUG: Input dialogues")

    local output = {}
    local relationshipGroups = {}
    local generalRelationships = {}

    -- 按关系类型和人名分组
    for key, dialogue in pairs(dialogues) do
        local relationType = nil
        local npcName = nil

        if string.match(key, "^dating_") and not string.match(key, "memory_") then
            relationType = "约会"
            -- 对于 dating_Shane_memory_oneday 这样的键,只提取Shane部分
            local afterDating = string.match(key, "^dating_(.+)")
            if afterDating then
                -- 如果包含_memory_,则只取memory之前的部分
                npcName = string.match(afterDating, "^([^_]+)_memory_") or afterDating
            end
        elseif string.match(key, "^married_") and not string.match(key, "memory_") then
            relationType = "结婚"
            -- 对于 married_Shane_memory_oneday 这样的键,只提取Shane部分
            local afterMarried = string.match(key, "^married_(.+)")
            if afterMarried then
                -- 如果包含_memory_,则只取memory之前的部分
                npcName = string.match(afterMarried, "^([^_]+)_memory_") or afterMarried
            end

            if npcName == "twice" then
                npcName = "通用"
                relationType = "再次结婚"
            end

            -- 特殊处理:married_memory_oneweek 是没有NPC名称的通用对话
            if key == "married_memory_oneweek" then
                npcName = "通用"
                relationType = "结婚(1 周后)"
            end
        elseif string.match(key, "^married$") then
            -- 非可攻略人物的结婚后对话(如Gus的married对话)
            relationType = "结婚后"
            npcName = "通用" -- 这类对话通常是通用的,不针对特定NPC
        elseif string.match(key, "^engagement_") then
            -- 处理订婚对话
            local afterEngagement = string.match(key, "^engagement_(.+)")
            if afterEngagement then
                -- 从engagement_Abigail0这样的键中提取Abigail
                npcName = string.match(afterEngagement, "^([A-Za-z]+)")
                if npcName == "Krobus" then
                    relationType = "成为室友"
                else
                    relationType = "订婚"
                end
            end
        elseif string.match(key, "^divorced") then
            relationType = "离婚"
            -- 提取离婚对象的NPC名称
            local afterDivorced = string.match(key, "^divorced_(.+)")
            -- 特殊处理:once和twice不是人名,而是次数
            if afterDivorced == "once" or afterDivorced == "twice" then
                npcName = "通用"
            else
                npcName = afterDivorced or "通用"
            end
        elseif string.match(key, "^dumped_") then
            relationType = "[[花束#群体10心事件|群体" .. (CHINESE_NUMBERS[10] or "十") .. "心事件]]后"
            npcName = string.match(key, "^dumped_(.+)")
            -- mw.log("DEBUG: Processing dumped_ dialogue - key: " .. key .. ", npcName: " .. (npcName or "nil") .. ", relationType: " .. relationType)
        elseif string.match(key, "^secondChance_") then
            relationType = "原谅后"
            npcName = string.match(key, "^secondChance_(.+)")
        elseif string.match(key, "^breakUp") then
            relationType = "分手后"
            npcName = "通用"
        elseif key == "married" then
            relationType = "结婚后"
            npcName = "通用"
        end

        if relationType and npcName then
            -- Guys和Girls是通用的,不是特定人名
            if npcName == "Guys" or npcName == "Girls" or npcName == "通用" then
                -- mw.log("DEBUG: Adding to generalRelationships - relationType: " .. relationType .. ", key: " .. key .. ", npcName: " .. npcName)
                if not generalRelationships[relationType] then
                    generalRelationships[relationType] = {}
                end
                generalRelationships[relationType][key] = dialogue
            else
                if not relationshipGroups[npcName] then
                    relationshipGroups[npcName] = {}
                end
                -- 对于订婚和成为室友对话,可能有多个,需要用表来存储
                if relationType == "订婚" or relationType == "成为室友" then
                    if not relationshipGroups[npcName][relationType] then
                        relationshipGroups[npcName][relationType] = {}
                    end
                    table.insert(relationshipGroups[npcName][relationType], dialogue)
                else
                    relationshipGroups[npcName][relationType] = dialogue
                end
            end
        end
    end
    mw.logObject(relationshipGroups, "DEBUG: relationshipGroups")

    -- 处理记忆对话 - 按正确格式输出,区分约会和结婚
    local datingMemoryDialogues = {}
    local marriedMemoryDialogues = {}

    for key, dialogue in pairs(dialogues) do
        if string.match(key, "_memory_") then
            -- 对于 dating_Shane_memory_oneday 或 married_Shane_memory_oneday 这样的键
            -- 提取前缀、NPC名称和时间框架
            local prefix, npcName, timeFrame = string.match(key, "^(.-)_([^_]+)_memory_(.+)$")
            if prefix and npcName and timeFrame then
                if prefix == "dating" then
                    -- 约会记忆对话
                    if not datingMemoryDialogues[npcName] then
                        datingMemoryDialogues[npcName] = {}
                    end
                    datingMemoryDialogues[npcName][timeFrame] = dialogue
                elseif prefix == "married" then
                    -- 结婚记忆对话
                    if not marriedMemoryDialogues[npcName] then
                        marriedMemoryDialogues[npcName] = {}
                    end
                    marriedMemoryDialogues[npcName][timeFrame] = dialogue
                end
            end
        end
    end

    -- 按人名分组输出特定NPC的关系变化
    local sortedNpcs = {}
    for npcName, _ in pairs(relationshipGroups) do
        table.insert(sortedNpcs, npcName)
    end
    table.sort(sortedNpcs)

    for _, npcName in ipairs(sortedNpcs) do
        local group = relationshipGroups[npcName]
        local chineseName = '[[' .. getCharacterName(npcName) .. ']]'
        -- 约会
        if group["约会"] then
            table.insert(output, formatLevel3Header("与" .. chineseName .. "约会"))
            table.insert(output, "")
            table.insert(output, formatDialogueText(group["约会"]))
            table.insert(output, "")
        end

        -- 约会一天后(使用约会memory对话)
        if datingMemoryDialogues[npcName] then
            -- 处理不同时间的约会记忆对话
            if datingMemoryDialogues[npcName]["oneday"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "约会(1 天后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(datingMemoryDialogues[npcName]["oneday"]))
                table.insert(output, "")
            end

            if datingMemoryDialogues[npcName]["oneweek"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "约会(1 周后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(datingMemoryDialogues[npcName]["oneweek"]))
                table.insert(output, "")
            end

            if datingMemoryDialogues[npcName]["twoweeks"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "约会(2 周后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(datingMemoryDialogues[npcName]["twoweeks"]))
                table.insert(output, "")
            end

            if datingMemoryDialogues[npcName]["fourweeks"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "约会(4 周后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(datingMemoryDialogues[npcName]["fourweeks"]))
                table.insert(output, "")
            end

            if datingMemoryDialogues[npcName]["oneyear"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "约会(1 年后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(datingMemoryDialogues[npcName]["oneyear"]))
                table.insert(output, "")
            end
        end

        -- 订婚或成为室友(在记忆对话之后)
        if group["订婚"] then
            table.insert(output, formatLevel3Header("与" .. chineseName .. "订婚"))
            table.insert(output, "")
            -- 处理多个订婚对话
            if type(group["订婚"]) == "table" then
                for _, dialogue in ipairs(group["订婚"]) do
                    table.insert(output, formatDialogueText(dialogue))
                    table.insert(output, "")
                end
            else
                table.insert(output, formatDialogueText(group["订婚"]))
                table.insert(output, "")
            end
        elseif group["成为室友"] then
            table.insert(output, formatLevel3Header("与" .. chineseName .. "成为室友"))
            table.insert(output, "")
            -- 处理多个成为室友对话
            if type(group["成为室友"]) == "table" then
                for _, dialogue in ipairs(group["成为室友"]) do
                    table.insert(output, formatDialogueText(dialogue))
                    table.insert(output, "")
                end
            else
                table.insert(output, formatDialogueText(group["成为室友"]))
                table.insert(output, "")
            end
        end

        -- 结婚
        if group["结婚"] then
            table.insert(output, formatLevel3Header("与" .. chineseName .. "结婚"))
            table.insert(output, "")
            table.insert(output, formatDialogueText(group["结婚"]))
            table.insert(output, "")
        end

        -- 结婚后的记忆对话(使用结婚memory对话)
        if marriedMemoryDialogues[npcName] then
            -- 处理不同时间的结婚记忆对话
            if marriedMemoryDialogues[npcName]["oneday"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "结婚(1 天后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(marriedMemoryDialogues[npcName]["oneday"]))
                table.insert(output, "")
            end

            if marriedMemoryDialogues[npcName]["oneweek"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "结婚(1 周后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(marriedMemoryDialogues[npcName]["oneweek"]))
                table.insert(output, "")
            end

            if marriedMemoryDialogues[npcName]["twoweeks"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "结婚(2 周后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(marriedMemoryDialogues[npcName]["twoweeks"]))
                table.insert(output, "")
            end

            if marriedMemoryDialogues[npcName]["fourweeks"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "结婚(4 周后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(marriedMemoryDialogues[npcName]["fourweeks"]))
                table.insert(output, "")
            end

            if marriedMemoryDialogues[npcName]["oneyear"] then
                table.insert(output, formatLevel3Header("与" .. chineseName .. "结婚(1 年后)"))
                table.insert(output, "")
                table.insert(output, formatDialogueText(marriedMemoryDialogues[npcName]["oneyear"]))
                table.insert(output, "")
            end
        end

        -- 结婚后(非可攻略人物的结婚后对话)
        if group["结婚后"] then
            table.insert(output, formatLevel3Header("结婚后"))
            table.insert(output, "")
            table.insert(output, formatDialogueText(group["结婚后"]))
            table.insert(output, "")
        end

        -- 离婚
        if group["离婚"] then
            table.insert(output, formatLevel3Header("与" .. chineseName .. "离婚后"))
            table.insert(output, "")
            table.insert(output, formatDialogueText(group["离婚"]))
            table.insert(output, "")
        end
    end

    -- 输出通用关系变化对话
    mw.logObject(generalRelationships, "DEBUG: generalRelationships content")
    if next(generalRelationships) then
        local relationOrder =
            {"[[花束#群体10心事件|群体" .. (CHINESE_NUMBERS[10] or "十") .. "心事件]]后", "原谅后",
             "分手后", "结婚", "结婚(1 周后)", "再次结婚", "离婚"}
        for _, relationType in ipairs(relationOrder) do
            -- mw.log("DEBUG: Checking relationOrder: " .. relationType)
            if generalRelationships[relationType] then
                mw.log(relationType)
                mw.logObject(generalRelationships[relationType])
                -- mw.log("DEBUG: Found generalRelationships for: " .. relationType)
                if relationType == "离婚" then
                    -- 离婚对话需要特殊处理,每个键都有自己的标题
                    -- 需要按照特定顺序排序:首次离婚在再次离婚前面
                    local divorceKeys = {}
                    for key, dialogue in pairs(generalRelationships[relationType]) do
                        table.insert(divorceKeys, key)
                    end

                    -- 自定义排序函数,确保首次离婚在再次离婚前面
                    table.sort(divorceKeys, function(a, b)
                        local aIsOnce = (a == "divorced_once" or string.match(a, "once"))
                        local bIsOnce = (b == "divorced_once" or string.match(b, "once"))
                        local aIsTwice = (a == "divorced_twice" or string.match(a, "twice"))
                        local bIsTwice = (b == "divorced_twice" or string.match(b, "twice"))

                        -- 如果一个是once,一个是twice,once排在前面
                        if aIsOnce and bIsTwice then
                            return true
                        elseif aIsTwice and bIsOnce then
                            return false
                        else
                            -- 其他情况按字母顺序排序
                            return a < b
                        end
                    end)

                    for _, key in ipairs(divorceKeys) do
                        local dialogue = generalRelationships[relationType][key]
                        local displayName = formatOtherDialogueKey(key)
                        table.insert(output, formatLevel3Header(displayName))
                        table.insert(output, "")
                        table.insert(output, formatDialogueText(dialogue))
                        table.insert(output, "")
                    end
                else
                    table.insert(output, formatLevel3Header(relationType))
                    table.insert(output, "")
                    for key, dialogue in pairs(generalRelationships[relationType]) do
                        table.insert(output, formatDialogueText(dialogue))
                        table.insert(output, "")
                    end
                end
            end
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    -- mw.log("DEBUG: formatRelationshipDialogues completed, output length: " .. #output)
    return table.concat(output, "\n")
end

-- 生成婚后对话输出
local function formatRomanticDialogues(dialogues)
    local output = {}
    local categories = {
        ["白天室内"] = {},
        ["晚上室内"] = {},
        ["雨天日间"] = {},
        ["雨天夜间"] = {},
        ["室外"] = {},
        ["外出"] = {},
        ["关系较好"] = {},
        ["关系一般"] = {},
        ["关系较差"] = {},
        ["特定日期"] = {},
        ["季节配偶对话"] = {},
        ["孩子"] = {
            ["一个孩子"] = {},
            ["两个孩子"] = {}
        },
        ["其他"] = {}
    }

    -- 分类婚后对话
    for key, dialogue in pairs(dialogues) do
        local cleanKey = string.gsub(key, "^Marriage_", "")
        -- 处理schedule_marriage_对话
        if string.match(key, "^schedule_marriage_") then
            cleanKey = string.gsub(key, "^schedule_marriage_", "")
            categories["外出"][key] = dialogue
            -- 处理patio对话
        elseif string.match(cleanKey, "^patio_") then
            categories["室外"][key] = dialogue
        else
            cleanKey = string.gsub(key, "^Marriage_", "")
            local category = "其他" -- 默认分类

            -- 根据关键词判断分类(按照Wiki文档的MarriageDialogue键格式)
            if string.match(cleanKey, "^Indoor_Day") or string.match(cleanKey, "^indoor") then
                category = "白天室内"
            elseif string.match(cleanKey, "^spouseRoom_") then
                category = "白天室内" -- 配偶房间对话归入白天室内
            elseif string.match(cleanKey, "^Indoor_Night") or string.match(cleanKey, "^night") then
                category = "晚上室内"
            elseif string.match(cleanKey, "^Rainy_Day") then
                category = "雨天日间"
            elseif string.match(cleanKey, "^Rainy_Night") then
                category = "雨天夜间"
            elseif string.match(cleanKey, "^Outdoor") then
                category = "室外"
            elseif string.match(cleanKey, "^funLeave") or string.match(cleanKey, "^funReturn") or
                string.match(cleanKey, "^jobLeave") or string.match(cleanKey, "^jobReturn") then
                category = "外出"
            elseif string.match(cleanKey, "^Good") then
                category = "关系较好"
            elseif string.match(cleanKey, "^Neutral") then
                category = "关系一般"
            elseif string.match(cleanKey, "^Bad") then
                category = "关系较差"
            elseif string.match(cleanKey, "^spring_") or string.match(cleanKey, "^summer_") or
                string.match(cleanKey, "^fall_") or string.match(cleanKey, "^winter_") then
                -- 检查是否为季节+配偶格式(如spring_Abigail)或季节+日期格式(如spring_1)
                local season, suffix = string.match(cleanKey, "^(%w+)_(.+)$")
                if season and suffix then
                    -- 如果suffix是数字或*,则为特定日期;否则为季节配偶对话
                    if string.match(suffix, "^%d+$") or string.match(suffix, "^%*$") then
                        category = "特定日期"
                    else
                        category = "季节配偶对话"
                    end
                else
                    category = "特定日期"
                end
            elseif string.match(cleanKey, "^OneKid") then
                categories["孩子"]["一个孩子"][key] = dialogue
                category = nil
            elseif string.match(cleanKey, "^TwoKids") then
                categories["孩子"]["两个孩子"][key] = dialogue
                category = nil
            end

            if category then
                categories[category][key] = dialogue
            end
        end
    end

    -- 输出各个分类
    for _, categoryName in ipairs(MARRIAGE_CATEGORIES) do
        if categoryName == "孩子" then
            -- 特殊处理孩子分类
            if next(categories["孩子"]["一个孩子"]) or next(categories["孩子"]["两个孩子"]) then
                local output_temp = {}
                if next(categories["孩子"]["一个孩子"]) then
                    table.insert(output_temp, "一个孩子")
                    table.insert(output_temp, "")
                    for key, dialogue in pairs(categories["孩子"]["一个孩子"]) do
                        table.insert(output_temp, formatDialogueText(dialogue, key))
                        table.insert(output_temp, "")
                    end
                end

                if next(categories["孩子"]["两个孩子"]) then
                    table.insert(output_temp, "两个孩子")
                    table.insert(output_temp, "")
                    for key, dialogue in pairs(categories["孩子"]["两个孩子"]) do
                        table.insert(output_temp, formatDialogueText(dialogue, key))
                        table.insert(output_temp, "")
                    end
                end
                table.insert(output, Helper.ExpandTemplate("Collapse", {
                    formatLevel3HeaderInline(categoryName),
                    content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
                }))
            end
        elseif categoryName == "特定日期" and next(categories[categoryName]) then
            local output_temp = {}

            -- 格式化特定日期对话
            local dateDialogues = {}
            for key, dialogue in pairs(categories[categoryName]) do
                local cleanKey = string.gsub(key, "^Marriage_", "")
                local formattedDate = formatMarriageDate(cleanKey)
                table.insert(dateDialogues, {
                    key = key,
                    dialogue = dialogue,
                    date = formattedDate,
                    sortKey = cleanKey
                })
            end

            -- 按日期排序(春夏秋冬主排序,日期从小到大次排序,应用优先级规则)
            table.sort(dateDialogues, function(a, b)
                -- 定义季节顺序
                local seasonOrder = {
                    spring = 1,
                    summer = 2,
                    fall = 3,
                    winter = 4
                }

                -- 提取季节和日期/NPC信息
                local aSeason, aDay = string.match(a.sortKey, "^([a-z]+)_(%d+)$")
                local bSeason, bDay = string.match(b.sortKey, "^([a-z]+)_(%d+)$")
                local aSeasonName, aNpcName = string.match(a.sortKey, "^([a-z]+)_([A-Z][a-z]+)$")
                local bSeasonName, bNpcName = string.match(b.sortKey, "^([a-z]+)_([A-Z][a-z]+)$")

                -- 获取季节信息(统一处理)
                local aSeasonKey = aSeason or aSeasonName
                local bSeasonKey = bSeason or bSeasonName

                -- 首先按季节排序(春夏秋冬)
                if aSeasonKey and bSeasonKey and aSeasonKey ~= bSeasonKey then
                    return (seasonOrder[aSeasonKey] or 5) < (seasonOrder[bSeasonKey] or 5)
                end

                -- 如果季节相同,按照MarriageDialogue优先级排序
                local priorityResult = compareMarriageDialoguePriority(a.key, b.key)
                if a.key ~= b.key then
                    return priorityResult
                end

                -- 如果都是日期格式且季节相同,按日期从小到大排序
                if aSeason and aDay and bSeason and bDay and aSeason == bSeason then
                    return tonumber(aDay) < tonumber(bDay)
                end

                -- 如果都是NPC格式且季节相同,按NPC名字排序
                if aSeasonName and aNpcName and bSeasonName and bNpcName and aSeasonName == bSeasonName then
                    return aNpcName < bNpcName
                end

                -- 默认字符串排序
                return a.sortKey < b.sortKey
            end)

            for _, item in ipairs(dateDialogues) do
                table.insert(output_temp, item.date)
                table.insert(output_temp, "")
                table.insert(output_temp, formatDialogueText(item.dialogue))
                table.insert(output_temp, "")
            end
            table.insert(output, Helper.ExpandTemplate("Collapse", {
                formatLevel3HeaderInline(categoryName),
                content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
            }))
        elseif categoryName == "外出" and next(categories[categoryName]) then
            local output_temp = {}
            -- 外出对话需要特殊排序:funLeave/jobLeave在前,funReturn/jobReturn在后,marriage_按星期和序号排序
            local funLeaveDialogues = {}
            local jobLeaveDialogues = {}
            local marriageDialogues = {}
            local funReturnDialogues = {}
            local jobReturnDialogues = {}

            for key, dialogue in pairs(categories[categoryName]) do
                if string.match(key, "funLeave") then
                    table.insert(funLeaveDialogues, {
                        key = key,
                        dialogue = dialogue
                    })
                elseif string.match(key, "jobLeave") then
                    table.insert(jobLeaveDialogues, {
                        key = key,
                        dialogue = dialogue
                    })
                elseif string.match(key, "funReturn") then
                    table.insert(funReturnDialogues, {
                        key = key,
                        dialogue = dialogue
                    })
                elseif string.match(key, "jobReturn") then
                    table.insert(jobReturnDialogues, {
                        key = key,
                        dialogue = dialogue
                    })
                elseif string.match(key, "schedule_marriage_") then
                    table.insert(marriageDialogues, {
                        key = key,
                        dialogue = dialogue
                    })
                end
            end

            -- 排序各类外出对话
            table.sort(funLeaveDialogues, function(a, b)
                return a.key < b.key
            end)
            table.sort(jobLeaveDialogues, function(a, b)
                return a.key < b.key
            end)
            table.sort(funReturnDialogues, function(a, b)
                return a.key < b.key
            end)
            table.sort(jobReturnDialogues, function(a, b)
                return a.key < b.key
            end)

            -- 排序marriage对话:按星期和序号
            table.sort(marriageDialogues, function(a, b)
                local aClean = string.gsub(a.key, "^schedule_marriage_", "")
                local bClean = string.gsub(b.key, "^schedule_marriage_", "")

                local aWeekday, aNum = string.match(aClean, "^([A-Z][a-z][a-z])%.(%d+)$")
                local bWeekday, bNum = string.match(bClean, "^([A-Z][a-z][a-z])%.(%d+)$")

                if aWeekday and bWeekday then
                    if aWeekday == bWeekday then
                        return tonumber(aNum) < tonumber(bNum)
                    else
                        local weekOrder = {
                            Mon = 1,
                            Tue = 2,
                            Wed = 3,
                            Thu = 4,
                            Fri = 5,
                            Sat = 6,
                            Sun = 7
                        }
                        return (weekOrder[aWeekday] or 8) < (weekOrder[bWeekday] or 8)
                    end
                end
                return a.key < b.key
            end)

            -- 输出排序后的对话
            -- 先输出离开对话
            for _, item in ipairs(funLeaveDialogues) do
                table.insert(output_temp, formatDialogueText(item.dialogue))
                table.insert(output_temp, "")
            end

            for _, item in ipairs(jobLeaveDialogues) do
                table.insert(output_temp, formatDialogueText(item.dialogue))
                table.insert(output_temp, "")
            end

            -- 再输出返回对话
            for _, item in ipairs(funReturnDialogues) do
                table.insert(output_temp, formatDialogueText(item.dialogue))
                table.insert(output_temp, "")
            end

            for _, item in ipairs(jobReturnDialogues) do
                table.insert(output_temp, formatDialogueText(item.dialogue))
                table.insert(output_temp, "")
            end

            -- 输出marriage对话,按星期分组
            local currentWeekday = nil
            for _, item in ipairs(marriageDialogues) do
                local cleanKey = string.gsub(item.key, "^schedule_marriage_", "")
                local weekday = string.match(cleanKey, "^([A-Z][a-z][a-z])%.")

                if weekday and weekday ~= currentWeekday then
                    if currentWeekday then
                        table.insert(output_temp, "")
                    end
                    local weekdayName = WEEKDAYS[weekday] or weekday
                    table.insert(output_temp, "'''" .. weekdayName .. "'''")
                    table.insert(output_temp, "")
                    currentWeekday = weekday
                end

                table.insert(output_temp, formatDialogueText(item.dialogue))
                table.insert(output_temp, "")
            end
            table.insert(output, Helper.ExpandTemplate("Collapse", {
                formatLevel3HeaderInline(categoryName),
                content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
            }))
        elseif next(categories[categoryName]) then
            local output_temp = {}
            -- 特殊处理季节配偶对话,按季节分组
            if categoryName == "季节配偶对话" then
                local seasonDialogues = {
                    spring = {},
                    summer = {},
                    fall = {},
                    winter = {}
                }

                for key, dialogue in pairs(categories[categoryName]) do
                    local cleanKey = string.gsub(key, "^Marriage_", "")
                    local season, spouse = string.match(cleanKey, "^(%w+)_(.+)$")
                    if season and seasonDialogues[season] then
                        table.insert(seasonDialogues[season], {
                            key = key,
                            dialogue = dialogue,
                            spouse = spouse
                        })
                    end
                end

                -- 按季节顺序输出
                local seasonOrder = {"spring", "summer", "fall", "winter"}
                local seasonNames = {
                    spring = "春季",
                    summer = "夏季",
                    fall = "秋季",
                    winter = "冬季"
                }

                for _, season in ipairs(seasonOrder) do
                    if next(seasonDialogues[season]) then
                        table.insert(output_temp, "'''" .. seasonNames[season] .. "'''")
                        table.insert(output_temp, "")

                        -- 按配偶名称排序
                        table.sort(seasonDialogues[season], function(a, b)
                            return a.spouse < b.spouse
                        end)

                        for _, item in ipairs(seasonDialogues[season]) do
                            table.insert(output_temp, formatDialogueText(item.dialogue))
                            table.insert(output_temp, "")
                        end
                    end
                end
                -- 特殊处理室外对话,按类型分组
            elseif categoryName == "室外" then
                local outdoorDialogues = {}
                local patioDialogues = {}

                for key, dialogue in pairs(categories[categoryName]) do
                    local cleanKey = string.gsub(key, "^Marriage_", "")
                    if string.match(cleanKey, "^patio_") then
                        table.insert(patioDialogues, {
                            key = key,
                            dialogue = dialogue
                        })
                    else
                        table.insert(outdoorDialogues, {
                            key = key,
                            dialogue = dialogue
                        })
                    end
                end

                -- 按照优先级排序室外对话
                table.sort(outdoorDialogues, function(a, b)
                    return compareMarriageDialoguePriority(a.key, b.key)
                end)

                table.sort(patioDialogues, function(a, b)
                    return compareMarriageDialoguePriority(a.key, b.key)
                end)

                -- 先输出其他室外对话
                for _, item in ipairs(outdoorDialogues) do
                    table.insert(output_temp, formatDialogueText(item.dialogue))
                    table.insert(output_temp, "")
                end

                -- 最后输出patio对话,标注为"活动区"
                if next(patioDialogues) then
                    table.insert(output_temp, "活动区")
                    table.insert(output_temp, "")
                    for _, item in ipairs(patioDialogues) do
                        table.insert(output_temp, formatDialogueText(item.dialogue))
                        table.insert(output_temp, "")
                    end
                end
                -- 特殊处理白天室内对话,将配偶房间对话放在最下面
            elseif categoryName == "白天室内" then
                local indoorDialogues = {}
                local spouseRoomDialogues = {}

                for key, dialogue in pairs(categories[categoryName]) do
                    local cleanKey = string.gsub(key, "^Marriage_", "")
                    if string.match(cleanKey, "^spouseRoom_") then
                        table.insert(spouseRoomDialogues, {
                            key = key,
                            dialogue = dialogue
                        })
                    else
                        table.insert(indoorDialogues, {
                            key = key,
                            dialogue = dialogue
                        })
                    end
                end

                -- 按照优先级排序普通室内对话
                table.sort(indoorDialogues, function(a, b)
                    return compareMarriageDialoguePriority(a.key, b.key)
                end)

                -- 按照优先级排序配偶房间对话
                table.sort(spouseRoomDialogues, function(a, b)
                    return compareMarriageDialoguePriority(a.key, b.key)
                end)

                -- 先输出普通室内对话
                for _, item in ipairs(indoorDialogues) do
                    table.insert(output_temp, formatDialogueText(item.dialogue))
                    table.insert(output_temp, "")
                end

                -- 再输出配偶房间对话
                if next(spouseRoomDialogues) then
                    table.insert(output_temp, "配偶房间")
                    table.insert(output_temp, "")
                    for _, item in ipairs(spouseRoomDialogues) do
                        table.insert(output_temp, formatDialogueText(item.dialogue))
                        table.insert(output_temp, "")
                    end
                end
            else
                -- 其他分类按照优先级排序输出
                local sortedDialogues = {}
                for key, dialogue in pairs(categories[categoryName]) do
                    table.insert(sortedDialogues, {
                        key = key,
                        dialogue = dialogue
                    })
                end

                -- 按照优先级排序:NPC专属文件 > 通用文件中的NPC特定 > 通用文件中的通用对话
                table.sort(sortedDialogues, function(a, b)
                    return compareMarriageDialoguePriority(a.key, b.key)
                end)

                for _, item in ipairs(sortedDialogues) do
                    table.insert(output_temp, formatDialogueText(item.dialogue))
                    table.insert(output_temp, "")
                end
            end
            table.insert(output, Helper.ExpandTemplate("Collapse", {
                formatLevel3HeaderInline(categoryName),
                content = table.concat(removeConsecutiveEmptyStrings(output_temp), "\n")
            }))
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成室友对话输出
local function formatRoommateDialogues(dialogues)
    local output = {}
    local categories = {
        ["白天室内"] = {},
        ["晚上室内"] = {},
        ["雨天日间"] = {},
        ["雨天夜间"] = {},
        ["室外"] = {},
        ["外出"] = {},
        ["关系较好"] = {},
        ["关系一般"] = {},
        ["关系较差"] = {},
        ["特定日期"] = {},
        ["其他"] = {}
    }

    -- 分类室友对话(与婚后对话类似的分类逻辑)
    for key, dialogue in pairs(dialogues) do
        local cleanKey = string.gsub(key, "^Marriage_", "")
        local category = "其他" -- 默认分类

        -- 根据关键词判断分类
        if string.match(cleanKey, "^Indoor_Day") or string.match(cleanKey, "^indoor") then
            category = "白天室内"
        elseif string.match(cleanKey, "^Indoor_Night") or string.match(cleanKey, "^night") then
            category = "晚上室内"
        elseif string.match(cleanKey, "^Rainy_Day") then
            category = "雨天日间"
        elseif string.match(cleanKey, "^Rainy_Night") then
            category = "雨天夜间"
        elseif string.match(cleanKey, "^Outdoor") then
            category = "室外"
        elseif string.match(cleanKey, "^funLeave") or string.match(cleanKey, "^funReturn") then
            category = "外出"
        elseif string.match(cleanKey, "^Good") then
            category = "关系较好"
        elseif string.match(cleanKey, "^Neutral") then
            category = "关系一般"
        elseif string.match(cleanKey, "^Bad") then
            category = "关系较差"
        elseif string.match(cleanKey, "^spring_") or string.match(cleanKey, "^summer_") or
            string.match(cleanKey, "^fall_") or string.match(cleanKey, "^winter_") then
            category = "特定日期"
        end

        categories[category][key] = dialogue
    end

    -- 按指定顺序输出各分类
    local categoryOrder = {"白天室内", "晚上室内", "雨天日间", "雨天夜间", "室外", "外出",
                           "关系较好", "关系一般", "关系较差", "特定日期", "季节配偶对话",
                           "其他"}

    for _, categoryName in ipairs(categoryOrder) do
        if next(categories[categoryName]) then
            table.insert(output, formatLevel3Header(categoryName))
            table.insert(output, "")

            -- 简单按键名排序输出
            local sortedKeys = {}
            for key, _ in pairs(categories[categoryName]) do
                table.insert(sortedKeys, key)
            end
            table.sort(sortedKeys)

            for _, key in ipairs(sortedKeys) do
                table.insert(output, formatDialogueText(categories[categoryName][key]))
                table.insert(output, "")
            end
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成雨天对话输出
local function formatRainDialogues(dialogues)
    local output = {}

    -- 简单按键名排序输出雨天对话
    local sortedKeys = {}
    for key, _ in pairs(dialogues) do
        table.insert(sortedKeys, key)
    end
    table.sort(sortedKeys)

    for _, key in ipairs(sortedKeys) do
        table.insert(output, formatDialogueText(dialogues[key]))
        table.insert(output, "")
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成物品对话输出
local function formatItemDialogues(dialogues)
    local output = {}

    -- 按类型分组物品对话(按照Wiki文档的Item dialogue分类)
    local giftDialogues = {}
    local birthdayGiftDialogues = {}
    local bouquetDialogues = {}
    local movieInvitationDialogues = {}
    local rejectItemDialogues = {}
    local rejectBouquetDialogues = {}
    local rejectMermaidPendantDialogues = {}
    local rejectGiftDivorcedDialogues = {}
    local otherItemDialogues = {}

    for key, dialogue in pairs(dialogues) do
        if string.match(key, "^AcceptGift") then
            giftDialogues[key] = dialogue
        elseif string.match(key, "^AcceptBirthdayGift") then
            birthdayGiftDialogues[key] = dialogue
        elseif string.match(key, "^AcceptBouquet$") then
            bouquetDialogues[key] = dialogue
        elseif string.match(key, "^MovieInvitation$") then
            movieInvitationDialogues[key] = dialogue
        elseif string.match(key, "^RejectItem") then
            rejectItemDialogues[key] = dialogue
        elseif string.match(key, "^RejectBouquet") then
            rejectBouquetDialogues[key] = dialogue
        elseif string.match(key, "^RejectMermaidPendant") then
            rejectMermaidPendantDialogues[key] = dialogue
        elseif string.match(key, "^RejectGift_Divorced$") then
            rejectGiftDivorcedDialogues[key] = dialogue
        else
            otherItemDialogues[key] = dialogue
        end
    end

    -- 输出生日礼物对话
    if next(birthdayGiftDialogues) then
        table.insert(output, formatLevel3Header("生日礼物"))
        table.insert(output, "")
        local sortedKeys = {}
        for key, _ in pairs(birthdayGiftDialogues) do
            table.insert(sortedKeys, key)
        end
        table.sort(sortedKeys)
        for _, key in ipairs(sortedKeys) do
            table.insert(output, formatDialogueText(birthdayGiftDialogues[key]))
            table.insert(output, "")
        end
    end

    -- 输出普通礼物对话
    if next(giftDialogues) then
        table.insert(output, formatLevel3Header("普通礼物"))
        table.insert(output, "")
        local sortedKeys = {}
        for key, _ in pairs(giftDialogues) do
            table.insert(sortedKeys, key)
        end
        table.sort(sortedKeys)
        for _, key in ipairs(sortedKeys) do
            table.insert(output, formatDialogueText(giftDialogues[key]))
            table.insert(output, "")
        end
    end

    -- 输出花束对话
    if next(bouquetDialogues) then
        table.insert(output, formatLevel3Header("接受花束"))
        table.insert(output, "")
        for key, dialogue in pairs(bouquetDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 输出电影邀请对话
    if next(movieInvitationDialogues) then
        table.insert(output, formatLevel3Header("电影邀请"))
        table.insert(output, "")
        for key, dialogue in pairs(movieInvitationDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 输出拒绝物品对话
    if next(rejectItemDialogues) then
        table.insert(output, formatLevel3Header("拒绝物品"))
        table.insert(output, "")
        local sortedKeys = {}
        for key, _ in pairs(rejectItemDialogues) do
            table.insert(sortedKeys, key)
        end
        table.sort(sortedKeys)
        for _, key in ipairs(sortedKeys) do
            table.insert(output, formatDialogueText(rejectItemDialogues[key]))
            table.insert(output, "")
        end
    end

    -- 输出拒绝花束对话
    if next(rejectBouquetDialogues) then
        table.insert(output, formatLevel3Header("拒绝花束"))
        table.insert(output, "")
        local sortedKeys = {}
        for key, _ in pairs(rejectBouquetDialogues) do
            table.insert(sortedKeys, key)
        end
        table.sort(sortedKeys)
        for _, key in ipairs(sortedKeys) do
            table.insert(output, formatDialogueText(rejectBouquetDialogues[key]))
            table.insert(output, "")
        end
    end

    -- 输出拒绝美人鱼吊坠对话
    if next(rejectMermaidPendantDialogues) then
        table.insert(output, formatLevel3Header("拒绝美人鱼吊坠"))
        table.insert(output, "")
        local sortedKeys = {}
        for key, _ in pairs(rejectMermaidPendantDialogues) do
            table.insert(sortedKeys, key)
        end
        table.sort(sortedKeys)
        for _, key in ipairs(sortedKeys) do
            table.insert(output, formatDialogueText(rejectMermaidPendantDialogues[key]))
            table.insert(output, "")
        end
    end

    -- 输出离婚后拒绝礼物对话
    if next(rejectGiftDivorcedDialogues) then
        table.insert(output, formatLevel3Header("离婚后拒绝礼物"))
        table.insert(output, "")
        for key, dialogue in pairs(rejectGiftDivorcedDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 输出其他物品对话
    if next(otherItemDialogues) then
        table.insert(output, formatLevel3Header("其他物品对话"))
        table.insert(output, "")
        local sortedKeys = {}
        for key, _ in pairs(otherItemDialogues) do
            table.insert(sortedKeys, key)
        end
        table.sort(sortedKeys)
        for _, key in ipairs(sortedKeys) do
            table.insert(output, formatDialogueText(otherItemDialogues[key]))
            table.insert(output, "")
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 清理山顶对话中的Event指令
local function cleanSummitDialogue(dialogue)
    if not dialogue or type(dialogue) ~= "string" then
        return dialogue
    end

    local cleanedDialogue = dialogue

    cleanedDialogue = string.gsub(cleanedDialogue, '"/faceDirection[^"]*"', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '/pause%s+%d+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '/emote%s+[^%s]+%s+%d+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '/jump%s+[^%s]+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '/shake%s+[^%s]+%s+%d+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '/speak%s+[^%s]+%s+"([^"]*)"', function(speechContent)
        return '#$b#' .. speechContent
    end)

    
    cleanedDialogue = string.gsub(cleanedDialogue, '"%s+[^%s]+%s+%d+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '"%s+[^%s]+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '%s*/%s*', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '^%s+', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '%s+$', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '#%$b##%$b#', '#$b#')
    cleanedDialogue = string.gsub(cleanedDialogue, '^"', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '"$', '')
    cleanedDialogue = string.gsub(cleanedDialogue, '"', '')

    return cleanedDialogue
end

-- 处理山顶对话的分割显示
local function processSummitDialogue(dialogue)
    if not dialogue or type(dialogue) ~= "string" then
        return dialogue
    end

    -- 首先清理动作命令
    local cleanedDialogue = cleanSummitDialogue(dialogue)

    -- 分割对话:#$b# 作为分隔符
    local parts = {}
    local currentPart = ""
    local i = 1

    while i <= #cleanedDialogue do
        if cleanedDialogue:sub(i, i + 3) == "#$b#" then
            if currentPart ~= "" then
                table.insert(parts, currentPart)
                currentPart = ""
            end
            i = i + 4
        else
            currentPart = currentPart .. cleanedDialogue:sub(i, i)
            i = i + 1
        end
    end

    -- 添加最后一部分
    if currentPart ~= "" then
        table.insert(parts, currentPart)
    end

    -- 如果只有一个部分,直接返回清理后的文本
    if #parts <= 1 then
        return createQuote(cleanText(cleanedDialogue))
    end

    -- 处理每个独立的对话片段
    local results = {}
    for _, part in ipairs(parts) do
        local trimmed = part:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            local cleanedText = cleanText(trimmed)
            table.insert(results, createQuote(cleanedText))
        end
    end

    return table.concat(results, "\n")
end

-- 生成特殊对话输出
local function formatSpecialDialogues(dialogues, npcName)
    -- mw.log("DEBUG: formatSpecialDialogues called for NPC: " .. (npcName or "unknown"))
    -- mw.logObject(dialogues, "DEBUG: All special dialogues input")
    local output = {}

    local resortDialogues = {}
    local rainyDialogues = {}
    local greenRainDialogues = {}
    local pamHouseDialogues = {}
    local memoryDialogues = {}
    local movieTheaterDialogues = {}
    local willyCrabsDialogues = {}
    local scheduleDialogues = {}
    -- 新增的Special dialogue类型
    local dumpsterDiveDialogues = {}
    local hitBySlingshotDialogues = {}
    local locationEntryDialogues = {}
    local spouseSpecialDialogues = {}
    local wipedMemoryDialogues = {}
    local summitDialogues = {}
    local playerBehaviorDialogues = {}
    local communityCenterDialogues = {}
    local jojaDialogues = {}
    local otherSpecial = {}

    -- 定义以对话类型为主键的匹配模式
    local specialDialoguePatterns = {
        -- 度假村对话
        resort = {"^Resort"},
        -- 雨天对话
        rainy = {"^rain", "^Rain"},
        -- 绿雨对话
        greenRain = {"^GreenRain$", "^GreenRain_2$", "^GreenRainFinished$"},
        -- 社区升级对话
        pamHouse = {"^pamHouse"},
        -- 垃圾桶对话
        dumpsterDive = {"^DumpsterDiveComment$"},
        -- 弹弓击中对话
        hitBySlingshot = {"^HitBySlingshot$"},
        -- 地点入口对话
        locationEntry = {"_Entry$"},
        -- 配偶特殊对话
        spouseSpecial = {"^SpouseFarmhouseClutter$", "^SpouseGiftJealous$", "^Spouse_MonstersInHouse$",
                         "^SpouseStardrop$"},
        -- 记忆擦除对话
        wipedMemory = {"^WipedMemory$"},
        -- 玩家行为对话(特殊记忆对话)
        playerBehaviorMemory = {"^firstVisit_BathHouse_MensLocker_memory_", "^purchasedAnimal_Chicken_memory_"},
        -- 记忆对话
        memory = {"_memory_"},
        -- 电影院对话
        movieTheater = {"^movieTheater"},
        -- 社区中心对话
        communityCenter = {"^cc_"},
        -- Joja超市对话
        joja = {"^joja_Begin$"},
        -- 威利蟹笼对话
        willyCrabs = {"^willyCrabs$"},
        -- 日程对话
        schedule = {"^schedule_"},
        -- 山顶事件对话
        summit = {"^Extra_SummitEvent_"},
        -- 玩家行为对话
        playerBehavior = {"^JojaMart$", "^SeedShop_Entry$", "^IslandSouth_", "^Trailer_Entry$", "^gotPet$",
                          "^structureBuilt_", "^wonIceFishing$", "^wonGrange$", "^fishCaught_", "^cropMatured_",
                          "^accept_", "^reject_", "^achievement_", "^houseUpgrade_", "^firstVisit_",
                          "^purchasedAnimal_", "^questComplete_", "^willyCrabs$", "^gotPet_", "^FullCrabPond$", "^mineArea_"}
    }

    -- 复合条件模式(需要排除某些模式的情况)
    local complexSpecialPatterns = {
        -- 日程对话(排除婚后外出对话)
        schedule = {function(key)
            return string.match(key, "^schedule_") and not string.match(key, "^schedule_marriage_")
        end}
    }

    -- 分离不同类型的特殊对话(按照Wiki文档的Special dialogue分类)
    for key, dialogue in pairs(dialogues) do
        local matched = false

        -- 首先检查复合条件模式
        for category, checkFunctions in pairs(complexSpecialPatterns) do
            for _, checkFunction in ipairs(checkFunctions) do
                if checkFunction(key) then
                    if category == "schedule" then
                        scheduleDialogues[key] = dialogue
                    end
                    matched = true
                    break
                end
            end
            if matched then
                break
            end
        end

        -- 如果复合条件未匹配,检查简单模式
        if not matched then
            for category, patterns in pairs(specialDialoguePatterns) do
                for _, pattern in ipairs(patterns) do
                    if string.match(key, pattern) then
                        if category == "resort" then
                            resortDialogues[key] = dialogue
                        elseif category == "rainy" then
                            rainyDialogues[key] = dialogue
                        elseif category == "greenRain" then
                            greenRainDialogues[key] = dialogue
                        elseif category == "pamHouse" then
                            pamHouseDialogues[key] = dialogue
                        elseif category == "dumpsterDive" then
                            dumpsterDiveDialogues[key] = dialogue
                        elseif category == "hitBySlingshot" then
                            hitBySlingshotDialogues[key] = dialogue
                        elseif category == "locationEntry" then
                            locationEntryDialogues[key] = dialogue
                        elseif category == "spouseSpecial" then
                            spouseSpecialDialogues[key] = dialogue
                        elseif category == "wipedMemory" then
                            wipedMemoryDialogues[key] = dialogue
                        elseif category == "playerBehaviorMemory" then
                            playerBehaviorDialogues[key] = dialogue
                        elseif category == "memory" then
                            memoryDialogues[key] = dialogue
                        elseif category == "movieTheater" then
                            movieTheaterDialogues[key] = dialogue
                        elseif category == "communityCenter" then
                            communityCenterDialogues[key] = dialogue
                        elseif category == "joja" then
                            jojaDialogues[key] = dialogue
                        elseif category == "willyCrabs" then
                            willyCrabsDialogues[key] = dialogue
                        elseif category == "summit" then
                            summitDialogues[key] = dialogue
                        elseif category == "playerBehavior" then
                            playerBehaviorDialogues[key] = dialogue
                        end
                        matched = true
                        break
                    end
                end
                if matched then
                    break
                end
            end
        end

        -- 如果所有模式都未匹配,归入其他特殊对话
        if not matched then
            otherSpecial[key] = dialogue
        end
    end

    -- 1. 处理社区中心对话
    if next(communityCenterDialogues) then
        table.insert(output, formatLevel3Header("[[社区中心]]"))
        table.insert(output, "")

        -- 按指定顺序排序cc_对话
        local ccOrder = {{
            key = "cc_Begin",
            label = "触发前置剧情"
        }, {
            key = "cc_Bus",
            label = "修复巴士"
        }, {
            key = "cc_Minecart",
            label = "修复矿车"
        }, {
            key = "cc_Boulder",
            label = "移除巨石"
        }, {
            key = "cc_Bridge",
            label = "修复采石场断桥"
        }, {
            key = "cc_Complete",
            label = "社区中心重建完成"
        }}

        for _, item in ipairs(ccOrder) do
            if communityCenterDialogues[item.key] then
                table.insert(output, "" .. item.label .. "")
                table.insert(output, "")
                table.insert(output, formatDialogueText(communityCenterDialogues[item.key]))
                table.insert(output, "")
            end
        end

        -- 处理其他cc_对话(如果有的话)
        for key, dialogue in pairs(communityCenterDialogues) do
            local found = false
            for _, item in ipairs(ccOrder) do
                if key == item.key then
                    found = true
                    break
                end
            end
            if not found then
                table.insert(output, "'''" .. key .. "'''")
                table.insert(output, "")
                table.insert(output, formatDialogueText(dialogue))
                table.insert(output, "")
            end
        end
    end

    -- 2. 处理Joja超市对话
    if next(jojaDialogues) then
        table.insert(output, formatLevel3Header("[[Joja超市|Joja]]"))
        table.insert(output, "")

        if jojaDialogues["joja_Begin"] then
            table.insert(output, "加入会员")
            table.insert(output, "")
            table.insert(output, formatDialogueText(jojaDialogues["joja_Begin"]))
            table.insert(output, "")
        end
    end

    -- 3. 处理雨天对话
    if next(rainyDialogues) then
        table.insert(output, formatLevel3Header("[[天气#雨天|雨天]]"))
        table.insert(output, "")

        -- 按键名排序雨天对话
        local rainyList = {}
        for key, dialogue in pairs(rainyDialogues) do
            table.insert(rainyList, {
                key = key,
                dialogue = dialogue
            })
        end
        table.sort(rainyList, function(a, b)
            return a.key < b.key
        end)

        for _, item in ipairs(rainyList) do
            table.insert(output, formatDialogueText(item.dialogue))
            table.insert(output, "")
        end
    end

    -- 4. 处理绿雨对话(合并显示)
    if next(greenRainDialogues) then
        table.insert(output, formatLevel3Header("[[天气#绿雨|绿雨]]"))
        table.insert(output, "")

        -- 第一年
        if greenRainDialogues["GreenRain"] then
            table.insert(output, "第一年")
            table.insert(output, "")
            table.insert(output, formatDialogueText(greenRainDialogues["GreenRain"]))
            table.insert(output, "")

            -- 绿雨结束后(第一年)
            if greenRainDialogues["GreenRainFinished"] then
                table.insert(output, "绿雨结束后")
                table.insert(output, "")
                table.insert(output, formatDialogueText(greenRainDialogues["GreenRainFinished"]))
                table.insert(output, "")
            end
        end

        -- 第二年以后
        if greenRainDialogues["GreenRain_2"] then
            table.insert(output, "第二年以后")
            table.insert(output, "")
            table.insert(output, formatDialogueText(greenRainDialogues["GreenRain_2"]))
            table.insert(output, "")
        end
    end

    -- 5. 处理体检日对话(从schedules中提取)
    if next(scheduleDialogues) then
        -- 定义体检日期表
        local checkupDates = {
            Alex = {"summer_16"},
            Sebastian = {"summer_4"},
            Sam = {"fall_11"},
            Haley = {"winter_9"},
            Penny = {"winter_4"},
            Elliott = {"summer_9"},
            Emily = {"winter_11"},
            Leah = {"spring_16"},
            Abigail = {"spring_4"},
            George = {"spring_23", "summer_23", "fall_23", "winter_23"},
            Jodi = {"spring_11", "spring_18"},
            Clint = {"winter_16"},
            Lewis = {"fall_9"},
            Caroline = {"fall_25"},
            Willy = {"spring_9"},
            Demetrius = {"summer_25"},
            Vincent = {"spring_11"},
            Gus = {"fall_4"},
            Pam = {"spring_25"},
            Marnie = {"fall_18", "winter_18"},
            Robin = {"summer_18"},
            Evelyn = {"spring_2", "summer_2", "fall_2", "winter_2", "spring_23", "summer_23", "fall_23", "winter_23"},
            Jas = {"winter_18"}
        }

        -- 提取体检日对话
        local checkupDialogues = {}
        local currentNpc = npcName

        for key, dialogue in pairs(scheduleDialogues) do
            local cleanKey = string.gsub(key, "^schedule_", "")

            -- 检查体检日对话
            local isCheckup = false
            if currentNpc and checkupDates[currentNpc] then
                for _, checkupDate in ipairs(checkupDates[currentNpc]) do
                    -- 匹配带季节前缀的键(如"spring_23.000")
                    if string.match(cleanKey, "^" .. checkupDate .. "%.") then
                        isCheckup = true
                        break
                    end
                    -- 匹配纯数字键(如"23.000"),用于乔治和艾芙琳等全季节体检的角色
                    local dayOnly = string.match(checkupDate, "_(%d+)$")
                    if dayOnly and string.match(cleanKey, "^" .. dayOnly .. "%.") then
                        isCheckup = true
                        break
                    end
                end
            end

            if isCheckup then
                checkupDialogues[cleanKey] = dialogue
            end
        end

        -- 输出体检日对话
        if next(checkupDialogues) then
            table.insert(output, formatLevel3Header("[[哈维的诊所#预约|体检日]]"))
            table.insert(output, "")

            -- 定义陪同关系
            local accompanimentInfo = {
                -- 乔迪陪同文森特:春季11日
                ["spring_11"] = {
                    companion = "Jodi",
                    patient = "Vincent",
                    label = "乔迪(陪同文森特)"
                },
                -- 玛妮陪同贾斯:冬季18日
                ["winter_18"] = {
                    companion = "Marnie",
                    patient = "Jas",
                    label = "玛妮(陪同贾斯)"
                },
                -- 艾芙琳陪同乔治:所有季节23日
                ["23"] = {
                    companion = "Evelyn",
                    patient = "George",
                    label = "艾芙琳(陪同乔治)"
                }
            }

            -- 分离个人体检和陪同体检对话
            local personalCheckups = {}
            local accompanimentCheckups = {}

            for key, dialogue in pairs(checkupDialogues) do
                local dateKey = string.match(key, "^([^%.]+)")
                if dateKey and accompanimentInfo[dateKey] then
                    local info = accompanimentInfo[dateKey]
                    -- 检查当前NPC是否为陪同者
                    if currentNpc == info.companion then
                        table.insert(accompanimentCheckups, {
                            key = key,
                            dialogue = dialogue,
                            label = info.label,
                            dateKey = dateKey
                        })
                    else
                        -- 如果当前NPC是被陪同者,也标注为陪同对话
                        table.insert(personalCheckups, {
                            key = key,
                            dialogue = dialogue,
                            dateKey = dateKey
                        })
                    end
                else
                    table.insert(personalCheckups, {
                        key = key,
                        dialogue = dialogue,
                        dateKey = dateKey
                    })
                end
            end

            -- 按日期排序个人体检对话
            table.sort(personalCheckups, function(a, b)
                return a.key < b.key
            end)

            -- 输出个人体检对话
            for _, item in ipairs(personalCheckups) do
                table.insert(output, formatDialogueText(item.dialogue))
                table.insert(output, "")
            end

            -- 按日期排序陪同体检对话
            table.sort(accompanimentCheckups, function(a, b)
                return a.key < b.key
            end)

            -- 输出陪同体检对话
            if next(accompanimentCheckups) then
                -- 按陪同类型分组
                local accompanimentGroups = {}
                for _, item in ipairs(accompanimentCheckups) do
                    local label = item.label
                    if not accompanimentGroups[label] then
                        accompanimentGroups[label] = {}
                    end
                    table.insert(accompanimentGroups[label], item)
                end

                -- 按标签排序输出每组陪同对话
                local groupLabels = {}
                for label, _ in pairs(accompanimentGroups) do
                    table.insert(groupLabels, label)
                end
                table.sort(groupLabels)

                for _, label in ipairs(groupLabels) do
                    local group = accompanimentGroups[label]
                    -- 输出分组标题
                    table.insert(output, "'''" .. label .. "'''")
                    table.insert(output, "")

                    -- 按日期排序该组内的对话
                    table.sort(group, function(a, b)
                        return a.key < b.key
                    end)

                    -- 输出该组的所有对话
                    for _, item in ipairs(group) do
                        table.insert(output, formatDialogueText(item.dialogue))
                        table.insert(output, "")
                    end
                end
            end
        end
    end

    -- 6. 处理度假村对话
    if next(resortDialogues) then
        table.insert(output, formatLevel3Header("[[姜岛#海滩度假村|姜岛度假村]]"))
        table.insert(output, "")

        -- 按预定义顺序输出
        local resortOrder = {{
            key = "Resort_Entering",
            name = "进入"
        }, {
            key = "Resort",
            name = "一般"
        }, {
            key = "Resort_Towel",
            name = "毛巾"
        }, {
            key = "Resort_Shore",
            name = "海滩"
        }, {
            key = "Resort_Wander",
            name = "漫步"
        }, {
            key = "Resort_Bar",
            name = "酒吧"
        }, {
            key = "Resort_Chair",
            name = "椅子"
        }, {
            key = "Resort_Leaving",
            name = "离开"
        }}

        for _, location in ipairs(resortOrder) do
            if resortDialogues[location.key] then
                table.insert(output, formatDialogueText(resortDialogues[location.key]))
                table.insert(output, "")
            end
        end

        -- 处理其他度假村对话
        for key, dialogue in pairs(resortDialogues) do
            local found = false
            for _, location in ipairs(resortOrder) do
                if key == location.key then
                    found = true
                    break
                end
            end
            if not found then
                table.insert(output, formatDialogueText(dialogue))
                table.insert(output, "")
            end
        end
    end

    -- 7. 处理社区升级对话
    if next(pamHouseDialogues) then
        table.insert(output, formatLevel3Header("社区升级"))
        table.insert(output, "")

        -- 分离匿名和不匿名对话
        local anonymousDialogue = nil
        local namedDialogue = nil
        local otherPamDialogues = {}

        for key, dialogue in pairs(pamHouseDialogues) do
            if key == "pamHouseUpgradeAnonymous" then
                anonymousDialogue = dialogue
            elseif key == "pamHouseUpgrade" then
                namedDialogue = dialogue
            else
                otherPamDialogues[key] = dialogue
            end
        end

        -- 输出匿名对话
        if anonymousDialogue then
            table.insert(output, "'''匿名'''")
            table.insert(output, "")
            table.insert(output, formatDialogueText(anonymousDialogue))
            table.insert(output, "")
        end

        -- 输出不匿名对话
        if namedDialogue then
            table.insert(output, "'''不匿名'''")
            table.insert(output, "")
            table.insert(output, formatDialogueText(namedDialogue))
            table.insert(output, "")
        end

        -- 输出其他pamHouse相关对话
        for key, dialogue in pairs(otherPamDialogues) do
            local displayKey = formatOtherDialogueKey(key)
            table.insert(output, "'''" .. displayKey .. "'''")
            table.insert(output, "")
            table.insert(output, formatDialogueText(dialogue, key))
            table.insert(output, "")
        end
    end

    -- 8. 处理玩家行为对话
    if next(playerBehaviorDialogues) then
        table.insert(output, formatLevel3Header("玩家行为"))
        table.insert(output, "")

        -- 按键名排序玩家行为对话
        local behaviorList = {}
        for key, dialogue in pairs(playerBehaviorDialogues) do
            table.insert(behaviorList, {
                key = key,
                dialogue = dialogue
            })
        end
        table.sort(behaviorList, function(a, b)
            return a.key < b.key
        end)

        for _, item in ipairs(behaviorList) do
            local displayKey = formatOtherDialogueKey(item.key)
            table.insert(output, "" .. displayKey .. "")
            table.insert(output, "")
            table.insert(output, formatDialogueText(item.dialogue, item.key))
            table.insert(output, "")
        end
    end

    -- 9. 处理特殊行程对话(从schedules中提取,排除体检日)
    if next(scheduleDialogues) then
        -- 定义体检日期表
        local checkupDates = {
            Alex = {"summer_16"},
            Sebastian = {"summer_4"},
            Sam = {"fall_11"},
            Haley = {"winter_9"},
            Penny = {"winter_4"},
            Elliott = {"summer_9"},
            Emily = {"winter_11"},
            Leah = {"spring_16"},
            Abigail = {"spring_4"},
            George = {"spring_23", "summer_23", "fall_23", "winter_23"},
            Jodi = {"spring_11", "spring_18"},
            Clint = {"winter_16"},
            Lewis = {"fall_9"},
            Caroline = {"fall_25"},
            Willy = {"spring_9"},
            Demetrius = {"summer_25"},
            Vincent = {"spring_11"},
            Gus = {"fall_4"},
            Pam = {"spring_25"},
            Marnie = {"fall_18", "winter_18"},
            Robin = {"summer_18"},
            Evelyn = {"spring_2", "summer_2", "fall_2", "winter_2", "spring_23", "summer_23", "fall_23", "winter_23"},
            Jas = {"winter_18"}
        }

        -- 分类schedule对话
        local sandyBirthdayDialogues = {}
        local otherScheduleDialogues = {}
        local currentNpc = npcName

        for key, dialogue in pairs(scheduleDialogues) do
            local cleanKey = string.gsub(key, "^schedule_", "")

            -- 检查桑迪生日对话
            if string.match(cleanKey, "^Sandy_Birthday_") then
                sandyBirthdayDialogues[cleanKey] = dialogue
            else
                -- 检查体检日对话(排除)
                local isCheckup = false
                if currentNpc and checkupDates[currentNpc] then
                    for _, checkupDate in ipairs(checkupDates[currentNpc]) do
                        -- 匹配带季节前缀的键(如"spring_23.000")
                        if string.match(cleanKey, "^" .. checkupDate .. "%.") then
                            isCheckup = true
                            break
                        end
                        -- 匹配纯数字键(如"23.000"),用于乔治和艾芙琳等全季节体检的角色
                        local dayOnly = string.match(checkupDate, "_(%d+)$")
                        if dayOnly and string.match(cleanKey, "^" .. dayOnly .. "%.") then
                            isCheckup = true
                            break
                        end
                    end
                end

                if not isCheckup then
                    -- 跳过夜市对话(已在节日对话中处理)
                    if not string.match(cleanKey, "^winter_1[567]%.") then
                        otherScheduleDialogues[cleanKey] = dialogue
                    end
                end
            end
        end

        -- 输出桑迪生日对话
        if next(sandyBirthdayDialogues) then
            table.insert(output, formatLevel3Header("[[桑迪]]生日"))
            table.insert(output, "")

            -- 按指定顺序排序
            local sandyOrder = {"Sandy_Birthday_Greeting", "Sandy_Birthday_Hangout", "Sandy_Birthday_Lake",
                                "Sandy_Birthday_Bones", "Sandy_Birthday_Camel", "Sandy_Birthday_Bye"}

            for _, orderKey in ipairs(sandyOrder) do
                if sandyBirthdayDialogues[orderKey] then
                    table.insert(output, formatDialogueText(sandyBirthdayDialogues[orderKey]))
                    table.insert(output, "")
                end
            end
        end

        -- 输出特殊行程对话
        if next(otherScheduleDialogues) then
            table.insert(output, formatLevel3Header("特殊行程对话"))
            table.insert(output, "")

            -- 按键名分组:周一到周日,然后按是否婚后分组
            local scheduleGroups = {}
            local weekdayNames = {
                Mon = "周一",
                Tue = "周二",
                Wed = "周三",
                Thu = "周四",
                Fri = "周五",
                Sat = "周六",
                Sun = "周日"
            }

            for key, dialogue in pairs(otherScheduleDialogues) do
                -- 检查是否为 "Sun.000" 和 "Sun.000_married" 这种格式
                local weekday = nil
                local isMarried = false

                -- 先检查是否以 _married 结尾
                if string.match(key, "_married$") then
                    isMarried = true
                    local baseKey = string.gsub(key, "_married$", "")
                    weekday = string.match(baseKey, "^([A-Z][a-z][a-z])%.%d+$")
                else
                    weekday = string.match(key, "^([A-Z][a-z][a-z])%.%d+$")
                end

                if weekday and weekdayNames[weekday] then
                    if not scheduleGroups[weekday] then
                        scheduleGroups[weekday] = {
                            normal = {},
                            married = {}
                        }
                    end

                    if isMarried then
                        table.insert(scheduleGroups[weekday].married, {
                            key = key,
                            dialogue = dialogue
                        })
                    else
                        table.insert(scheduleGroups[weekday].normal, {
                            key = key,
                            dialogue = dialogue
                        })
                    end
                else
                    -- 其他格式的键保持原样输出
                    if not scheduleGroups["其他"] then
                        scheduleGroups["其他"] = {}
                    end
                    table.insert(scheduleGroups["其他"], {
                        key = key,
                        dialogue = dialogue
                    })
                end
            end

            -- 按星期顺序输出
            local weekOrder = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
            for _, weekday in ipairs(weekOrder) do
                if scheduleGroups[weekday] then
                    local weekdayName = weekdayNames[weekday]

                    -- 有普通和婚后对话的情况
                    if next(scheduleGroups[weekday].normal) or next(scheduleGroups[weekday].married) then
                        -- 输出星期标题(加粗)
                        table.insert(output, "'''" .. weekdayName .. "'''")
                        table.insert(output, "")

                        -- 先输出普通对话
                        for _, item in ipairs(scheduleGroups[weekday].normal) do
                            table.insert(output, formatDialogueText(item.dialogue))
                            table.insert(output, "")
                        end

                        -- 再输出婚后对话
                        if next(scheduleGroups[weekday].married) then
                            -- 婚后标题(不加粗)
                            table.insert(output, "婚后")
                            table.insert(output, "")
                            for _, item in ipairs(scheduleGroups[weekday].married) do
                                table.insert(output, formatDialogueText(item.dialogue))
                                table.insert(output, "")
                            end
                        end
                    end
                end
            end

            -- 输出其他格式的对话
            if scheduleGroups["其他"] then
                for _, item in ipairs(scheduleGroups["其他"]) do
                    if not item.key:find(".00") then
                        local displayKey = formatOtherDialogueKey(item.key) or item.key
                        table.insert(output, "'''" .. displayKey .. "'''")
                    end
                    table.insert(output, "")
                    table.insert(output, formatDialogueText(item.dialogue))
                    table.insert(output, "")
                end
            end
        end
    end

    -- 10. 处理其他对话
    if next(otherSpecial) then
        table.insert(output, formatLevel3Header("其他对话"))
        table.insert(output, "")
        for key, dialogue in pairs(otherSpecial) do
            local displayKey = formatOtherDialogueKey(key) or key
            table.insert(output, "'''" .. displayKey .. "'''")
            table.insert(output, "")
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理记忆对话
    if next(memoryDialogues) then
        -- mw.logObject(memoryDialogues, "DEBUG: memoryDialogues content")

        -- 按类型分组记忆对话
        local memoryGroups = {}

        for key, dialogue in pairs(memoryDialogues) do
            local timeFrame = string.match(key, "memory_(.+)")
            local timeLabel = timeFrame
            if timeFrame == "oneday" then
                timeLabel = "1 天后"
            elseif timeFrame == "oneweek" then
                timeLabel = "1 周后"
            elseif timeFrame == "oneyear" then
                timeLabel = "1 年后"
            elseif timeFrame == "twoweeks" then
                timeLabel = "2 周后"
            elseif timeFrame == "fourweeks" then
                timeLabel = "4 周后"
            end

            -- 识别特殊类型的记忆对话
            local dialogueType = nil
            if string.match(key, "^dating_") then
                dialogueType = "约会记忆"
            elseif string.match(key, "^married_") then
                dialogueType = "结婚记忆"
            else
                dialogueType = "其他记忆"
            end

            if not memoryGroups[dialogueType] then
                memoryGroups[dialogueType] = {}
            end

            if not memoryGroups[dialogueType][timeLabel] then
                memoryGroups[dialogueType][timeLabel] = {}
            end

            table.insert(memoryGroups[dialogueType][timeLabel], dialogue)
        end

        -- 按类型输出记忆对话
        local typeOrder = {"约会记忆", "结婚记忆", "其他记忆"}
        for _, dialogueType in ipairs(typeOrder) do
            if memoryGroups[dialogueType] then
                if dialogueType ~= "其他记忆" then
                    table.insert(output, "'''" .. dialogueType .. "'''")
                    table.insert(output, "")
                end

                -- 按时间排序
                local timeOrder = {"1 天后", "1 周后", "2 周后", "4 周后", "1 年后"}
                for _, timeLabel in ipairs(timeOrder) do
                    if memoryGroups[dialogueType][timeLabel] then
                        if dialogueType == "其他记忆" then
                            table.insert(output, "" .. timeLabel .. "")
                            table.insert(output, "")
                        end

                        for _, dialogue in ipairs(memoryGroups[dialogueType][timeLabel]) do
                            table.insert(output, formatDialogueText(dialogue))
                            table.insert(output, "")
                        end
                    end
                end
            end
        end
    end

    -- 处理其他特殊对话类型
    -- 处理垃圾桶翻找对话
    if next(dumpsterDiveDialogues) then
        table.insert(output, formatLevel3Header("翻找垃圾桶被发现"))
        table.insert(output, "")
        for key, dialogue in pairs(dumpsterDiveDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理弹弓击中对话
    if next(hitBySlingshotDialogues) then
        table.insert(output, formatLevel3Header("被弹弓击中"))
        table.insert(output, "")
        for key, dialogue in pairs(hitBySlingshotDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理地点进入对话
    if next(locationEntryDialogues) then
        table.insert(output, formatLevel3Header("进入地点"))
        table.insert(output, "")
        for key, dialogue in pairs(locationEntryDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理配偶特殊对话
    if next(spouseSpecialDialogues) then
        table.insert(output, formatLevel3Header("配偶特殊情况"))
        table.insert(output, "")
        for key, dialogue in pairs(spouseSpecialDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理记忆清除对话
    if next(wipedMemoryDialogues) then
        table.insert(output, formatLevel3Header("记忆清除后"))
        table.insert(output, "")
        for key, dialogue in pairs(wipedMemoryDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理电影院开业对话
    if next(movieTheaterDialogues) then
        table.insert(output, formatLevel3Header("[[电影院]]开业"))
        table.insert(output, "")
        for key, dialogue in pairs(movieTheaterDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 处理威利蟹笼对话
    if next(willyCrabsDialogues) then
        table.insert(output, formatLevel3Header("触发威利蟹笼事件后"))
        table.insert(output, "")
        for key, dialogue in pairs(willyCrabsDialogues) do
            table.insert(output, formatDialogueText(dialogue))
            table.insert(output, "")
        end
    end

    -- 11. 处理完美(山顶对话)- 放在最后,因为是大结局
    -- 只有可攻略人物、Lewis和Morris才输出山顶对话
    -- mw.log("DEBUG: Checking summitDialogues, has content: " .. tostring(next(summitDialogues) ~= nil))
    -- mw.logObject(summitDialogues, "DEBUG: summitDialogues content")

    -- 只有可攻略人物、Lewis和Morris才显示山顶对话
    mw.logObject(summitDialogues)
    if next(summitDialogues) and (isMarriageableCharacter(npcName) or npcName == "Lewis" or npcName == "Morris") then
        -- mw.log("DEBUG: Processing summit dialogues for NPC: " .. (npcName or "unknown"))
        table.insert(output, formatLevel3Header("[[完美]](山顶对话)"))
        table.insert(output, "")

        -- 根据getSummitEvent.cs的逻辑,山顶对话有三种情况:
        -- 1. 配偶对话(可攻略角色)
        -- 2. 刘易斯对话(社区线,没有攻略任何人物)
        -- 3. 莫里斯对话(Joja线,没有攻略任何人物)

        if isMarriageableCharacter(npcName) then
            -- mw.log("DEBUG: Processing marriageable character summit dialogue")
            -- 配偶对话(可攻略角色)
            table.insert(output, "配偶对话")
            table.insert(output, "")
            -- table.insert(output, "''当该角色为配偶时的山顶对话''")
            -- table.insert(output, "")

            -- 按照getSummitEvent.cs中的顺序显示对话
            local summitOrder = {"Extra_SummitEvent_Intro_Spouse", "Extra_SummitEvent_Intro2_Spouse",
                                 "Extra_SummitEvent_Intro2_Spouse_Gruff", "Extra_SummitEvent_Dialogue1_Spouse",
                                 "Extra_SummitEvent_Dialogue1B_Spouse", "Extra_SummitEvent_Dialogue2_Spouse",
                                 "Extra_SummitEvent_Dialogue3_" .. npcName}

            for _, orderKey in ipairs(summitOrder) do
                -- mw.log("DEBUG: Checking summit order key: " .. orderKey)
                if summitDialogues[orderKey] then
                    -- mw.log("DEBUG: Found summit dialogue for key: " .. orderKey)
                    table.insert(output, processSummitDialogue(summitDialogues[orderKey]))
                    table.insert(output, "")
                else
                    -- mw.log("DEBUG: No summit dialogue found for key: " .. orderKey)
                end
            end
        elseif npcName == "Lewis" then
            -- 刘易斯对话(社区线)
            table.insert(output, "社区线对话(玩家没有攻略任何人物,且选择社区线时)")
            table.insert(output, "")

            local lewisOrder = {"Extra_SummitEvent_Intro_Lewis", "Extra_SummitEvent_Dialogue1_Lewis",
                                "Extra_SummitEvent_Dialogue2_Lewis", "Extra_SummitEvent_Outro_Lewis"}

            for _, orderKey in ipairs(lewisOrder) do
                if summitDialogues[orderKey] then
                    table.insert(output, processSummitDialogue(summitDialogues[orderKey]))
                    table.insert(output, "")
                end
            end
        elseif npcName == "Morris" then
            -- 莫里斯对话(Joja线)
            table.insert(output, "Joja 线对话(玩家没有攻略任何人物,且选择 Joja 线时)")
            table.insert(output, "")

            local morrisOrder = {"Extra_SummitEvent_Intro_Morris", "Extra_SummitEvent_Dialogue1_Morris",
                                 "Extra_SummitEvent_Dialogue2_Morris", "Extra_SummitEvent_Outro_Morris"}

            for _, orderKey in ipairs(morrisOrder) do
                if summitDialogues[orderKey] then
                    table.insert(output, processSummitDialogue(summitDialogues[orderKey]))
                    table.insert(output, "")
                end
            end
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成ExtraDialogue额外对话输出
local function formatExtraDialogues(dialogues, npcName)
    local output = {}
    local extraGroups = {
        ["其他额外对话"] = {}
    }

    -- 按类型分组
    for key, dialogue in pairs(dialogues) do
        local cleanKey = string.gsub(key, "^Extra_", "")
        if not string.match(cleanKey, "^SummitEvent_") and
            not (string.match(cleanKey, "^PurchasedItem_") and npcName == "Willy") and
            not (string.match(cleanKey, "PamUpgrade_") and npcName == "Pam") then
            extraGroups["其他额外对话"][cleanKey] = dialogue
        end
    end

    -- 输出各分组
    local groupOrder = {"其他额外对话"}
    for _, groupName in ipairs(groupOrder) do
        if next(extraGroups[groupName]) then
            table.insert(output, formatLevel3Header(groupName))
            table.insert(output, "")

            local sortedKeys = {}
            for key, _ in pairs(extraGroups[groupName]) do
                table.insert(sortedKeys, key)
            end
            table.sort(sortedKeys)

            for _, key in ipairs(sortedKeys) do
                table.insert(output, formatDialogueText(extraGroups[groupName][key]))
                table.insert(output, "")
            end
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 生成地点相关对话输出
local function formatLocationDialogues(dialogues)
    local output = {}
    local locationGroups = {
        ["世界地图对话"] = {},
        ["地点数据对话"] = {},
        ["其他地点对话"] = {}
    }

    -- 按类型分组
    for key, dialogue in pairs(dialogues) do
        if string.match(key, "^WorldMap_") then
            locationGroups["世界地图对话"][key] = dialogue
        elseif string.match(key, "^Location_") then
            locationGroups["地点数据对话"][key] = dialogue
        else
            locationGroups["其他地点对话"][key] = dialogue
        end
    end

    -- 输出各分组
    local groupOrder = {"世界地图对话", "地点数据对话", "其他地点对话"}
    for _, groupName in ipairs(groupOrder) do
        if next(locationGroups[groupName]) then
            table.insert(output, formatLevel3Header(groupName))
            table.insert(output, "")

            local sortedKeys = {}
            for key, _ in pairs(locationGroups[groupName]) do
                table.insert(sortedKeys, key)
            end
            table.sort(sortedKeys)

            for _, key in ipairs(sortedKeys) do
                table.insert(output, formatDialogueText(locationGroups[groupName][key]))
                table.insert(output, "")
            end
        end
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

local function loadTalkData(frame)
    local version = frame.args["version"] or ''
    version = version:match("^%s*(.-)%s*$")
    if not version or version == '' then
        version = "data"
    end
    if not TalkData then
        TalkData = Helper.LazyLoad('Module:Talk/' .. version)
    end
end

function p.talk(frame)
    loadTalkData(frame)

    local npcName = frame.args[1] or frame.args["1"]
    npcName = NPC.getEnglishName(npcName)

    if not npcName then
        return "错误:请提供NPC名称"
    end

    -- 检查是否为支持的NPC
    local isSupported = false
    for _, supportedNpc in ipairs(SUPPORTED_NPCS) do
        if supportedNpc == npcName then
            isSupported = true
            break
        end
    end

    if not isSupported then
        return "错误:不支持的NPC名称: " .. npcName .. "。支持的NPC: " ..
                   table.concat(SUPPORTED_NPCS, ", ")
    end
    -- 获取对话数据
    local rawDialogues = getNpcDialogues(npcName)
    if not next(rawDialogues) then
        return "未找到 " .. npcName .. " 的对话数据"
    end

    -- 设置全局NPC数据用于$q对话处理
    setGlobalNpcData(rawDialogues)

    -- 预扫描所有对话,识别$q对话并建立已处理键列表
    for key, dialogue in pairs(rawDialogues) do
        if type(dialogue) == "string" and dialogue:match("%$q%s+") then
            -- 临时处理$q对话以建立已处理键列表,但不保存输出
            processQuestionDialogue(dialogue, rawDialogues)
        end
    end

    -- 过滤对话
    local filteredDialogues = filterDialogues(rawDialogues)

    -- 分类对话
    local categories = categorizeDialogues(filteredDialogues, npcName)

    -- 生成输出(按照Wiki文档的标准顺序)
    local output = {}

    -- 1. 特殊对话(Special dialogue)
    if next(categories.special) then
        -- table.insert(output, formatLevel2Header("特殊对话"))
        table.insert(output, "")
        table.insert(output, formatSpecialDialogues(categories.special, npcName))
        table.insert(output, "")
    end

    -- 2. 物品对话(Item dialogue)
    if next(categories.item) then
        -- table.insert(output, formatLevel2Header("物品对话"))
        table.insert(output, "")
        table.insert(output, formatItemDialogues(categories.item))
        table.insert(output, "")
    end

    -- 3. 节日对话(Festival dialogue)
    if next(categories.festival) then
        -- table.insert(output, formatLevel2Header("节日对话"))
        table.insert(output, "")
        table.insert(output, formatFestivalDialogues(categories.festival, categories.special, npcName))
        table.insert(output, "")
    end

    -- 4. 地点对话(Location dialogue)- 包含在日常对话中
    -- 5. 通用对话(Generic dialogue)- 包含在日常对话中
    if next(categories.daily) then
        -- table.insert(output, formatLevel2Header("日常对话"))
        table.insert(output, "")
        table.insert(output, formatDailyDialogues(categories.daily, rawDialogues, npcName))
        table.insert(output, "")
    end

    -- 6. 雨天对话(Rain dialogue)
    if next(categories.rain) then
        -- table.insert(output, formatLevel2Header("雨天对话"))
        table.insert(output, "")
        table.insert(output, formatRainDialogues(categories.rain))
        table.insert(output, "")
    end

    -- 7. 婚后对话(Marriage dialogue)
    -- 只有可攻略人物才输出婚后对话,非可攻略人物强制输出为空
    if next(categories.romantic) and isMarriageableCharacter(npcName) then
        -- table.insert(output, formatLevel2Header("配偶对话"))
        table.insert(output, "")
        table.insert(output, formatRomanticDialogues(categories.romantic))
        table.insert(output, "")
    end

    -- 8. 室友对话(Roommate dialogue)
    if next(categories.roommate) then
        -- table.insert(output, formatLevel2Header("室友对话"))
        table.insert(output, "")
        table.insert(output, formatRoommateDialogues(categories.roommate))
        table.insert(output, "")
    end

    -- 关系变化对话(作为特殊类别)
    if next(categories.relationship) then
        -- table.insert(output, formatLevel2Header("关系变化对话"))
        table.insert(output, "")
        table.insert(output, formatRelationshipDialogues(categories.relationship))
        table.insert(output, "")
    end

    -- ExtraDialogue额外对话
    if next(categories.extra) then
        -- table.insert(output, formatLevel2Header("额外对话"))
        table.insert(output, "")
        table.insert(output, formatExtraDialogues(categories.extra, npcName))
        table.insert(output, "")
    end

    -- 地点相关对话
    if next(categories.location) then
        -- table.insert(output, formatLevel2Header("地点对话"))
        table.insert(output, "")
        table.insert(output, formatLocationDialogues(categories.location))
        table.insert(output, "")
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 根据分类输出对话
function p.category(frame)
    loadTalkData(frame)
    local npcName = frame.args[1] or frame.args["1"]
    npcName = NPC.getEnglishName(npcName)
    local category = frame.args[2] or frame.args["2"]

    if not npcName then
        return "错误:请提供NPC名称"
    end

    if not category then
        return
            "错误:请提供对话分类(日常、节日、关系变化、配偶、室友、雨天、物品、特殊、额外、地点)"
    end

    -- 获取所有对话数据(包括节日、婚后、电影等)
    local rawDialogues = getNpcDialogues(npcName)
    if not next(rawDialogues) then
        return "错误:找不到NPC " .. npcName .. " 的对话数据"
    end

    -- 设置全局NPC数据用于$q对话处理
    setGlobalNpcData(rawDialogues)

    -- 预扫描所有对话,识别$q对话并建立已处理键列表
    for key, dialogue in pairs(rawDialogues) do
        if type(dialogue) == "string" and dialogue:match("%$q%s+") then
            -- 临时处理$q对话以建立已处理键列表,但不保存输出
            processQuestionDialogue(dialogue, rawDialogues)
        end
    end

    -- 过滤对话(与talk函数保持一致)
    local filteredDialogues = filterDialogues(rawDialogues)

    -- 分类对话数据
    local categories = categorizeDialogues(filteredDialogues, npcName)

    local output = {}

    -- 根据分类参数输出对应内容
    if category == "日常" then
        -- 将 Saloon 相关的地点对话合并到日常对话中
        local combinedDailyDialogues = {}
        for key, dialogue in pairs(categories.daily) do
            combinedDailyDialogues[key] = dialogue
        end
        for key, dialogue in pairs(categories.location) do
            if string.match(key, "^Saloon") then
                combinedDailyDialogues[key] = dialogue
            end
        end

        if next(combinedDailyDialogues) then
            table.insert(output, formatDailyDialogues(combinedDailyDialogues, rawDialogues, npcName))
        else
            return "该NPC没有日常对话"
        end
    elseif category == "节日" then
        if next(categories.festival) then
            table.insert(output, formatFestivalDialogues(categories.festival, categories.special, npcName))
        else
            return "该NPC没有节日对话"
        end
    elseif category == "关系变化" then
        if next(categories.relationship) then
            table.insert(output, formatRelationshipDialogues(categories.relationship))
        else
            return "该NPC没有关系变化对话"
        end
    elseif category == "配偶" then
        -- 非可攻略人物强制返回空内容
        if not isMarriageableCharacter(npcName) then
            return ""
        end

        if next(categories.romantic) then
            table.insert(output, formatRomanticDialogues(categories.romantic))
        else
            return "该NPC没有配偶对话"
        end
    elseif category == "室友" then
        if next(categories.roommate) then
            table.insert(output, formatRoommateDialogues(categories.roommate))
        else
            return "该NPC没有室友对话"
        end
    elseif category == "雨天" then
        if next(categories.rain) then
            table.insert(output, formatRainDialogues(categories.rain))
        else
            return "该NPC没有雨天对话"
        end
    elseif category == "特殊" then
        local has_dialogue = false
        if next(categories.special) then
            table.insert(output, formatSpecialDialogues(categories.special, npcName))
            has_dialogue = true
        end
        if next(categories.item) then
            table.insert(output, formatItemDialogues(categories.item))
            has_dialogue = true
        end
        if next(categories.extra) then
            table.insert(output, formatExtraDialogues(categories.extra, npcName))
            has_dialogue = true
        end
        if not has_dialogue then
            return "该NPC没有特殊对话"
        end

    elseif category == "地点" then
        if next(categories.location) then
            table.insert(output, formatLocationDialogues(categories.location))
        else
            return "该NPC没有地点对话"
        end
    else
        return
            "错误:无效的分类参数。请使用:日常、节日、关系变化、配偶、室友、雨天、物品、特殊、额外、地点"
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

-- 输出所有提问对话
function p.questions(frame)
    loadTalkData(frame)
    local npcName = frame.args[1] or frame.args["1"]
    npcName = NPC.getEnglishName(npcName)
    if not npcName then
        return "错误:请提供NPC名称"
    end

    -- 获取所有对话数据(包括节日、婚后、电影等)
    local allDialogues = getNpcDialogues(npcName)
    if not next(allDialogues) then
        return "错误:找不到NPC " .. npcName .. " 的对话数据"
    end

    -- 查找所有包含$q命令的对话
    local questionDialogues = {}
    for key, dialogue in pairs(allDialogues) do
        if type(dialogue) == "string" and dialogue:match("%$q%s+") then
            questionDialogues[key] = dialogue
        end
    end

    if not next(questionDialogues) then
        return "该NPC没有提问对话"
    end

    local output = {}
    -- table.insert(output, formatLevel2Header(npcName .. " 的提问对话"))
    table.insert(output, "")

    -- 按键名排序(智能排序:考虑季节、星期、好感度等)
    local sortedKeys = {}
    for key in pairs(questionDialogues) do
        table.insert(sortedKeys, key)
    end

    -- 自定义排序函数
    table.sort(sortedKeys, function(a, b)
        -- 季节顺序
        local seasonOrder = {
            spring = 1,
            summer = 2,
            fall = 3,
            winter = 4
        }
        -- 星期顺序
        local weekdayOrder = {
            Mon = 1,
            Tue = 2,
            Wed = 3,
            Thu = 4,
            Fri = 5,
            Sat = 6,
            Sun = 7
        }

        -- 解析键名中的季节、星期、好感度信息
        local function parseKey(key)
            -- 尝试匹配 season_weekday_hearts 格式 (如 fall_Wed10)
            local season, weekday, hearts = string.match(key, "^([a-z]+)_([A-Z][a-z][a-z])(%d*)$")
            if season and weekday then
                return {
                    season = seasonOrder[season] or 5,
                    weekday = weekdayOrder[weekday] or 8,
                    hearts = tonumber(hearts) or 0,
                    hasHearts = hearts ~= "",
                    hasSeason = true,
                    original = key
                }
            end

            -- 尝试匹配 weekday_hearts 格式 (如 Wed10)
            local weekday2, hearts2 = string.match(key, "^([A-Z][a-z][a-z])(%d*)$")
            if weekday2 then
                return {
                    season = 0, -- 无季节的排在前面
                    weekday = weekdayOrder[weekday2] or 8,
                    hearts = tonumber(hearts2) or 0,
                    hasHearts = hearts2 ~= "",
                    hasSeason = false,
                    original = key
                }
            end

            -- 其他格式,使用默认排序
            return {
                season = 6,
                weekday = 9,
                hearts = 0,
                hasHearts = false,
                hasSeason = false,
                original = key
            }
        end

        local infoA = parseKey(a)
        local infoB = parseKey(b)

        -- 首先按星期排序
        if infoA.weekday ~= infoB.weekday then
            return infoA.weekday < infoB.weekday
        end

        -- 同一星期内,无季节的排在前面
        if infoA.hasSeason ~= infoB.hasSeason then
            return not infoA.hasSeason -- false < true,所以无季节的排在前面
        end

        -- 都有季节或都没有季节时,按季节排序
        if infoA.season ~= infoB.season then
            return infoA.season < infoB.season
        end

        -- 季节相同时,按好感度排序
        if infoA.hearts ~= infoB.hearts then
            return infoA.hearts < infoB.hearts
        end

        -- 最后按原始键名排序
        return a < b
    end)

    -- 输出每个提问对话
    for _, key in ipairs(sortedKeys) do
        local dialogue = questionDialogues[key]

        -- 格式化键名
        local displayKey = formatOtherDialogueKey(key)

        table.insert(output, formatLevel3Header(displayKey))
        table.insert(output, "")
        table.insert(output, formatDialogueText(dialogue, key, allDialogues))
        table.insert(output, "")
    end

    output = removeConsecutiveEmptyStrings(output)
    return table.concat(output, "\n")
end

p.debug2 = function(frame)
    local npc = '阿比盖尔' or "LeoMainland" or "Krobus" or "Jodi" or "艾米丽" or "艾利欧特" or "Sam" or '文森特' or '矮人' or 'Sandy' or 'Willy' or 'Caroline' or '哈维' or 'George' or '格斯' or '潘姆' or '亚历克斯' or 'Haley' or
                    '阿比盖尔' or '亚历克斯'
    local lst = {}
    -- table.insert(lst, p.category {
    --     args = {npc, "事件"}
    -- })
    -- table.insert(lst, p.category {args={npc, "日常"}})
    -- table.insert(lst, p.category {args = {npc, "节日"}})
    -- table.insert(lst, p.category {args={npc, "关系变化"}})
    -- table.insert(lst, p.category {args={npc, "配偶"}})
    table.insert(lst, p.category { args = { npc, "特殊" } })
    return table.concat(lst, '\n')
end

return p