维护提醒
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