维护提醒

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

全站通知:

模块:Events

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

-- 懒加载数据模块
local EventsData = Helper.LazyLoad("Module:Events/data")
local EventsStrings = Helper.LazyLoad("Module:Events/data/strings")
local TalkData = Helper.LazyLoad("Module:Talk/data")

local p = {}

-- 前向声明事件相关函数
local processEventDialogue
local findEventQuestionReply
local processEventQuestionCommand
local processEventQuickQuestionCommand
local findNpcResponse
local formatDialogueText
local removeConsecutiveEmptyStrings
local findEventReactions
local formatEventReactions
local findGotoAfterLabel
local processGenderDialogue
local processCutsceneCommand

-- 全局变量
local squote = "Squote"
local questionHasNoSpecificResponse = false
local warpInDD = false
local forkProcessed = false
local hasProcessed = {}

-- 条件goto的标签描述表
-- 格式:[条件ID] = "描述文本"
local conditionalGotoLabels = {
    -- 事件相关
    LeahInternet = "二心事件中,提议将作品放到网上出售",
    LeahArtShowSuggestion = "二心事件中,提议在镇上举办艺术展",
    ["51_default"] = "二心事件中,冒犯了莉亚",
    ["54_default"] = "二心事件中,提议在镇上举办艺术展",

    choseWizard = "选择的职业为法师时",
    choseWarrior = "选择的职业为战士时",
    ["27_default"] = "选择的职业为牧师时",

    -- 对话答案相关
    Answer_38 = "上一个选项中,让玛鲁说实话",
    ["7_default"] = "上一个选项中,让玛鲁推卸责任",
    
    Answer_77 = "(如果选择了重金属风格)",
    Answer_78 = "(如果选择了电子音乐风格)",
    Answer_79 = "(如果选择了乡村音乐风格)",

    Answer_81 = "(如果四心事件中选择了「山姆故意弄掉的」)",

    Answer_958699 = "二心事件中,选择了“悬念”",
    Answer_958700 = "二心事件中,选择了“浪漫”",
    ["1848481_default"] = "二心事件中,选择了“科幻”",
    

    -- 可以继续添加其他事件的条件标签
    -- 对于默认分支,使用 "事件ID_default" 格式
}

-- cutscene选项的描述表
-- 格式:[minigame_label] = "描述文本"
local cutsceneLabels = {
    -- Abigail游戏相关
    AbigailGame_wonGame = "游戏成功通关",
    AbigailGame_lostGame = "游戏未能通关",

    -- 可以继续添加其他cutscene的标签
    -- 格式:minigame名称_label名称 = "描述文本"
}

-- 清理hasProcessed表的函数
local function clearProcessedForks()
    hasProcessed = {}
end

-- 获取已处理的fork分支列表
local function getProcessedForks()
    local forks = {}
    for fork, _ in pairs(hasProcessed) do
        table.insert(forks, fork)
    end
    return forks
end

-- 获取翻译文本
local function getTranslationText(translationKey)
    if not translationKey or translationKey == "" then
        return ""
    end

    -- 处理Strings/Events格式的翻译键
    if translationKey:match("^Strings/Events:") then
        local key = translationKey:match("^Strings/Events:(.+)$")
        if key and EventsStrings[key] then
            return EventsStrings[key]
        end
    end

    -- 处理Strings/StringsFromCSFiles格式的翻译键
    if translationKey:match("^Strings/StringsFromCSFiles:") then
        local key = translationKey:match("^Strings/StringsFromCSFiles:(.+)$")
        if key and TalkData and TalkData["Strings/StringsFromCSFiles"] and TalkData["Strings/StringsFromCSFiles"][key] then
            return TalkData["Strings/StringsFromCSFiles"][key]
        end
    end

    -- 处理Characters/Dialogue格式的对话引用
    if translationKey:match("^Characters/Dialogue/") then
        local npcAndKey = translationKey:match("^Characters/Dialogue/(.+):(.+)$")
        if npcAndKey then
            local npcName, dialogueKey = translationKey:match("^Characters/Dialogue/([^:]+):(.+)$")
            if npcName and dialogueKey and TalkData and TalkData[npcName] then
                local dialogue = TalkData[npcName][dialogueKey]
                if dialogue then
                    return dialogue
                end
            end
        end
    end

    -- 如果找不到翻译,返回原键作为占位符
    return "(" .. translationKey .. ")"
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("\\\"", "\"")
    text = text:gsub("\\n", "\n")

    -- 处理玩家名称占位符
    text = text:gsub("@", '<span class="player-name">玩家名</span>')
    text = text:gsub("{0}", '<span class="player-name">玩家名</span>')
    text = text:gsub("%%farm", '<span class="player-name">农场名</span>')
    text = text:gsub("%%book", '<span class="player-name">书名</span>')

    text = text:gsub(" < ", '喜欢') -- [[文件:Emojis046.png|24px|link=]]
    text = text:gsub("“<”", '“喜欢”') -- [[文件:Emojis046.png|24px|link=]]
    text = text:gsub("%%firstnameletter", '<span class="player-name">玩家名的第一个字符</span>')

    -- 处理其他占位符
    text = text:gsub("%%noturn", '<span class="trigger-chance">(不转身)</span>')

    -- 注意:表情参数($h, $s, $u, $l, $a, $0-9等)现在在parsePortraitParameter中处理
    text = text:gsub("%$[be]", "")  -- 只清理$b和$e,保留表情参数
    text = text:gsub("##", "\n")
    text = text:gsub("%s*%[%s*%d+%s*%]", '<span class="refuse-item">(给予物品)</span>')
    text = text:gsub("%%fork", '')
    text = text:gsub("*", "<span>*</span>")

    -- 处理物品给予标记
    text = text:gsub("%[([%d%s]+)%]", '<span class="trigger-chance">(给予物品)</span>')
    text = text:gsub("^%%", '<span class="refuse-item">(不回应)</span>')

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

    return text
end

-- 获取角色中文名称
local function getCharacterName(character)
    if not character then return "" end

    -- 处理特殊角色名
    if character == "farmer" then
        return Helper.ExpandTemplate("玩家", {})
    end

    character = character:gsub("^%l", string.upper)

    return NPC.getChineseName(character)
end

-- 解析表情参数,将英语别名转换为数字
local function parsePortraitParameter(text)
    if not text or type(text) ~= "string" then
        return text, nil
    end

    local portrait = nil
    local cleanedText = text

    -- 首先处理数字表情参数(包括多位数字),使用更精确的匹配
    local customPortrait = cleanedText:match("%$(%d+)%s*$")  -- 匹配行末的数字参数
    if not customPortrait then
        customPortrait = cleanedText:match("%$(%d+)[^%d]")  -- 匹配后面不是数字的数字参数
        if customPortrait then
            -- 验证这个匹配是完整的(不是更长数字的一部分)
            local fullMatch = cleanedText:match("%$" .. customPortrait .. "(%d)")
            if fullMatch then
                customPortrait = nil  -- 如果后面还有数字,说明这不是完整匹配
            end
        end
    end
    if not customPortrait then
        customPortrait = cleanedText:match("^.*%$(%d+)$")  -- 匹配整行末尾的数字参数
    end

    if customPortrait then
        portrait = customPortrait
        cleanedText = cleanedText:gsub("%$" .. customPortrait, "")
    else
        -- 如果没有找到数字参数,再处理字母别名
        local portraitMap = {
            ["$h"] = "1",  -- happy
            ["$s"] = "2",  -- sad
            ["$u"] = "3",  -- unique
            ["$l"] = "4",  -- love
            ["$a"] = "5",  -- angry
        }

        -- 查找表情参数(通常在行末)
        for pattern, number in pairs(portraitMap) do
            local escapedPattern = pattern:gsub("%$", "%%$")
            if cleanedText:find(escapedPattern) then
                portrait = number
                cleanedText = cleanedText:gsub(escapedPattern, "")
                break
            end
        end
    end

    return cleanedText, portrait
end

-- 包装函数
local function wrapInText(text, label)
    if label then
        return '<div class="choice-option" data-goto="' .. label .. '">' .. text .. '</div>'
    else
        return '<div class="choice-option">' .. text .. '</div>'
    end
end

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

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

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

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

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

-- 创建引用格式
local function createQuote(text, template, id, character)
    if not template then
        template = "Say"
    end

    local type = "left"
    local portrait

    -- 如果没有传入角色名,尝试从文本中提取
    if not character then
        return Helper.ExpandTemplate(template, {"voiceover", text})
    else
        portrait = NPC.getEnglishName(character:gsub('(对话气泡)', '')) or nil
    end

    if not id and character then
        id = 0
    end

    if portrait then
        portrait = portrait:lower()
    end
    
    if id and portrait then
        return Helper.ExpandTemplate(template, {type, character, text, id, portrait})
    else
        return Helper.ExpandTemplate(template, {type, character, text})
    end

end

-- 分析label中的好感度变化
local function analyzeLabelFriendship(labelName, allCommands, commandIndex)
    if not labelName or not allCommands then
        return "0", nil
    end

    -- 从指定位置开始查找label
    for i = commandIndex, #allCommands do
        local command = allCommands[i]
        if command and command:match("^label%s+" .. labelName .. "$") then
            -- 找到label,继续查找friendship命令
            for j = i + 1, #allCommands do
                local nextCommand = allCommands[j]
                if not nextCommand then
                    break
                end

                -- 如果遇到下一个label,停止搜索
                if nextCommand:match("^label%s+") then
                    break
                end

                -- 查找friendship命令:friendship <character> <amount>
                local character, amount = nextCommand:match("^friendship%s+([^%s]+)%s+([+-]?%d+)$")
                if character and amount then
                    return amount, character
                end
            end
            break
        end
    end

    return "0", nil
