全站通知:
模块:Dialogue
刷
历
编
跳到导航
跳到搜索
local utils = require("Module:Utils")
local NPC = require("Module:NPC")
local items = require("Module:Items")
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 utils.expandTemplate("PlayerChoice",
{"> " .. text, friendship, character})
else
return utils.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 utils.expandTemplate(template, {text}) end
-- 如果只有两句话,用\n直接隔开
if #parts == 2 then
local result = parts[1] .. "\n" .. parts[2]
return utils.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 utils.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 utils.expandTemplate("Name",
{"Strange Doll (green)", class = "inline"})
elseif itemId == "(O)127" or itemId == "127" then
return utils.expandTemplate("Name",
{"Strange Doll (yellow)", class = "inline"})
end
local chineseName = items.getEnglishNameById("(O)" .. itemId) or
items.getEnglishNameById(itemId)
if chineseName and chineseName ~= "" then
return utils.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)
-- 使用utils.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 = 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, utils.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, "'''" ..
utils.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, utils.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, utils.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, utils.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, utils.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, utils.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, utils.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, utils.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, utils.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 = mw.loadData('Module:Dialogue/' .. 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

沪公网安备 31011002002714 号