end

-- 创建玩家选择格式
local function createPlayerChoice(text, friendship, character)
    if character then
        character = '[[' .. character .. ']]'
        return Helper.ExpandTemplate("Choose", {"> " .. text, friendship, character})
    else
        return Helper.ExpandTemplate("Choose", {"> " .. text, friendship})
    end
end


-- 分离人物名称和对话内容
local function separateCharacterAndDialogue(text)
    if not text or type(text) ~= "string" then
        return nil, text
    end

    -- 匹配格式:角色名:对话内容
    local characterName, dialogue = text:match("^([^:]+):(.*)$")
    if characterName and dialogue then
        return characterName:match("^%s*(.-)%s*$"), dialogue
    end

    return nil, text
end

-- 处理包含分隔符的对话文本,分割为独立对话
local function processDialogueWithSeparators(text, template, characterName)
    if not text or type(text) ~= "string" then
        return ""
    end

    local dialogueText = text

    -- 如果没有传入角色名,尝试从对话文本中分离
    if not characterName then
        local extractedName, extractedDialogue = separateCharacterAndDialogue(text)
        if extractedName then
            characterName = extractedName
            dialogueText = extractedDialogue
        end
    end

    if (dialogueText == nil or dialogueText == '') and characterName then
        dialogueText = characterName
        characterName = nil
    end

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

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

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

    -- 如果没有分隔符,直接处理整个文本
    if #parts <= 1 then
        if dialogueText:find("%^") then
            -- 有性别分支,调用性别处理函数
            return processGenderDialogue(dialogueText, template, characterName)
        else
            local cleanedText, portrait = parsePortraitParameter(dialogueText)
            cleanedText = cleanText(cleanedText)
            return createQuote(cleanedText, template, portrait, characterName)
        end
    end

    -- 处理每个独立的对话片段
    local results = {}
    for _, part in ipairs(parts) do
        local trimmed = part:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            -- 检查这个片段是否包含性别分支(包括 ^ 和 ${...^...}$ 格式)
            if trimmed:find("%^") then
                -- 有性别分支,调用性别处理函数
                table.insert(results, processGenderDialogue(trimmed, template, characterName))
            else
                -- 没有性别分支,直接处理
                local cleanedText, portrait = parsePortraitParameter(trimmed)
                cleanedText = cleanText(cleanedText)
                table.insert(results, createQuote(cleanedText, template, portrait, characterName))
            end
        end
    end

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

-- 处理$y快速问答对话(从Talk.lua移植)
local function processQuickResponseDialogue(text, template, characterName)
    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
                local cleanedRemaining, remainingPortrait = parsePortraitParameter(remaining)
                cleanedRemaining = cleanText(cleanedRemaining)
                table.insert(result, createQuote(cleanedRemaining, template, remainingPortrait, characterName))
            end
            break
        end

        -- 添加$y之前的文本
        local beforeY = text:sub(currentPos, yPos - 1)
        if beforeY and beforeY ~= "" and beforeY ~= "#" then
            local cleanedBeforeY, beforeYPortrait = parsePortraitParameter(beforeY)
            cleanedBeforeY = cleanText(cleanedBeforeY)
            table.insert(result, createQuote(cleanedBeforeY, template, beforeYPortrait, characterName))
        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]

                -- 处理问题(包括表情参数)
                local cleanedQuestion, questionPortrait = parsePortraitParameter(question)
                cleanedQuestion = cleanText(cleanedQuestion)
                table.insert(result, createQuote(cleanedQuestion, template, questionPortrait, characterName))

                -- 处理选项和回应
                table.insert(result, choice())

                -- 首先生成所有选项
                local optionLabels = {}
                for i = 2, #parts, 2 do
                    local answer = parts[i]
                    if answer then
                        answer = cleanText(answer, true)
                        local labelName = "y_option_" .. ((i-2)/2 + 1) .. "_" .. tostring(math.random(10000, 99999))
                        table.insert(optionLabels, labelName)

                        -- 格式化选择
                        local choiceTemplate = createPlayerChoice(answer, "0")
                        table.insert(result, wrapInText(choiceTemplate, labelName))
                    end
                end
                table.insert(result, div())

                -- 然后生成对应的event-label
                for i = 2, #parts, 2 do
                    local reply = parts[i + 1]
                    local labelIndex = (i-2)/2 + 1
                    local labelName = optionLabels[labelIndex]

                    if reply and labelName then
                        -- 处理回应(包括表情参数)
                        local cleanedReply, replyPortrait = parsePortraitParameter(reply)
                        cleanedReply = cleanText(cleanedReply)

                        table.insert(result, '<div class="event-label" data-label="' .. labelName .. '" style="display:none;">')
                        table.insert(result, createQuote(cleanedReply, template, replyPortrait, characterName))
                        table.insert(result, '</div>')
                    end
                end
            end

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

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

-- 处理条件对话(如 $p 81#文本1$s|文本2)
local function processConditionalDialogue(text, template, characterName)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 检查是否包含 $p <number># 格式的条件对话
    local conditionPattern = "%$p%s+(%d+)#([^|]+)|(.+)"
    local questionId, trueText, falseText = text:match(conditionPattern)
    
    if questionId and trueText and falseText then
        local result = {}
        if questionId == "81" then
            table.insert(result, "(如果选择了选项三)")
        end
        -- 处理条件为真的情况
        local trueProcessed = processDialogueWithSeparators(trueText, template, characterName)
        table.insert(result, trueProcessed)
        if questionId == "81" then
            table.insert(result, "(如果选择了选项三以外的选项)")
        end
        -- 处理条件为假的情况
        local falseProcessed = processDialogueWithSeparators(falseText, template, characterName)
        table.insert(result, falseProcessed)
        
        return table.concat(result, "\n")
    end
    
    return text
end

-- 统一的对话处理函数:先处理换行切割,再处理性别分支
local function processDialogueUnified(text, template, characterName)
    if not text or type(text) ~= "string" then
        return ""
    end

    -- 优先处理条件对话($p格式)
    if text:match("%$p%s+%d+#") then
        return processConditionalDialogue(text, template, characterName)
    end

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

    -- 直接调用换行切割处理,它会在内部处理性别分支
    return processDialogueWithSeparators(text, template, characterName)
end

-- 格式化对话文本
formatDialogueText = function(text)
    if not text then return "" end
    return processDialogueUnified(text, nil, nil)
end

-- 包装label区域
local function wrapLabel(content, label)
    return '<div class="event-label" data-label="' .. label .. '" style="display:none;">' .. content .. '</div>'
end

-- 分析goto命令,返回标签列表和是否为条件goto
local function analyzeGotoCommand(gotoCommand)
    local remaining = gotoCommand:match("^goto%s+(.+)$")
    if not remaining then
        return {}, false
    end

    -- 检查是否包含引号(条件goto的标志)
    local hasConditions = remaining:find('"') ~= nil

    if hasConditions then
        -- 条件goto:格式为 goto target1 "condition1" target2 "condition2" targetDefault
        -- 根据文档:goto [<target> [condition]]+
        local parts = {}
        local inQuote = false
        local current = ""

        for i = 1, #remaining do
            local char = remaining:sub(i, i)
            if char == '"' then
                inQuote = not inQuote
                if not inQuote then
                    -- 结束引号,保存当前内容
                    table.insert(parts, current)
                    current = ""
                end
            elseif char == ' ' and not inQuote then
                if current ~= "" then
                    table.insert(parts, current)
                    current = ""
                end
            elseif inQuote then
                current = current .. char
            else
                current = current .. char
            end
        end
        if current ~= "" then
            table.insert(parts, current)
        end

        -- 提取所有标签(包括有条件和无条件的)
        local labels = {}
        local i = 1
        while i <= #parts do
            local part = parts[i]

            -- 如果这个部分不是以引号开始,说明是标签
            if not part:match('^"') then
                table.insert(labels, part)
                i = i + 1

                -- 跳过下一个条件部分(如果存在)
                if i <= #parts and parts[i]:match('^"') then
                    i = i + 1
                end
            else
                -- 这是条件部分,跳过
                i = i + 1
            end
        end

        return labels, true
    else
        -- 简单goto:只有一个标签
        return {remaining}, false
    end
end

-- 从条件字符串中提取ID(如从"PLAYER_HAS_MAIL Current LeahInternet"提取"LeahInternet")
local function extractConditionId(condition)
    if not condition then
        return nil
    end

    -- 处理PLAYER_HAS_MAIL格式
    local mailId = condition:match("PLAYER_HAS_MAIL%s+Current%s+(.+)")
    if mailId then
        return mailId
    end

    -- 处理PLAYER_HAS_DIALOGUE_ANSWER格式
    local answerId = condition:match("PLAYER_HAS_DIALOGUE_ANSWER%s+Current%s+(.+)")
    if answerId then
        return "Answer_" .. answerId
    end

    -- 可以添加其他条件格式的处理
    -- 例如:PLAYER_NPC_RELATIONSHIP等

    return nil
end

-- 处理条件goto命令
local function processConditionalGoto(gotoCommand, eventId, allCommands, commandIndex)
    local remaining = gotoCommand:match("^goto%s+(.+)$")
    if not remaining then
        return nil
    end

    -- 检查是否包含引号(条件goto的标志)
    if not remaining:find('"') then
        return nil
    end

    -- 解析条件goto:格式为 goto target1 "condition1" target2 "condition2" targetDefault
    local parts = {}
    local inQuote = false
    local current = ""

    for i = 1, #remaining do
        local char = remaining:sub(i, i)
        if char == '"' then
            inQuote = not inQuote
            if not inQuote then
                -- 结束引号,保存当前内容
                table.insert(parts, current)
                current = ""
            end
        elseif char == ' ' and not inQuote then
            if current ~= "" then
                table.insert(parts, current)
                current = ""
            end
        elseif inQuote then
            current = current .. char
        else
            current = current .. char
        end
    end
    if current ~= "" then
        table.insert(parts, current)
    end

    -- 调试:输出解析的parts
    -- print("DEBUG: parts = ", table.concat(parts, " | "))

    -- 解析条件分支
    local result = {}
    local options = {}
    local i = 1

    while i <= #parts do
        local label = parts[i]

        -- 检查下一个部分是否是条件
        -- 条件应该是从引号中解析出来的内容,通常包含空格和特定关键词
        if i + 1 <= #parts and (parts[i + 1]:find("PLAYER_") or parts[i + 1]:find(" ")) then
            -- 下一个部分是条件
            local condition = parts[i + 1]
            i = i + 2

            -- 从条件中提取ID
            local conditionId = extractConditionId(condition)
            mw.log(condition)
            if conditionId then
                local description = conditionalGotoLabels[conditionId] or ("条件分支:" .. conditionId)

                -- 查找标签后的goto命令和隐式跳转
                local additionalLabels = {}
                if allCommands and commandIndex then
                    additionalLabels = findGotoAfterLabel(allCommands, commandIndex + 1, label)
                end

                -- 构建完整的标签列表
                local allLabels = {label}
                for _, additionalLabel in ipairs(additionalLabels) do
                    table.insert(allLabels, additionalLabel)
                end
                local labelString = table.concat(allLabels, " ")

                table.insert(options, '<div class="choice-option" data-goto="' .. labelString .. '">')
                table.insert(options, '<p>' .. description .. '</p>')
                table.insert(options, '</div>')
            end
        else
            -- 没有条件,这是默认分支
            local defaultKey = (eventId or "unknown") .. "_default"
            local description = conditionalGotoLabels[defaultKey] or ("默认分支")

            -- 查找标签后的goto命令和隐式跳转
            local additionalLabels = {}
            if allCommands and commandIndex then
                additionalLabels = findGotoAfterLabel(allCommands, commandIndex + 1, label)
            end

            -- 构建完整的标签列表
            local allLabels = {label}
            for _, additionalLabel in ipairs(additionalLabels) do
                table.insert(allLabels, additionalLabel)
            end
            local labelString = table.concat(allLabels, " ")

            table.insert(options, '<div class="choice-option" data-goto="' .. labelString .. '">')
            table.insert(options, '<p>' .. description .. '</p>')
            table.insert(options, '</div>')
            i = i + 1
        end
    end

    -- 用choice-container包装所有选项
    if #options > 0 then
        table.insert(result, '<div class="choice-container">')
        for _, option in ipairs(options) do
            table.insert(result, option)
        end
        table.insert(result, '</div>')
    end

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

-- 移除连续的空字符串
removeConsecutiveEmptyStrings = function(arr)
    local i = 1
    while i <= #arr do
        if arr[i] == "" then
            local j = i
            while j <= #arr and arr[j] == "" do
                j = j + 1
            end
            if j > i + 1 then
                for k = i + 1, j - 1 do
                    table.remove(arr, i + 1)
                end
            end
            i = i + 1
        else
            i = i + 1
        end
    end
end

-- 移除空的div元素(包括event-label和choice-container)
local function removeEmptyDivs(text)
    if not text or type(text) ~= "string" then
        return text
    end

    -- 移除空的event-label div(只包含空白字符、换行符或完全为空)
    -- 这些模式会匹配所有event-label类的div,包括带有show类的

    -- 模式1:完全为空的event-label div
    local pattern1 = '<div%s+class="event%-label[^"]*"[^>]*></div>'
    text = text:gsub(pattern1, '')

    -- 模式2:只包含空白字符的event-label div
    local pattern2 = '<div%s+class="event%-label[^"]*"[^>]*>%s+</div>'
    text = text:gsub(pattern2, '')

    -- 模式3:只包含换行符和空白字符的event-label div
    local pattern3 = '<div%s+class="event%-label[^"]*"[^>]*>[\r\n%s]*</div>'
    text = text:gsub(pattern3, '')

    -- 移除空的choice-container div
    -- 模式4:完全为空的choice-container div
    local pattern4 = '<div%s+class="choice%-container"[^>]*></div>'
    text = text:gsub(pattern4, '')

    -- 模式5:只包含空白字符的choice-container div
    local pattern5 = '<div%s+class="choice%-container"[^>]*>%s+</div>'
    text = text:gsub(pattern5, '')

    -- 模式6:只包含换行符和空白字符的choice-container div
    local pattern6 = '<div%s+class="choice%-container"[^>]*>[\r\n%s]*</div>'
    text = text:gsub(pattern6, '')

    return text
end

-- 处理性别分隔的对话(处理"^"分隔符)
-- 注意:这个函数现在处理已经切割后的单个对话片段,所以逻辑大大简化
processGenderDialogue = function(text, template, characterName)
    if not template then
        template = "Say"
    end

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

    -- 检查是否包含 ${男性内容^女性内容}$ 格式
    local hasInlineGender = text:find("%$%{[^%^}]+%^[^}]+%}%$")

    if hasInlineGender then
        -- 处理 ${男性内容^女性内容}$ 格式
        -- 需要生成两个完整的句子
        local maleText = text:gsub("%$%{([^%^}]+)%^[^}]+%}%$", "%1")
        local femaleText = text:gsub("%$%{[^%^}]+%^([^}]+)%}%$", "%1")

        -- 处理表情参数和清理文本,然后创建对话
        local maleCleanedText, malePortrait = parsePortraitParameter(maleText)
        maleCleanedText = cleanText(maleCleanedText) .. '<span class="trigger-chance">(男)</span>'
        local maleResult = createQuote(maleCleanedText, template, malePortrait, characterName)

        local femaleCleanedText, femalePortrait = parsePortraitParameter(femaleText)
        femaleCleanedText = cleanText(femaleCleanedText) .. '<span class="trigger-chance">(女)</span>'
        local femaleResult = createQuote(femaleCleanedText, template, femalePortrait, characterName)

        return maleResult .. "\n" .. femaleResult
    end

    -- 如果没有性别分支,直接处理(这种情况理论上不应该发生,因为调用前已经检查过)
    if not text:find("%^") then
        local cleanedText, portrait = parsePortraitParameter(text)
        cleanedText = cleanText(cleanedText)
        return createQuote(cleanedText, template, portrait, characterName)
    end

    -- 简化的性别分支处理:现在只需要处理单个片段,不会有换行符
    -- 直接处理 ^ 分隔符
    local caretPos = text:find("%^")

    -- 分离男性和女性内容
    local beforeCaret = text:sub(1, caretPos - 1)
    local afterCaret = text:sub(caretPos + 1)

    -- 清理前后空白
    local maleText = beforeCaret:match("^%s*(.-)%s*$") or ""
    local femaleText = afterCaret:match("^%s*(.-)%s*$") or ""

    mw.log(maleText, femaleText)

    -- 处理表情参数和清理文本,然后创建对话
    local maleCleanedText, malePortrait = parsePortraitParameter(maleText)
    maleCleanedText = cleanText(maleCleanedText) .. '<span class="trigger-chance">(男)</span>'
    local maleResult = createQuote(maleCleanedText, template, malePortrait, characterName)

    local femaleCleanedText, femalePortrait = parsePortraitParameter(femaleText)
    femaleCleanedText = cleanText(femaleCleanedText) .. '<span class="trigger-chance">(女)</span>'
    local femaleResult = createQuote(femaleCleanedText, template, femalePortrait, characterName)

    return maleResult .. "\n" .. femaleResult
end

-- 查找NPC回应
findNpcResponse = function(character, optionKey)
    -- 这里需要实现查找NPC回应的逻辑
    -- 暂时返回空,后续可以扩展
    return nil
end

-- 检测标签路径是否会执行到事件结束
local function doesLabelPathEndEvent(commands, startIndex, targetLabel)
    local visited = {}  -- 防止无限循环

    -- 递归检测函数
    local function checkLabelRecursive(labelName)
        if visited[labelName] then
            return false  -- 避免无限循环
        end
        visited[labelName] = true

        for i = startIndex, #commands do
            local command = commands[i]:match("^%s*(.-)%s*$")

            -- 如果遇到label命令,检查是否是目标标签
            if command:match("^label%s+") then
                local currentLabelName = command:match("^label%s+(.+)$")
                if currentLabelName == labelName then
                    -- 找到目标标签,继续查找后续命令
                    for j = i + 1, #commands do
                        local nextCommand = commands[j]:match("^%s*(.-)%s*$")

                        -- 如果遇到end命令,说明会执行到事件结束
                        if nextCommand:match("^end$") or nextCommand:match("^end%s+") then
                            return true
                        elseif nextCommand:match("^goto%s+") then
                            -- 如果遇到goto命令,递归检查goto的目标
                            local gotoLabels, isConditional = analyzeGotoCommand(nextCommand)
                            if not isConditional and #gotoLabels > 0 then
                                -- 无条件goto,递归检查所有目标标签
                                for _, gotoLabel in ipairs(gotoLabels) do
                                    if checkLabelRecursive(gotoLabel) then
                                        return true
                                    end
                                end
                                return false -- 无条件goto但没有找到结束路径
                            else
                                -- 条件goto或其他情况,继续检查当前路径
                                -- 这里不返回,继续循环检查后续命令
                            end
                        elseif nextCommand:match("^choose%s+") then
                            -- 遇到choose命令,停止查找(分支选择)
                            return false
                        elseif nextCommand:match("^label%s+") then
                            -- 遇到新的label,这是隐式跳转,递归检查
                            local nextLabelName = nextCommand:match("^label%s+(.+)$")
                            return checkLabelRecursive(nextLabelName)
                        end
                    end
                    return false
                end
            end
        end
        return false
    end

    -- 开始递归检查
    return checkLabelRecursive(targetLabel)
end

-- 查找标签后的goto命令,返回额外的标签(递归查找所有隐式跳转)
findGotoAfterLabel = function(commands, startIndex, targetLabel)
    local additionalLabels = {}
    local visited = {}  -- 防止无限循环

    -- 递归查找函数
    local function findLabelsRecursive(labelName)
        if visited[labelName] then
            return  -- 避免无限循环
        end
        visited[labelName] = true

        for i = startIndex, #commands do
            local command = commands[i]:match("^%s*(.-)%s*$")

            -- 如果遇到label命令,检查是否是目标标签
            if command:match("^label%s+") then
                local currentLabelName = command:match("^label%s+(.+)$")
                if currentLabelName == labelName then
                    -- 找到目标标签,继续查找后续的goto命令或检测隐式跳转
                    for j = i + 1, #commands do
                        local nextCommand = commands[j]:match("^%s*(.-)%s*$")

                        -- 如果遇到goto命令
                        if nextCommand:match("^goto%s+") then
                            local gotoLabels, isConditional = analyzeGotoCommand(nextCommand)
                            if not isConditional and #gotoLabels > 0 then
                                -- 无条件goto,添加标签并递归查找
                                for _, gotoLabel in ipairs(gotoLabels) do
                                    table.insert(additionalLabels, gotoLabel)
                                    findLabelsRecursive(gotoLabel)  -- 递归查找
                                end
                            end
                            return -- 找到goto命令后停止查找
                        elseif nextCommand:match("^end$") or nextCommand:match("^end%s+") then
                            -- 遇到end命令,停止查找
                            return
                        elseif nextCommand:match("^choose%s+") then
                            -- 遇到choose命令,停止查找
                            return
                        elseif nextCommand:match("^label%s+") then
                            -- 遇到新的label,这是隐式跳转
                            local nextLabelName = nextCommand:match("^label%s+(.+)$")
                            table.insert(additionalLabels, nextLabelName)
                            findLabelsRecursive(nextLabelName)  -- 递归查找
                            return
                        end
                    end
                    return
                end
            end
        end
    end

    -- 开始递归查找
    findLabelsRecursive(targetLabel)

    return additionalLabels
end

-- 查找choose命令后的隐式goto内容(用于"_"标签)
local function findImplicitGotoContent(allCommands, chooseCommandIndex)
    local implicitContent = {}
    local i = chooseCommandIndex + 1

    -- 从choose命令后开始查找,直到遇到关键词为止
    while i <= #allCommands do
        local command = allCommands[i]:match("^%s*(.-)%s*$")

        -- 如果遇到这些关键词,停止收集
        if command:match("^goto%s+") or
           command:match("^label%s+") then
            break
        elseif command:match("^end$") or command:match("^end%s+") then
            -- end命令也要包含在implicit_goto内容中,然后停止收集
            table.insert(implicitContent, command)
            i = i + 1
            break
        end

        -- 收集其他命令作为隐式goto的内容(包括choose命令)
        table.insert(implicitContent, command)
        i = i + 1
    end

    return implicitContent, i - 1  -- 返回内容和最后处理的命令索引
end

-- 处理choose命令
local function processChooseCommand(command, allCommands, commandIndex)
    -- choose "<question>" "<answer1>" <label1> "<answer2>" <label2> ...
    local content = command:match("^choose%s+(.+)$")
    if not content then
        return nil, nil
    end

    local result = {}
    local parts = {}
    local inQuote = false
    local current = ""

    -- 解析choose命令的参数
    for i = 1, #content do
        local char = content:sub(i, i)
        if char == '"' and (i == 1 or content:sub(i-1, i-1) ~= "\\") then
            inQuote = not inQuote
            if not inQuote then
                table.insert(parts, current)
                current = ""
            end
        elseif char == " " and not inQuote then
            if current ~= "" then
                table.insert(parts, current)
                current = ""
            end
        elseif inQuote then
            current = current .. char
        else
            current = current .. char
        end
    end
    if current ~= "" then
        table.insert(parts, current)
    end

    local skipToIndex = nil  -- 需要跳过的命令索引

    -- 检查隐式goto内容中是否有splitSpeak命令
    local hasSplitSpeak = false
    local splitSpeakDialogues = {}
    local splitSpeakCommandIndex = nil

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

        -- 显示问题(如果不为空)
        if question and question ~= "" then
            -- 检查是否为翻译键或Characters/Dialogue引用
            if question:match("^Strings/") or question:match("^Characters/Dialogue/") then
                question = getTranslationText(question)
            end
            table.insert(result, choice())
            -- 此处的对话应当不会有性别分支
            table.insert(result, processDialogueUnified(question, nil, nil))
        else
            table.insert(result, choice())
        end

        -- 检查是否有"_"标签的隐式goto
        local hasImplicitGoto = false
        local implicitGotoLabels = {}
        local implicitGotoContent = nil
        local implicitGotoEndIndex = nil

        -- 扫描所有选项,查找"_"标签
        local j = 2
        while j < #parts do
            local label = parts[j + 1]
            if label == "_" then
                hasImplicitGoto = true
                break
            end
            j = j + 2
        end

        -- 如果有隐式goto,处理相关内容
        if hasImplicitGoto and allCommands and commandIndex then
            implicitGotoContent, implicitGotoEndIndex = findImplicitGotoContent(allCommands, commandIndex)
            skipToIndex = implicitGotoEndIndex  -- 设置需要跳过的索引

            -- 在隐式goto内容中查找splitSpeak命令
            if implicitGotoContent then
                for cmdIndex, cmd in ipairs(implicitGotoContent) do
                    if cmd:match("^splitSpeak%s+") then
                        hasSplitSpeak = true
                        splitSpeakCommandIndex = commandIndex + cmdIndex

                        -- 解析splitSpeak命令
                        local splitSpeakContent = cmd:match("^splitSpeak%s+(.+)$")
                        if splitSpeakContent then
                            local splitParts = {}
                            local splitInQuote = false
                            local splitCurrent = ""

                            -- 解析splitSpeak的参数
                            for i = 1, #splitSpeakContent do
                                local char = splitSpeakContent:sub(i, i)
                                if char == '"' and (i == 1 or splitSpeakContent:sub(i-1, i-1) ~= "\\") then
                                    splitInQuote = not splitInQuote
                                    if not splitInQuote then
                                        table.insert(splitParts, splitCurrent)
                                        splitCurrent = ""
                                    end
                                elseif char == " " and not splitInQuote then
                                    if splitCurrent ~= "" then
                                        table.insert(splitParts, splitCurrent)
                                        splitCurrent = ""
                                    end
                                elseif splitInQuote then
                                    splitCurrent = splitCurrent .. char
                                else
                                    splitCurrent = splitCurrent .. char
                                end
                            end
                            if splitCurrent ~= "" then
                                table.insert(splitParts, splitCurrent)
                            end

                            -- 第一个参数是角色名,后面的是对话内容
                            if #splitParts >= 2 then
                                local character = splitParts[1]
                                for i = 2, #splitParts do
                                    local dialogue = splitParts[i]
                                    if dialogue:match("^Strings/") or dialogue:match("^Characters/Dialogue/") then
                                        dialogue = getTranslationText(dialogue)
                                    end
                                    table.insert(splitSpeakDialogues, {character = character, text = dialogue})
                                end
                            end
                        end
                        break
                    end
                end
            end

            -- 为所有"_"标签使用同一个标签名,因为它们的内容是相同的
            local sharedImplicitLabel = "implicit_goto_" .. commandIndex
            j = 2
            while j < #parts do
                local label = parts[j + 1]
                if label == "_" then
                    implicitGotoLabels[j] = sharedImplicitLabel
                end
                j = j + 2
            end
        end

        -- 处理选项
        j = 2
        local optionIndex = 1  -- 用于splitSpeak对话的索引
        while j < #parts do
            local answer = parts[j]
            local label = parts[j + 1]

            if answer and label then
                -- 检查是否为翻译键或Characters/Dialogue引用
                if answer:match("^Strings/") or answer:match("^Characters/Dialogue/") then
                    answer = getTranslationText(answer)
                end

                local finalLabel = label
                local additionalLabels = {}

                -- 如果有splitSpeak且是"_"标签,创建特殊的标签来处理splitSpeak对话
                if hasSplitSpeak and label == "_" then
                    finalLabel = "splitspeak_option_" .. optionIndex .. "_" .. commandIndex

                    -- splitSpeak后面会继续接着通用对话,所以需要添加implicit_goto标签
                    if implicitGotoLabels[j] then
                        table.insert(additionalLabels, implicitGotoLabels[j])
                    end

                    -- 查找隐式goto内容后的goto命令
                    if implicitGotoEndIndex and allCommands then
                        -- 检查隐式内容结束后是否有goto命令
                        if implicitGotoEndIndex + 1 <= #allCommands then
                            local nextCommand = allCommands[implicitGotoEndIndex + 1]:match("^%s*(.-)%s*$")
                            if nextCommand:match("^goto%s+") then
                                local gotoLabels, isConditional = analyzeGotoCommand(nextCommand)
                                if not isConditional then
                                    for _, gotoLabel in ipairs(gotoLabels) do
                                        table.insert(additionalLabels, gotoLabel)
                                        -- 递归查找后续的goto
                                        local moreLabels = findGotoAfterLabel(allCommands, implicitGotoEndIndex + 2, gotoLabel)
                                        for _, moreLabel in ipairs(moreLabels) do
                                            table.insert(additionalLabels, moreLabel)
                                        end
                                    end
                                end
                                -- 如果有goto命令,也需要跳过它
                                skipToIndex = implicitGotoEndIndex + 1
                            end
                        end
                    end
                elseif label == "_" and implicitGotoLabels[j] then
                    -- 如果是"_"标签但没有splitSpeak,使用生成的隐式标签
                    finalLabel = implicitGotoLabels[j]

                    -- 查找隐式goto内容后的goto命令
                    if implicitGotoEndIndex and allCommands then
                        -- 检查隐式内容结束后是否有goto命令
                        if implicitGotoEndIndex + 1 <= #allCommands then
                            local nextCommand = allCommands[implicitGotoEndIndex + 1]:match("^%s*(.-)%s*$")
                            if nextCommand:match("^goto%s+") then
                                local gotoLabels, isConditional = analyzeGotoCommand(nextCommand)
                                if not isConditional then
                                    for _, gotoLabel in ipairs(gotoLabels) do
                                        table.insert(additionalLabels, gotoLabel)
                                        -- 递归查找后续的goto
                                        local moreLabels = findGotoAfterLabel(allCommands, implicitGotoEndIndex + 2, gotoLabel)
                                        for _, moreLabel in ipairs(moreLabels) do
                                            table.insert(additionalLabels, moreLabel)
                                        end
                                    end
                                end
                                -- 如果有goto命令,也需要跳过它
                                skipToIndex = implicitGotoEndIndex + 1
                            end
                        end
                    end
                else
                    -- 普通标签,查找标签后的goto命令和隐式跳转
                    if allCommands and commandIndex then
                        additionalLabels = findGotoAfterLabel(allCommands, commandIndex + 1, label)
                    end
                end

                -- 构建完整的标签列表
                local allLabels = {finalLabel}
                for _, additionalLabel in ipairs(additionalLabels) do
                    table.insert(allLabels, additionalLabel)
                end

                -- 检查这个选项是否会执行到事件结束,如果是则添加event-reactions
                if allCommands and commandIndex then
                    local willEndEvent = false

                    -- 检查主标签是否会执行到事件结束
                    if doesLabelPathEndEvent(allCommands, commandIndex + 1, finalLabel) then
                        willEndEvent = true
                    else
                        -- 检查所有附加标签是否会执行到事件结束
                        for _, additionalLabel in ipairs(additionalLabels) do
                            if doesLabelPathEndEvent(allCommands, commandIndex + 1, additionalLabel) then
                                willEndEvent = true
                                break
                            end
                        end
                    end

                    -- 如果会执行到事件结束,添加event-reactions
                    if willEndEvent then
                        table.insert(allLabels, "event-reactions")
                    end
                end

                local labelString = table.concat(allLabels, " ")

                -- 分析label中的好感度变化
                local friendship, character = analyzeLabelFriendship(label == "_" and finalLabel or label, allCommands, commandIndex)
                local characterName = nil
                if character then
                    characterName = getCharacterName(character)
                end

                local choiceTemplate = createPlayerChoice(answer, friendship, characterName)
                -- 使用完整的标签列表作为goto目标
                table.insert(result, wrapInText(choiceTemplate, labelString))
            end

            j = j + 2
            optionIndex = optionIndex + 1  -- 递增选项索引
        end

        table.insert(result, div())

        -- 如果有splitSpeak,为每个选项创建对应的event-label
        if hasSplitSpeak and #splitSpeakDialogues > 0 then
            for i = 1, #splitSpeakDialogues do
                local dialogue = splitSpeakDialogues[i]
                local labelName = "splitspeak_option_" .. i .. "_" .. commandIndex

                table.insert(result, '<div class="event-label" data-label="' .. labelName .. '" style="display:none;">')

                -- 处理对话文本
                local processedDialogue = processDialogueUnified(dialogue.text, nil, getCharacterName(dialogue.character))
                table.insert(result, processedDialogue)

                table.insert(result, '</div>')
            end
        end

        -- 如果有隐式goto内容,添加对应的event-label(放在splitSpeak后面)
        if hasImplicitGoto and implicitGotoContent then
            -- 使用一个集合来跟踪已经创建的标签,避免重复
            local createdLabels = {}
            for labelIndex, implicitLabel in pairs(implicitGotoLabels) do
                -- 如果这个标签已经创建过,跳过
                if not createdLabels[implicitLabel] then
                    createdLabels[implicitLabel] = true

                    -- 处理隐式goto的内容
                    local implicitDialogue = {}
                    for cmdIndex, cmd in ipairs(implicitGotoContent) do
                        -- 跳过splitSpeak命令,因为它已经单独处理了
                        if cmd:match("^splitSpeak%s+") then
                            -- 跳过splitSpeak命令,不处理
                        -- 处理speak命令
                        elseif cmd:match("^speak%s+") then
                            local character, text = cmd:match("^speak%s+([^%s]+)%s+\"(.*)\"$")
                            if character and text then
                                if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                                    text = getTranslationText(text)
                                end
                                table.insert(implicitDialogue, processDialogueUnified(text, nil, getCharacterName(character)))
                            end
                        -- 处理message命令
                        elseif cmd:match("^message%s+") then
                            local messageText = cmd:match("^message%s+\"(.*)\"$")
                            if messageText then
                                if messageText:match("^Strings/") or messageText:match("^Characters/Dialogue/") then
                                    messageText = getTranslationText(messageText)
                                else
                                    messageText = cleanText(messageText)
                                end
                                if not messageText:find(":") then
                                    messageText = messageText -- "(旁白)"
                                end
                                table.insert(implicitDialogue, processDialogueUnified(messageText, nil, nil))
                            end
                        -- 处理dialogue命令
                        elseif cmd:match("^dialogue%s+") then
                            local character, text = cmd:match("^dialogue%s+([^%s]+)%s+\"(.*)\"$")
                            if character and text then
                                if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                                    text = getTranslationText(text)
                                else
                                    text = cleanText(text)
                                end
                                table.insert(implicitDialogue, processDialogueUnified(text, nil, getCharacterName(character)))
                            end
                        -- 处理textAboveHead命令
                        elseif cmd:match("^textAboveHead%s+") then
                            local actor, headText = cmd:match("^textAboveHead%s+([^%s]+)%s+\"(.*)\"$")
                            if actor and headText then
                                if headText:match("^Strings/") or headText:match("^Characters/Dialogue/") then
                                    headText = getTranslationText(headText)
                                else
                                    headText = cleanText(headText)
                                end
                                table.insert(implicitDialogue, processDialogueUnified(headText, nil, getCharacterName(actor) .. "(对话气泡)"))
                            end
                        -- 处理choose命令
                        elseif cmd:match("^choose%s+") then
                            -- 处理choose命令,需要传递完整的命令列表以便正确分析好感度
                            -- 隐式goto内容从第一个choose命令后开始,所以实际索引是 commandIndex + cmdIndex
                            local actualCommandIndex = commandIndex + cmdIndex
                            local choiceResult, _ = processChooseCommand(cmd, allCommands, actualCommandIndex)
                            if choiceResult then
                                for _, line in ipairs(choiceResult) do
                                    table.insert(implicitDialogue, line)
                                end
                            end
                        -- 处理end命令
                        elseif cmd:match("^end$") or cmd:match("^end%s+") then
                            table.insert(implicitDialogue, "[ 事件结束 ]")
                        -- 可以添加其他命令的处理
                        end
                    end

                    -- 创建event-label包装隐式goto内容
                    if #implicitDialogue > 0 then
                        table.insert(result, '<div class="event-label" data-label="' .. implicitLabel .. '" style="display:none;">')
                        for _, dialogue in ipairs(implicitDialogue) do
                            table.insert(result, dialogue)
                        end
                        table.insert(result, '</div>')
                    end
                end
            end
        end
    end

    return result, skipToIndex
end

-- 处理cutscene命令
local function processCutsceneCommand(command, allCommands, commandIndex)
    -- cutscene <minigame> [<label1> <label2> ...]
    local content = command:match("^cutscene%s+(.+)$")
    if not content then
        return nil
    end

    local parts = {}
    -- 简单按空格分割参数(cutscene命令的参数不包含引号)
    for word in content:gmatch("%S+") do
        table.insert(parts, word)
    end

    -- 如果只有一个参数(如 cutscene marucomet),这只是播放动画,不需要创建选项
    if #parts < 2 then
        return nil
    end

    local minigame = parts[1]
    local result = {}

    -- 创建选项容器
    table.insert(result, '<div class="choice-container">')

    -- 处理每个标签选项
    for i = 2, #parts do
        local label = parts[i]
        local cutsceneKey = minigame .. "_" .. label
        local description = cutsceneLabels[cutsceneKey] or ("选择:" .. label)

        -- 查找标签后的goto命令和隐式跳转
        local additionalLabels = {}
        if allCommands and commandIndex then
            additionalLabels = findGotoAfterLabel(allCommands, commandIndex + 1, label)
        end

        -- 构建完整的标签列表
        local allLabels = {label}
        for _, additionalLabel in ipairs(additionalLabels) do
            table.insert(allLabels, additionalLabel)
        end

        -- 检查这个选项是否会执行到事件结束,如果是则添加event-reactions
        if allCommands and commandIndex then
            local willEndEvent = false

            -- 检查主标签是否会执行到事件结束
            if doesLabelPathEndEvent(allCommands, commandIndex + 1, label) then
                willEndEvent = true
            else
                -- 检查所有附加标签是否会执行到事件结束
                for _, additionalLabel in ipairs(additionalLabels) do
                    if doesLabelPathEndEvent(allCommands, commandIndex + 1, additionalLabel) then
                        willEndEvent = true
                        break
                    end
                end
            end

            -- 如果会执行到事件结束,添加event-reactions
            if willEndEvent then
                table.insert(allLabels, "event-reactions")
            end
        end

        local labelString = table.concat(allLabels, " ")

        -- 创建选项
        table.insert(result, '<div class="choice-option" data-goto="' .. labelString .. '">')
        table.insert(result, '<p>' .. description .. '</p>')
        table.insert(result, '</div>')
    end

    table.insert(result, '</div>')

    return result
end

-- 处理conditionSpeak命令
local function processConditionSpeakCommand(command)
    -- conditionSpeak <npc> [<dialogue> [condition]]+
    local content = command:match("^conditionSpeak%s+(.+)$")
    if not content then
        return nil
    end

    local parts = {}
    local inQuote = false
    local current = ""

    -- 解析参数,处理引号
    for i = 1, #content do
        local char = content:sub(i, i)
        if char == '"' and (i == 1 or content:sub(i-1, i-1) ~= "\\") then
            inQuote = not inQuote
            if not inQuote then
                table.insert(parts, current)
                current = ""
            end
        elseif char == " " and not inQuote then
            if current ~= "" then
                table.insert(parts, current)
                current = ""
            end
        elseif inQuote then
            current = current .. char
        else
            current = current .. char
        end
    end
    if current ~= "" then
        table.insert(parts, current)
    end

    if #parts >= 2 then
        local character = parts[1]:match("^%s*(.-)%s*$")
        local dialogue = parts[2]

        if character and dialogue then
            -- 检查是否为翻译键或Characters/Dialogue引用
            if dialogue:match("^Strings/") or dialogue:match("^Characters/Dialogue/") then
                dialogue = getTranslationText(dialogue)
            else
                dialogue = cleanText(dialogue)
            end

            -- 处理性别分支
            return processDialogueUnified(dialogue, nil, getCharacterName(character))
        end
    end

    return nil
end

-- 处理事件对话,过滤掉指令,只保留文本内容
processEventDialogue = function(eventScript, eventLocation, eventId)
    if not eventScript or type(eventScript) ~= "string" then
        return eventScript
    end

    -- 事件脚本由斜杠分隔的命令组成
    local commands = {}
    local currentPos = 1
    local inQuotes = false

    -- Label管理变量
    local currentLabel = nil
    local labelStack = {}
    local currentCommand = ""
    
    -- 解析命令,考虑引号内的内容
    for i = 1, #eventScript do
        local char = eventScript:sub(i, i)
        if char == '"' and (i == 1 or eventScript:sub(i - 1, i - 1) ~= "\\") then
            inQuotes = not inQuotes
            currentCommand = currentCommand .. char
        elseif char == "/" and not inQuotes then
            if currentCommand ~= "" then
                table.insert(commands, currentCommand)
                currentCommand = ""
            end
        else
            currentCommand = currentCommand .. char
        end
    end
    
    -- 添加最后一个命令
    if currentCommand ~= "" then
        table.insert(commands, currentCommand)
    end
    
    local dialogueTexts = {}
    local currentLocation = eventLocation
    local i = 1
    local hasChoiceContainer = false
    local preChoiceContentAdded = false

    -- 检查是否有choose命令或条件goto命令
    for _, cmd in ipairs(commands) do
        local trimmedCmd = cmd:match("^%s*(.-)%s*$")
        if trimmedCmd:match("^choose%s+") then
            hasChoiceContainer = true
            break
        elseif trimmedCmd:match("^goto%s+") and trimmedCmd:find('"') then
            -- 条件goto命令也会产生选择
            hasChoiceContainer = true
            break
        end
    end

    -- 处理命令序列
    while i <= #commands do
        local trimmedCommand = commands[i]:match("^%s*(.-)%s*$")
        
        -- 处理speak命令:speak <character> "<text>"
        if trimmedCommand:match("^speak%s+") then
            local character, text = trimmedCommand:match("^speak%s+([^%s]+)%s+\"(.*)\"$")
            if character and text then
                -- 检查是否为翻译键或Characters/Dialogue引用
                if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                    text = getTranslationText(text)
                end

                -- 处理性别分支
                table.insert(dialogueTexts, processDialogueUnified(text, nil, getCharacterName(character)))
            end
            i = i + 1
            
        -- 处理choose命令(1.6.16新命令)
        elseif trimmedCommand:match("^choose%s+") then
            -- 如果不在label内且有choose命令且还没有添加预选择内容包装,现在添加
            if not currentLabel and hasChoiceContainer and not preChoiceContentAdded then
                -- 将之前的内容包装在默认显示的event-label中
                if #dialogueTexts > 0 then
                    local preChoiceContent = table.concat(dialogueTexts, "\n")
                    -- 检查内容是否为空(去除空白字符后)
                    if preChoiceContent:match("^%s*$") == nil then
                        dialogueTexts = {}
                        table.insert(dialogueTexts, '<div class="event-label show" style="display: block;">')
                        table.insert(dialogueTexts, preChoiceContent)
                        table.insert(dialogueTexts, '</div>')
                    else
                        dialogueTexts = {}
                    end
                end
                preChoiceContentAdded = true
            end

            local choiceResult, skipToIndex = processChooseCommand(trimmedCommand, commands, i)
            if choiceResult then
                -- 如果在label内,choice-container应该包含在当前label内
                -- 如果不在label内,choice-container会被包装在预选择内容中或直接添加
                for _, line in ipairs(choiceResult) do
                    table.insert(dialogueTexts, line)
                end
            end

            -- 如果有需要跳过的索引,跳过隐式goto内容
            if skipToIndex and skipToIndex > i then
                i = skipToIndex + 1
            else
                i = i + 1
            end
            
        -- 处理goto命令(1.6.16新命令)
        elseif trimmedCommand:match("^goto%s+") then
            -- 检查是否是条件goto(包含引号)
            if trimmedCommand:find('"') then
                -- 如果不在label内且有条件goto命令且还没有添加预选择内容包装,现在添加
                if not currentLabel and hasChoiceContainer and not preChoiceContentAdded then
                    -- 将之前的内容包装在默认显示的event-label中
                    if #dialogueTexts > 0 then
                        local preChoiceContent = table.concat(dialogueTexts, "\n")
                        -- 检查内容是否为空(去除空白字符后)
                        if preChoiceContent:match("^%s*$") == nil then
                            dialogueTexts = {}
                            table.insert(dialogueTexts, '<div class="event-label show" style="display: block;">')
                            table.insert(dialogueTexts, preChoiceContent)
                            table.insert(dialogueTexts, '</div>')
                        else
                            dialogueTexts = {}
                        end
                    end
                    preChoiceContentAdded = true
                end

                local conditionalResult = processConditionalGoto(trimmedCommand, eventId, commands, i)
                if conditionalResult then
                    table.insert(dialogueTexts, conditionalResult)
                end
            else
                -- 简单goto命令 - 不显示跳转信息,因为它会在选项的data-label中处理
                -- 这里什么都不做,让goto命令静默执行
            end
            i = i + 1
            
        -- 处理label命令(1.6.16新命令)
        elseif trimmedCommand:match("^label%s+") then
            local labelName = trimmedCommand:match("^label%s+(.+)$")
            if labelName then
                -- 检查是否是skippedItem标签,如果是则跳过到下一个/end
                if labelName == "skippedItem" then
                    -- 跳过skippedItem标签的所有内容直到找到/end
                    local skipIndex = i + 1
                    while skipIndex <= #commands do
                        local skipCommand = commands[skipIndex]:match("^%s*(.-)%s*$")
                        if skipCommand:match("^end$") or skipCommand:match("^end%s+") then
                            -- 找到end命令,检查是否有end dialogue内容需要处理
                            local endContent = skipCommand:match("^end%s+(.+)$")
                            if endContent and endContent:match("^dialogue%s+") then
                                -- 将end dialogue内容添加到前面的内容中
                                local character, text = endContent:match("^dialogue%s+([^%s]+)%s+\"(.*)\"$")
                                if character and text then
                                    -- 检查是否为翻译键或Characters/Dialogue引用
                                    if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                                        text = getTranslationText(text)
                                    else
                                        text = text:gsub("\\\"", "\"")
                                        text = text:gsub("\\n", "\n")
                                    end
                                    table.insert(dialogueTexts, processDialogueUnified(text, nil, getCharacterName(character)))
                                end
                            end
                            -- 添加事件结束标记到前面的内容中
                            table.insert(dialogueTexts, "[ 事件结束 ]")
                            -- 跳过到end命令之后
                            i = skipIndex + 1
                            break
                        end
                        skipIndex = skipIndex + 1
                    end
                    -- 如果没找到end命令,跳过到最后
                    if skipIndex > #commands then
                        i = #commands + 1
                    end
                else
                    -- 正常处理其他标签
                    -- 如果之前有未关闭的label,需要检查是否应该添加隐式goto
                    if currentLabel then
                        -- 检查前一个命令是否是end、goto或choose,如果不是,则添加隐式goto
                        local shouldAddImplicitGoto = true
                        if i > 1 then
                            local prevCommand = commands[i - 1]:match("^%s*(.-)%s*$")
                            if prevCommand:match("^end$") or prevCommand:match("^end%s+") or
                               prevCommand:match("^goto%s+") or prevCommand:match("^choose%s+") then
                                shouldAddImplicitGoto = false
                            end
                        end

                        -- 总是关闭之前的label并开始新的label
                        -- 隐式跳转将在findGotoAfterLabel中检测
                        table.insert(dialogueTexts, '</div>')
                        currentLabel = labelName
                        table.insert(dialogueTexts, '<div class="event-label" data-label="' .. labelName .. '" style="display:none;">')
                    else
                        -- 开始一个新的label区域
                        currentLabel = labelName
                        table.insert(dialogueTexts, '<div class="event-label" data-label="' .. labelName .. '" style="display:none;">')
                    end
                    i = i + 1
                end
            else
                i = i + 1
            end
            
        -- 处理conditionSpeak命令(1.6.16新命令)
        elseif trimmedCommand:match("^conditionSpeak%s+") then
            local conditionResult = processConditionSpeakCommand(trimmedCommand)
            if conditionResult then
                table.insert(dialogueTexts, conditionResult)
            end
            i = i + 1

        -- 处理splitSpeak命令(兼容旧版本,但通常与choose命令一起使用)
        elseif trimmedCommand:match("^splitSpeak%s+") then
            -- splitSpeak通常在choose命令中处理,这里只是为了兼容性
            -- 如果单独出现,可以作为普通对话处理
            local splitSpeakContent = trimmedCommand:match("^splitSpeak%s+(.+)$")
            if splitSpeakContent then
                local splitParts = {}
                local splitInQuote = false
                local splitCurrent = ""

                -- 解析splitSpeak的参数
                for j = 1, #splitSpeakContent do
                    local char = splitSpeakContent:sub(j, j)
                    if char == '"' and (j == 1 or splitSpeakContent:sub(j-1, j-1) ~= "\\") then
                        splitInQuote = not splitInQuote
                        if not splitInQuote then
                            table.insert(splitParts, splitCurrent)
                            splitCurrent = ""
                        end
                    elseif char == " " and not splitInQuote then
                        if splitCurrent ~= "" then
                            table.insert(splitParts, splitCurrent)
                            splitCurrent = ""
                        end
                    elseif splitInQuote then
                        splitCurrent = splitCurrent .. char
                    else
                        splitCurrent = splitCurrent .. char
                    end
                end
                if splitCurrent ~= "" then
                    table.insert(splitParts, splitCurrent)
                end

                -- 第一个参数是角色名,后面的是对话内容
                if #splitParts >= 2 then
                    local character = splitParts[1]
                    for j = 2, #splitParts do
                        local dialogue = splitParts[j]
                        if dialogue:match("^Strings/") or dialogue:match("^Characters/Dialogue/") then
                            dialogue = getTranslationText(dialogue)
                        end
                        table.insert(dialogueTexts, processDialogueUnified(dialogue, nil, getCharacterName(character)))
                    end
                end
            end
            i = i + 1

        -- 处理message命令:message "<text>"
        elseif trimmedCommand:match("^message%s+") then
            local messageText = trimmedCommand:match("^message%s+\"(.*)\"$")
            if messageText then
                -- 检查是否为翻译键或Characters/Dialogue引用
                if messageText:match("^Strings/") or messageText:match("^Characters/Dialogue/") then
                    messageText = getTranslationText(messageText)
                else
                    messageText = cleanText(messageText)
                end

                if not messageText:find(":") then
                    messageText = messageText -- "(旁白)"
                end

                -- 处理性别分支
                table.insert(dialogueTexts, processDialogueUnified(messageText, nil, nil))
            end
            i = i + 1

        -- 处理textAboveHead命令:textAboveHead <actor> "<text>"
        elseif trimmedCommand:match("^textAboveHead%s+") then
            local actor, headText = trimmedCommand:match("^textAboveHead%s+([^%s]+)%s+\"(.*)\"$")
            if actor and headText then
                -- 检查是否为翻译键或Characters/Dialogue引用
                if headText:match("^Strings/") or headText:match("^Characters/Dialogue/") then
                    headText = getTranslationText(headText)
                else
                    headText = cleanText(headText)
                end

                -- 处理性别分支
                table.insert(dialogueTexts, processDialogueUnified(headText, nil, getCharacterName(actor) .. "(对话气泡)"))
            end
            i = i + 1

        -- 处理dialogue命令:dialogue <character> "<text>"
        elseif trimmedCommand:match("^dialogue%s+") then
            local character, text = trimmedCommand:match("^dialogue%s+([^%s]+)%s+\"(.*)\"$")
            if character and text then
                -- 检查是否为翻译键或Characters/Dialogue引用
                if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                    text = getTranslationText(text)
                else
                    text = cleanText(text)
                end

                -- 处理性别分支
                table.insert(dialogueTexts, processDialogueUnified(text, nil, getCharacterName(character)))
            end
            i = i + 1

        -- 处理cutscene命令:cutscene <minigame> [<label1> <label2> ...]
        elseif trimmedCommand:match("^cutscene%s+") then
            local cutsceneResult = processCutsceneCommand(trimmedCommand, commands, i)

            -- 只有当cutscene命令返回选项时才进行预选择内容包装
            if cutsceneResult then
                -- 如果不在label内且有cutscene命令且还没有添加预选择内容包装,现在添加
                if not currentLabel and hasChoiceContainer and not preChoiceContentAdded then
                    -- 将之前的内容包装在默认显示的event-label中
                    if #dialogueTexts > 0 then
                        local preChoiceContent = table.concat(dialogueTexts, "\n")
                        -- 检查内容是否为空(去除空白字符后)
                        if preChoiceContent:match("^%s*$") == nil then
                            dialogueTexts = {}
                            table.insert(dialogueTexts, '<div class="event-label show" style="display: block;">')
                            table.insert(dialogueTexts, preChoiceContent)
                            table.insert(dialogueTexts, '</div>')
                        else
                            dialogueTexts = {}
                        end
                    end
                    preChoiceContentAdded = true
                end

                for _, line in ipairs(cutsceneResult) do
                    table.insert(dialogueTexts, line)
                end
            end
            -- 如果cutsceneResult为nil,说明这是一个简单的动画播放命令,不需要特殊处理
            i = i + 1

        -- 处理end命令
        elseif trimmedCommand:match("^end$") or trimmedCommand:match("^end%s+") then
            -- 检查是否已经在skippedItem处理中添加了事件结束标记
            local alreadyHasEndMarker = false
            if #dialogueTexts > 0 then
                local lastText = dialogueTexts[#dialogueTexts]
                if lastText == "[ 事件结束 ]" then
                    alreadyHasEndMarker = true
                end
            end

            if not alreadyHasEndMarker then
                local endContent = trimmedCommand:match("^end%s+(.+)$")
                table.insert(dialogueTexts, "[ 事件结束 ]")

                -- 处理end dialogue命令
                if endContent and endContent:match("^dialogue%s+") then
                    -- 解析dialogue命令:dialogue <character> "<text>"
                    local character, text = endContent:match("^dialogue%s+([^%s]+)%s+\"(.*)\"$")
                    if character and text then
                        -- 检查是否为翻译键或Characters/Dialogue引用
                        if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                            text = getTranslationText(text)
                        else
                            -- 处理普通文本的转义字符
                            text = text:gsub("\\\"", "\"")
                            text = text:gsub("\\n", "\n")
                        end

                        -- 创建一个新的event-label来包装事件结束后的对话
                        table.insert(dialogueTexts, '<div class="event-label" style="display: block;">')
                        table.insert(dialogueTexts, '<p><strong>事件后续对话</strong></p>')
                        table.insert(dialogueTexts, processDialogueUnified(text, nil, getCharacterName(character)))
                        table.insert(dialogueTexts, '</div>')
                    end
                end
            else
                -- 如果已经有事件结束标记,只处理end dialogue命令
                local endContent = trimmedCommand:match("^end%s+(.+)$")
                if endContent and endContent:match("^dialogue%s+") then
                    -- 解析dialogue命令:dialogue <character> "<text>"
                    local character, text = endContent:match("^dialogue%s+([^%s]+)%s+\"(.*)\"$")
                    if character and text then
                        -- 检查是否为翻译键或Characters/Dialogue引用
                        if text:match("^Strings/") or text:match("^Characters/Dialogue/") then
                            text = getTranslationText(text)
                        else
                            -- 处理普通文本的转义字符
                            text = text:gsub("\\\"", "\"")
                            text = text:gsub("\\n", "\n")
                        end

                        -- 创建一个新的event-label来包装事件结束后的对话
                        table.insert(dialogueTexts, '<div class="event-label" style="display: block;">')
                        table.insert(dialogueTexts, '<p><strong>事件后续对话</strong></p>')
                        table.insert(dialogueTexts, processDialogueUnified(text, nil, getCharacterName(character)))
                        table.insert(dialogueTexts, '</div>')
                    end
                end
            end

            -- 如果有当前label,立即关闭它,确保事件结束标签在容器内
            if currentLabel then
                table.insert(dialogueTexts, '</div>')
                currentLabel = nil
            end
            i = i + 1
            
        else
            i = i + 1
        end
    end
    
    -- 关闭任何未关闭的label区域
    if currentLabel then
        table.insert(dialogueTexts, '</div>')
        currentLabel = nil
    end

    -- 如果没有choose命令但有对话内容,包装在默认显示的event-label中
    if not hasChoiceContainer and #dialogueTexts > 0 then
        local allContent = table.concat(dialogueTexts, "\n")
        -- 检查内容是否为空(去除空白字符后)
        if allContent:match("^%s*$") == nil then
            dialogueTexts = {}
            table.insert(dialogueTexts, '<div class="event-label show" style="display: block;">')
            table.insert(dialogueTexts, allContent)
            table.insert(dialogueTexts, '</div>')
        else
            dialogueTexts = {}
        end
    end

    -- 如果没有找到对话文本,返回原始内容的简化版本
    if #dialogueTexts == 0 then
        return "(事件脚本,包含复杂指令)"
    end

    removeConsecutiveEmptyStrings(dialogueTexts)
    local result = table.concat(dialogueTexts, "\n")

    -- 移除空的div元素
    result = removeEmptyDivs(result)

    -- 包装在事件容器中
    -- 移动了
    if result and result ~= "" then
        result = result
    end

    return result
end

-- 根据事件编号和地点查找并处理事件对话
p.event = function(frame)
    local args = frame.args or frame
    local eventId = args[1]
    local location = args[2]
    local mainOnly = args[3] or nil
    
    if not mainOnly or mainOnly == "" then
        mainOnly = false
    elseif mainOnly == "否" then
        mainOnly = true
    else
        mainOnly = false
    end
    
    if not eventId or eventId == "" then
        return "错误:请提供事件编号"
    end
    
    local mainResult = ""
    local foundLocation = ""
    local foundEventScript = ""
    
    if not location or location == "" then
        -- 在所有地点查找事件
        if not EventsData then
            return "错误:EventsData未加载"
        end
        
        for locationName, locationData in pairs(EventsData) do
            if type(locationData) == "table" then
                for eventKey, eventScript in pairs(locationData) do
                    -- 检查事件key是否包含指定的事件编号
                    if eventKey:match("^" .. eventId .. "/") or eventKey == eventId then
                        local result = processEventDialogue(eventScript, locationName, eventId)
                        if result and result ~= "(事件脚本,包含复杂指令)" then
                            mainResult = "<!-- 事件 " .. eventId .. " (位置: " .. locationName .. ") -->" .. result
                            foundLocation = locationName
                            foundEventScript = eventScript
                            break
                        end
                    end
                end
                if mainResult ~= "" then
                    break
                end
            end
        end
        
        if mainResult == "" then
            return "未找到编号为 " .. eventId .. " 的事件"
        end
    else
        -- 在指定地点查找事件
        if not EventsData or not EventsData[location] then
            return "未找到地点为 " .. location .. " 的事件"
        end
        
        for key, eventScript in pairs(EventsData[location]) do
            -- 检查事件key是否包含指定的事件编号
            if key:match("^" .. eventId .. "/") or key == eventId then
                local result = processEventDialogue(eventScript, location, eventId)
                if result and result ~= "(事件脚本,包含复杂指令)" then
                    mainResult = "<!-- 事件 " .. eventId .. " (位置: " .. location .. ") -->" .. result
                    foundLocation = location
                    foundEventScript = eventScript
                    break
                end
            end
        end
        
        if mainResult == "" then
            return "在地点 " .. location .. " 未找到编号为 " .. eventId .. " 的事件"
        end
    end
    
    -- 查找事件反应对话
    local reactions = findEventReactions(eventId)
    local reactionsText = formatEventReactions(eventId, reactions, foundEventScript)

    if mainOnly then

        if reactionsText and reactionsText ~= '' then
            reactionsText = "\n<div class=\"event-label show\" data-label=\"event-reactions\" style=\"display:block;\">\n" .. reactionsText .. "\n</div>" -- <p>'''事件的后续'''</p>\n
        end

        frame:callParserFunction('#vardefine', {'event_reaction_' .. eventId, reactionsText})
        return '<div class="event-container" data-event="' .. (eventId or "unknown") .. '">\n' .. mainResult .. '\n</div>'
    end

    if reactionsText and reactionsText ~= '' then
        -- 检查主事件是否包含选择容器
        local hasChoices = mainResult:find('<div class="choice%-container">')

        if hasChoices then
            -- 有选择的事件,event-reactions默认隐藏
            reactionsText = "\n<div class=\"event-label\" data-label=\"event-reactions\" style=\"display:none;\">\n<p>'''事件的后续'''</p>\n" .. reactionsText .. "\n</div>"
        else
            -- 没有选择的事件,event-reactions直接显示
            reactionsText = "\n<div class=\"event-label show\" data-label=\"event-reactions\" style=\"display:block;\">\n<p>'''事件的后续'''</p>\n" .. reactionsText .. "\n</div>"
        end
    end

    -- 合并主事件对话和反应对话
    local finalResult = '<div class="event-container" data-event="' .. (eventId or "unknown") .. '">\n' .. mainResult .. reactionsText .. '\n</div>'

    -- 移除空的div元素
    finalResult = removeEmptyDivs(finalResult)

    return finalResult
end

-- 分析事件脚本中人物出现次数
local function analyzeEventCharacters(eventScript)
    if not eventScript or type(eventScript) ~= "string" then
        return {}
    end

    local characterCounts = {}

    -- 解析事件脚本中的speak命令
    for character in eventScript:gmatch("speak%s+([^%s]+)%s+") do
        if character and character ~= "" then
            characterCounts[character] = (characterCounts[character] or 0) + 1
        end
    end

    -- 解析conditionSpeak命令
    for character in eventScript:gmatch("conditionSpeak%s+([^%s]+)%s+") do
        if character and character ~= "" then
            characterCounts[character] = (characterCounts[character] or 0) + 1
        end
    end

    -- 解析其他可能包含人物名的命令(如warp、faceDirection等)
    for character in eventScript:gmatch("warp%s+([^%s]+)%s+") do
        if character and character ~= "" and character ~= "farmer" then
            characterCounts[character] = (characterCounts[character] or 0) + 0.5 -- 权重较低
        end
    end

    for character in eventScript:gmatch("faceDirection%s+([^%s]+)%s+") do
        if character and character ~= "" and character ~= "farmer" then
            characterCounts[character] = (characterCounts[character] or 0) + 0.3 -- 权重更低
        end
    end

    return characterCounts
end

-- 查找所有村民对事件的反应对话
findEventReactions = function(eventId)
    if not eventId or not TalkData then
        return {}
    end

    local reactions = {}

    -- 遍历所有NPC数据
    for npcName, npcData in pairs(TalkData) do
        -- 跳过Events/位置数据,只处理NPC数据
        if not npcName:match("^Events/") and type(npcData) == "table" then
            for key, dialogue in pairs(npcData) do
                -- 查找eventSeen_事件编号相关的对话
                if key:match("^eventSeen_" .. eventId .. "$") or key:match("^eventSeen_" .. eventId .. "_") then
                    if not reactions[npcName] then
                        reactions[npcName] = {}
                    end
                    reactions[npcName][key] = dialogue
                end
            end
        end
    end
    -- mw.logObject(reactions)
    return reactions
end

-- 格式化事件反应对话
formatEventReactions = function(eventId, reactions, eventScript)
    if not reactions or not next(reactions) then
        return ""
    end

    local output = {}
    -- table.insert(output, )

    -- 分析事件脚本中的人物出现次数
    local characterCounts = {}
    if eventScript then
        characterCounts = analyzeEventCharacters(eventScript)
    end

    -- 按时间分组收集对话
    local timeGroups = {}

    for npcName, dialogues in pairs(reactions) do
        local appearanceCount = characterCounts[npcName] or 0

        for key, dialogue in pairs(dialogues) do
            -- 确定时间分组
            local timeGroup = "触发后"
            local timeFrame = key:match("^eventSeen_" .. eventId .. "_memory_(.+)$")
            if timeFrame then
                if timeFrame == "oneday" then
                    timeGroup = "1 天后"
                elseif timeFrame == "oneweek" then
                    timeGroup = "1 周后"
                elseif timeFrame == "oneyear" then
                    timeGroup = "1 年后"
                elseif timeFrame == "twoweeks" then
                    timeGroup = "2 周后"
                elseif timeFrame == "fourweeks" then
                    timeGroup = "4 周后"
                else
                    timeGroup = "" .. timeFrame .. "后"
                end
            end

            if not timeGroups[timeGroup] then
                timeGroups[timeGroup] = {}
            end

            table.insert(timeGroups[timeGroup], {
                npcName = npcName,
                dialogue = dialogue,
                appearanceCount = appearanceCount
            })
        end
    end

    -- 定义时间组的排序顺序
    local timeOrder = {
        ["触发后"] = 1,
        ["1 天后"] = 2,
        ["1 周后"] = 3,
        ["2 周后"] = 4,
        ["4 周后"] = 5,
        ["1 年后"] = 6
    }

    -- 收集并排序时间组
    local sortedTimeGroups = {}
    for timeGroup, npcs in pairs(timeGroups) do
        table.insert(sortedTimeGroups, {
            timeGroup = timeGroup,
            npcs = npcs
        })
    end

    table.sort(sortedTimeGroups, function(a, b)
        local orderA = timeOrder[a.timeGroup] or 999
        local orderB = timeOrder[b.timeGroup] or 999
        if orderA ~= orderB then
            return orderA < orderB
        else
            return a.timeGroup < b.timeGroup
        end
    end)

    -- 输出每个时间组
    for _, timeGroupInfo in ipairs(sortedTimeGroups) do
        local timeGroup = timeGroupInfo.timeGroup
        local npcs = timeGroupInfo.npcs

        table.insert(output, "" .. timeGroup .. "") -- 时间组的标题

        -- 在每个时间组内按出现次数排序NPC
        table.sort(npcs, function(a, b)
            if a.appearanceCount ~= b.appearanceCount then
                return a.appearanceCount > b.appearanceCount -- 出现次数多的排在前面
            else
                return a.npcName < b.npcName -- 出现次数相同时按名字排序
            end
        end)

        -- 输出该时间组的所有NPC对话
        for _, npcInfo in ipairs(npcs) do
            local processedDialogue = processDialogueUnified(npcInfo.dialogue, nil, getCharacterName(npcInfo.npcName))
            table.insert(output, processedDialogue)
        end

        table.insert(output, "")
    end

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

-- 调试函数
p.debug = function(frame)
    return p.event {
        args = {"38"} -- 测试事件ID 3091462 13 195013 3910975 51 54 9581348 6184644 3091462 1 10
    }
end

return p