全站通知:
            
            
        
模块:Theater
                  
                  
                  刷
                  
                     
                               
                               历
                            
                  
                    
                      
                        
                        编
                      
                    
                
            
            
            
            
            
            跳到导航
            跳到搜索
            
            
                
            
            
            
            
        
    local utils = require("Module:Utils")
local NPC = require("Module:NPC")
local p = {}
local createMovieQuote
local createMovieQuotes
local moviesData = mw.loadData('Module:Theater/data/movies')
local concessionsData = mw.loadData('Module:Theater/data/concessions')
local concessionTastesData = mw.loadData('Module:Theater/data/concession tastes')
local moviesReactionsData = mw.loadData('Module:Theater/data/movies reactions')
local dialogueData = mw.loadData('Module:Dialogue/data')
local movieNames = {
    ["spring_movie_0"] = "勇敢的小树苗",
    ["summer_movie_0"] = "草原之王之旅:大电影",
    ["fall_movie_0"] = "神秘事迹",
    ["winter_movie_0"] = "冷星牧场的奇迹",
    ["spring_movie_1"] = "自然奇观:探索我们这充满活力的世界",
    ["summer_movie_1"] = "温布斯",
    ["fall_movie_1"] = "它在雨中嚎叫",
    ["winter_movie_1"] = "祖祖城特快列车"
}
local concessionNames = {
    ["0"] = "棉花糖",
    ["1"] = "茉莉花茶",
    ["2"] = "Joja 可乐",
    ["3"] = "酸味史莱姆",
    ["4"] = "个人披萨",
    ["5"] = "芝士玉米片",
    ["6"] = "鲑鱼汉堡",
    ["7"] = "冰淇淋三明治",
    ["8"] = "爆米花",
    ["9"] = "薯条",
    ["10"] = "巧克力爆米花",
    ["11"] = "黑甘草糖",
    ["12"] = "星形饼干",
    ["13"] = "大糖球",
    ["14"] = "盐渍花生",
    ["15"] = "鹰嘴豆泥小吃包",
    ["16"] = "羽衣甘蓝汁",
    ["17"] = "苹果脆片",
    ["18"] = "意式面包沙拉",
    ["19"] = "松露爆米花",
    ["20"] = "卡布奇诺慕斯蛋糕",
    ["21"] = "Joja 玉米",
    ["22"] = "星之果实冰糕",
    ["23"] = "糖冰棍"
}
local movieNamesEn = {
    ["spring_movie_0"] = "The Brave Little Sapling",
    ["summer_movie_0"] = "Journey Of The Prairie King: The Motion Picture",
    ["fall_movie_0"] = "Mysterium",
    ["winter_movie_0"] = "The Miracle At Coldstar Ranch",
    ["spring_movie_1"] = "Natural Wonders: Exploring Our Vibrant World",
    ["summer_movie_1"] = "Wumbus",
    ["fall_movie_1"] = "It Howls In The Rain",
    ["winter_movie_1"] = "The Zuzu City Express"
}
local concessionNamesEn = {
    ["0"] = "Cotton Candy",
    ["1"] = "Jasmine Tea",
    ["2"] = "Joja Cola",
    ["3"] = "Sour Slimes",
    ["4"] = "Personal Pizza",
    ["5"] = "Nachos",
    ["6"] = "Salmon Burger",
    ["7"] = "Ice Cream Sandwich",
    ["8"] = "Popcorn",
    ["9"] = "Fries",
    ["10"] = "Chocolate Popcorn",
    ["11"] = "Black Licorice",
    ["12"] = "Star Cookie",
    ["13"] = "Rock Candy",
    ["14"] = "Salted Peanuts",
    ["15"] = "Hummus Snack Pack",
    ["16"] = "Kale Smoothie",
    ["17"] = "Apple Slices",
    ["18"] = "Panzanella Salad",
    ["19"] = "Truffle Popcorn",
    ["20"] = "Cappuccino Mousse Cake",
    ["21"] = "Joja Corn",
    ["22"] = "Stardrop Sorbet",
    ["23"] = "Rock Candy"
}
local function getCharacterName(character)
    if not character then return "" end
    character = character:gsub("^%l", string.upper)
    local chineseName = NPC.getChineseName(character)
    if chineseName then return chineseName end
    return character
end
local function getMovieTags(movieId)
    for _, movie in ipairs(moviesData) do
        if movie.Id == movieId then return movie.Tags or {} end
    end
    return {}
end
local function getConcessionTags(concessionId)
    for _, concession in ipairs(concessionsData) do
        if concession.Id == concessionId then
            return concession.ItemTags or {}
        end
    end
    return {}
end
local function calculateMoviePreference(npcName, movieId)
    local movieTags = getMovieTags(movieId)
    local npcReactions = nil
    for _, npcData in ipairs(moviesReactionsData) do
        if npcData.NPCName == npcName then
            npcReactions = npcData.Reactions
            break
        end
    end
    if not npcReactions then return "like" end
    
    for _, reaction in ipairs(npcReactions) do
        for _, tag in ipairs(movieTags) do
        	if reaction.Tag == tag then return reaction.Response end
        end
        if reaction.Tag == movieId then return reaction.Response end
    end
    for _, reaction in ipairs(npcReactions) do
		local hasContent = false
		for _ in pairs(reaction.Whitelist) do
		    hasContent = true
		    break
		end
        if reaction.Tag == "*" and not hasContent then return reaction.Response end
    end
    return "like"
end
local function calculateConcessionPreference(npcName, concessionId)
    local concessionTags = getConcessionTags(concessionId)
    local concessionName = nil
    for _, concession in ipairs(concessionsData) do
        if concession.Id == concessionId then
            concessionName = concession.Name
            break
        end
    end
    local npcTastes = nil
    local universalTastes = nil
    for _, tasteData in ipairs(concessionTastesData) do
        if tasteData.Name == npcName then
            npcTastes = tasteData
        elseif tasteData.Name == "*" then
            universalTastes = tasteData
        end
    end
    
    -- 合并 universalTastes 和 npcTastes,优先采用 npcTastes
    local mergedTastes = {
        DislikedTags = {},
        LovedTags = {},
        LikedTags = {}
    }
    
    -- 辅助函数:检查表中是否包含值
    local function tableContains(tbl, val)
        for _, v in ipairs(tbl) do
            if v == val then return true end
        end
        return false
    end
    
    -- 先添加 universalTastes
    if universalTastes then
        for _, tag in ipairs(universalTastes.DislikedTags or {}) do
            table.insert(mergedTastes.DislikedTags, tag)
        end
        for _, tag in ipairs(universalTastes.LovedTags or {}) do
            table.insert(mergedTastes.LovedTags, tag)
        end
        for _, tag in ipairs(universalTastes.LikedTags or {}) do
            table.insert(mergedTastes.LikedTags, tag)
        end
    end
    
    -- 然后添加 npcTastes,覆盖冲突
    if npcTastes then
        for _, tag in ipairs(npcTastes.DislikedTags or {}) do
            -- 从其他列表移除
            for i = #mergedTastes.LovedTags, 1, -1 do
                if mergedTastes.LovedTags[i] == tag then table.remove(mergedTastes.LovedTags, i) end
            end
            for i = #mergedTastes.LikedTags, 1, -1 do
                if mergedTastes.LikedTags[i] == tag then table.remove(mergedTastes.LikedTags, i) end
            end
            -- 添加到 DislikedTags 如果不在
            if not tableContains(mergedTastes.DislikedTags, tag) then
                table.insert(mergedTastes.DislikedTags, tag)
            end
        end
        for _, tag in ipairs(npcTastes.LovedTags or {}) do
            for i = #mergedTastes.DislikedTags, 1, -1 do
                if mergedTastes.DislikedTags[i] == tag then table.remove(mergedTastes.DislikedTags, i) end
            end
            for i = #mergedTastes.LikedTags, 1, -1 do
                if mergedTastes.LikedTags[i] == tag then table.remove(mergedTastes.LikedTags, i) end
            end
            if not tableContains(mergedTastes.LovedTags, tag) then
                table.insert(mergedTastes.LovedTags, tag)
            end
        end
        for _, tag in ipairs(npcTastes.LikedTags or {}) do
            for i = #mergedTastes.DislikedTags, 1, -1 do
                if mergedTastes.DislikedTags[i] == tag then table.remove(mergedTastes.DislikedTags, i) end
            end
            for i = #mergedTastes.LovedTags, 1, -1 do
                if mergedTastes.LovedTags[i] == tag then table.remove(mergedTastes.LovedTags, i) end
            end
            if not tableContains(mergedTastes.LikedTags, tag) then
                table.insert(mergedTastes.LikedTags, tag)
            end
        end
    end
    
    local function checkPreference(tastes, tags, concessionName)
        if not tastes then return nil end
        if concessionName then
            for _, disliked in ipairs(tastes.DislikedTags or {}) do
                if disliked == concessionName then
                    return "dislike"
                end
            end
            for _, loved in ipairs(tastes.LovedTags or {}) do
                if loved == concessionName then return "love" end
            end
            for _, liked in ipairs(tastes.LikedTags or {}) do
                if liked == concessionName then return "like" end
            end
        end
        for _, tag in ipairs(tags) do
            for _, disliked in ipairs(tastes.DislikedTags or {}) do
                if disliked == tag then return "dislike" end
            end
            for _, loved in ipairs(tastes.LovedTags or {}) do
                if loved == tag then return "love" end
            end
            for _, liked in ipairs(tastes.LikedTags or {}) do
                if liked == tag then return "like" end
            end
        end
        return nil
    end
    
    local preference = checkPreference(mergedTastes, concessionTags, concessionName)
    return preference or "like"
end
local function getMovieYearSeason(movieId)
    for _, movie in ipairs(moviesData) do
        if movie.Id == movieId then
            local yearText = ""
            local seasonText = ""
            if movie.YearRemainder == 0 then
                yearText = "第一年"
            elseif movie.YearRemainder == 1 then
                yearText = "第二年"
            else
                yearText = "第" .. (movie.YearRemainder + 1) .. "年"
            end
            if movie.Seasons then
                local seasons = {}
                for i, season in ipairs(movie.Seasons) do
                    local cleanSeason = tostring(season):gsub("^%s*(.-)%s*$",
                                                              "%1")
                    if cleanSeason then
                        table.insert(seasons,
                                     '<br><div style="white-space: nowrap;">' ..
                                         utils.expandTemplate("Season",
                                                               {cleanSeason}) ..
                                         '</div>')
                    end
                end
                seasonText = table.concat(seasons, "、")
            end
            return yearText .. seasonText
        end
    end
    return ""
end
function p.getNPCMoviePreferences(frame)
    local npcName = frame.args[1] or frame.args.npc
    if not npcName then return "<!-- 错误:请提供NPC名称 -->" end
    local displayName = npcName
    local englishName = npcName
    if NPC.getEnglishName(npcName) then
        englishName = NPC.getEnglishName(npcName)
    else
        displayName = getCharacterName(npcName)
    end
    local loved = {}
    local liked = {}
    local disliked = {}
    for _, movie in ipairs(moviesData) do
        local preference = calculateMoviePreference(englishName, movie.Id)
        local movieName = movieNames[movie.Id] or movie.Id
        local yearSeason = getMovieYearSeason(movie.Id)
        local movieDisplayName = movieName .. "(" .. yearSeason .. ")"
        if preference == "love" then
            table.insert(loved, movieDisplayName)
        elseif preference == "like" then
            table.insert(liked, movieDisplayName)
        elseif preference == "dislike" then
            table.insert(disliked, movieDisplayName)
        end
    end
    local result = "=== " .. displayName .. "的电影喜好 ===\n"
    if #loved > 0 then
        result = result .. "'''最爱:'''" .. table.concat(loved, "、") ..
                     "\n\n"
    end
    if #liked > 0 then
        result = result .. "'''喜欢:'''" .. table.concat(liked, "、") ..
                     "\n\n"
    end
    if #disliked > 0 then
        result =
            result .. "'''不喜欢:'''" .. table.concat(disliked, "、") ..
                "\n\n"
    end
    return result
end
function p.getNPCConcessionPreferences(frame)
    local npcName = frame.args[1] or frame.args.npc
    if not npcName then return "<!-- 错误:请提供NPC名称 -->" end
    local displayName = npcName
    local englishName = npcName
    if NPC.getEnglishName(npcName) then
        englishName = NPC.getEnglishName(npcName)
    else
        displayName = getCharacterName(npcName)
    end
    local loved = {}
    local liked = {}
    local disliked = {}
    for _, concession in ipairs(concessionsData) do
        local preference = calculateConcessionPreference(englishName,
                                                         concession.Id)
        local concessionName = concessionNames[concession.Id] or concession.Name
        if preference == "love" then
            table.insert(loved, concessionName)
        elseif preference == "like" then
            table.insert(liked, concessionName)
        elseif preference == "dislike" then
            table.insert(disliked, concessionName)
        end
    end
    local result = "=== " .. displayName .. "的零食喜好 ===\n"
    if #loved > 0 then
        result = result .. "'''最爱:'''" .. table.concat(loved, "、") ..
                     "\n\n"
    end
    if #liked > 0 then
        result = result .. "'''喜欢:'''" .. table.concat(liked, "、") ..
                     "\n\n"
    end
    if #disliked > 0 then
        result =
            result .. "'''不喜欢:'''" .. table.concat(disliked, "、") ..
                "\n\n"
    end
    return result
end
function p.getMovieNPCPreferences(frame)
    local movieId = frame.args[1] or frame.args.movie
    if not movieId then return "<!-- 错误:请提供电影ID -->" end
    local movieName = movieNames[movieId] or movieId
    local yearSeason = getMovieYearSeason(movieId)
    local loved = {}
    local liked = {}
    local disliked = {}
    local allNPCs = {}
    for _, npcData in ipairs(moviesReactionsData) do
        table.insert(allNPCs, npcData.NPCName)
    end
    for _, npcName in ipairs(allNPCs) do
        local preference = calculateMoviePreference(npcName, movieId)
        if preference == "love" then
            table.insert(loved, npcName)
        elseif preference == "like" then
            table.insert(liked, npcName)
        elseif preference == "dislike" then
            table.insert(disliked, npcName)
        end
    end
    local result = "=== 《" .. movieName .. "》(" .. yearSeason ..
                       ")的观众喜好 ===\n"
    if #loved > 0 then
        result = result .. "'''最爱:'''" .. table.concat(loved, "、") ..
                     "\n\n"
    end
    if #liked > 0 then
        result = result .. "'''喜欢:'''" .. table.concat(liked, "、") ..
                     "\n\n"
    end
    if #disliked > 0 then
        result =
            result .. "'''不喜欢:'''" .. table.concat(disliked, "、") ..
                "\n\n"
    end
    return result
end
function p.getConcessionNPCPreferences(frame)
    local concessionId = frame.args[1] or frame.args.concession
    if not concessionId then return "<!-- 错误:请提供零食ID -->" end
    local concessionName = concessionNames[concessionId] or concessionId
    local loved = {}
    local liked = {}
    local disliked = {}
    local allNPCs = {}
    for _, tasteData in ipairs(concessionTastesData) do
        if tasteData.Name ~= "*" then
            table.insert(allNPCs, tasteData.Name)
        end
    end
    for _, npcName in ipairs(allNPCs) do
        local preference = calculateConcessionPreference(npcName, concessionId)
        if preference == "love" then
            table.insert(loved, npcName)
        elseif preference == "like" then
            table.insert(liked, npcName)
        elseif preference == "dislike" then
            table.insert(disliked, npcName)
        end
    end
    local result = "=== " .. concessionName .. "的受欢迎程度 ===\n"
    if #loved > 0 then
        result = result .. "'''最爱:'''" .. table.concat(loved, "、") ..
                     "\n\n"
    end
    if #liked > 0 then
        result = result .. "'''喜欢:'''" .. table.concat(liked, "、") ..
                     "\n\n"
    end
    if #disliked > 0 then
        result =
            result .. "'''不喜欢:'''" .. table.concat(disliked, "、") ..
                "\n\n"
    end
    return result
end
function p.getConcessionPrice(frame)
    local concessionId = frame.args[1] or frame.args.concession
    if not concessionId then return "<!-- 错误:请提供零食ID -->" end
    for _, concession in ipairs(concessionsData) do
        if concession.Id == concessionId then
            local concessionName = concessionNames[concessionId] or
                                       concession.Name
            return concessionName .. "的价格:" ..
                       utils.expandTemplate("price", {concession.Price})
        end
    end
    return "<!-- 错误:未找到零食ID " .. concessionId .. " -->"
end
function p.getMovieCranePrizes(frame)
    local movieId = frame.args[1] or frame.args.movie
    if not movieId then return "<!-- 错误:请提供电影ID -->" end
    local movieName = movieNames[movieId] or movieId
    local yearSeason = getMovieYearSeason(movieId)
    for _, movie in ipairs(moviesData) do
        if movie.Id == movieId then
            local result = "=== 《" .. movieName .. "》(" .. yearSeason ..
                               ")上映期间的娃娃机特殊物品 ===\n"
            if movie.CranePrizes and #movie.CranePrizes > 0 then
                for _, prize in ipairs(movie.CranePrizes) do
                    local rarity = ""
                    if prize.Rarity == 1 then
                        rarity = "(普通)"
                    elseif prize.Rarity == 2 then
                        rarity = "(稀有)"
                    elseif prize.Rarity == 3 then
                        rarity = "(豪华)"
                    end
                    local itemId = prize.ItemId
                    if itemId and itemId:match("%(F%)(%d+)") then
                        local furnitureId = itemId:match("%(F%)(%d+)")
                        result = result .. "* " ..
                                     utils.expandTemplate("Name", {furnitureId}) ..
                                     rarity .. "\n"
                    else
                        result =
                            result .. "* " .. (prize.Id or "未知物品") ..
                                rarity .. "\n"
                    end
                end
            else
                result = result ..
                             "该电影上映期间没有特殊物品。\n"
            end
            return result
        end
    end
    return "<!-- 错误:未找到电影ID " .. movieId .. " -->"
end
local function extractScriptDialogue(script)
    if not script then return nil end
    local messageDialogue = script:match('/message%s+"([^"]+)"')
    if messageDialogue then return messageDialogue end
    local complexMessage = script:match('/emote%s+[^/]+/message%s+"([^"]+)"')
    if complexMessage then return complexMessage end
    return nil
end
local function getMovieDialogueTexts(translationKey, script)
    local textDialogues = {}
    local scriptDialogues = {}
    local processedKeys = {}
    if translationKey and translationKey ~= "" then
        if dialogueData then
            local key = translationKey:match(
                            "%[LocalizedText Strings\\MovieReactions:(.+)%]")
            if key and dialogueData.MovieReactions then
                processedKeys[key] = true
                if dialogueData.MovieReactions[key] then
                    table.insert(textDialogues, dialogueData.MovieReactions[key])
                end
            else
                table.insert(textDialogues, translationKey)
            end
        else
            table.insert(textDialogues, translationKey)
        end
    end
    if script then
        local scriptDialogue = extractScriptDialogue(script)
        if scriptDialogue then
            if scriptDialogue:match("%[LocalizedText") then
                local scriptKey = scriptDialogue:match(
                                      "%[LocalizedText Strings\\MovieReactions:(.+)%]")
                if scriptKey and not processedKeys[scriptKey] then
                    local scriptTexts = getMovieDialogueTexts(scriptDialogue,
                                                              nil)
                    for _, text in ipairs(scriptTexts.textDialogues or {}) do
                        table.insert(scriptDialogues, text)
                    end
                end
            else
                table.insert(scriptDialogues, scriptDialogue)
            end
        end
    end
    return {textDialogues = textDialogues, scriptDialogues = scriptDialogues}
end
local function cleanMovieText(text)
    if not text or type(text) ~= "string" then return text, nil end
    local emotion = nil
    emotion = text:match("%$([hslua])$")
    if not emotion then
        emotion = text:match("%$(%d+)$")
        if emotion then
            local num = tonumber(emotion)
            if not num or num < 0 or num > 15 then emotion = nil end
        end
    end
    text = text:gsub("%$[hslua]$", "")
    text = text:gsub("%$%d+$", "")
    text = text:gsub("%$%{([^%^}]+)%^([^}]+)%}", "%1/%2")
    text = text:gsub("\\\"", "\"")
    text = text:gsub("\\n", "\n")
    text = text:gsub("@", '<span class="player-name">玩家名</span>')
    local has0 = text:find("{0}") ~= nil
    local has2 = text:find("{2}") ~= nil
    if has0 and has2 then
        text = text:gsub("{0}", '<span class="player-name">电影名称</span>')
        text = text:gsub("{2}", '<span class="player-name">人物名称</span>')
    elseif has0 then
        text = text:gsub("{0}", '<span class="player-name">_____</span>')
    elseif has2 then
        text = text:gsub("{2}", '<span class="player-name">_____</span>')
    end
    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*%]",
                     '<span class="refuse-item">(给予物品)</span>')
    if text:match("^[^。!?]*在[^。!?]*。?%s*$") then
        text = '<span class="action-description">' .. text .. '</span>'
    end
    if emotion then
        if emotion == "h" then
            emotion = "1"
        elseif emotion == "s" then
            emotion = "2"
        elseif emotion == "u" then
            emotion = "3"
        elseif emotion == "l" then
            emotion = "4"
        elseif emotion == "a" then
            emotion = "5"
        end
    end
    return text, emotion
end
local function processMovieGenderDialogue(text, characterName, isFromScript)
    if not text or type(text) ~= "string" then return text end
    local hasInlineGender = text:find("%$%{[^%^}]+%^[^}]+%}%$")
    if hasInlineGender then
        local maleText = text:gsub("%$%{([^%^}]+)%^[^}]+%}%$", "%1")
        local femaleText = text:gsub("%$%{[^%^}]+%^([^}]+)%}%$", "%1")
        local maleResult = createMovieQuote(maleText ..
                                                '<span class="trigger-chance">(男)</span>',
                                            characterName, isFromScript)
        local femaleResult = createMovieQuote(femaleText ..
                                                  '<span class="trigger-chance">(女)</span>',
                                              characterName, isFromScript)
        return maleResult .. "\n" .. femaleResult
    end
    if not text:find("%^") then
        return createMovieQuote(text, characterName, isFromScript)
    end
    local caretPos = text:find("%^")
    local beforeCaret = text:sub(1, caretPos - 1)
    local afterCaret = text:sub(caretPos + 1)
    local maleText = beforeCaret:match("^%s*(.-)%s*$") or ""
    local femaleText = afterCaret:match("^%s*(.-)%s*$") or ""
    local maleResult = createMovieQuote(maleText ..
                                            '<span class="trigger-chance">(男)</span>',
                                        characterName, isFromScript)
    local femaleResult = createMovieQuote(femaleText ..
                                              '<span class="trigger-chance">(女)</span>',
                                          characterName, isFromScript)
    return maleResult .. "\n" .. femaleResult
end
-- 处理单个对话片段(不包含分隔符)
local createMovieQuoteSingle = function(text, characterName, isFromScript)
    if not text then return "" end
    local cleanedText, emotion = cleanMovieText(text)
    if isFromScript then
        return utils.expandTemplate("Say", {"voiceover", cleanedText})
    end
    local chineseName = getCharacterName(characterName)
    local portrait = characterName
    if portrait then portrait = portrait:lower() end
    if not emotion and characterName then emotion = "0" end
    if emotion and portrait then
        return utils.expandTemplate("Say", {
            "left", chineseName, cleanedText, emotion, portrait
        })
    else
        return utils.expandTemplate("Say", {"left", chineseName, cleanedText})
    end
end
-- 处理包含分隔符的电影对话文本,分割为独立对话
local function processMovieDialogueWithSeparators(text, characterName, isFromScript)
    if not text or type(text) ~= "string" then
        return ""
    end
    -- 分割对话:#$b# 和 #$e# 都作为分隔符
    local parts = {}
    local currentPart = ""
    local i = 1
    while i <= #text do
        if text:sub(i, i + 3) == "#$b#" then
            if currentPart ~= "" then
                table.insert(parts, currentPart)
                currentPart = ""
            end
            i = i + 4
        elseif text:sub(i, i + 3) == "#$e#" then
            if currentPart ~= "" then
                table.insert(parts, currentPart)
                currentPart = ""
            end
            i = i + 4
        else
            currentPart = currentPart .. text:sub(i, i)
            i = i + 1
        end
    end
    -- 添加最后一部分
    if currentPart ~= "" then
        table.insert(parts, currentPart)
    end
    -- 如果没有分隔符,直接处理整个文本
    if #parts <= 1 then
        return createMovieQuoteSingle(text, characterName, isFromScript)
    end
    -- 处理每个独立的对话片段
    local results = {}
    for _, part in ipairs(parts) do
        local trimmed = part:match("^%s*(.-)%s*$")
        if trimmed and trimmed ~= "" then
            -- 检查这个片段是否包含性别分支
            if trimmed:find("%^") or trimmed:find("%$%{[^%^}]+%^[^}]+%}%$") then
                -- 有性别分支,调用性别处理函数
                table.insert(results, processMovieGenderDialogue(trimmed, characterName, isFromScript))
            else
                -- 没有性别分支,直接处理
                table.insert(results, createMovieQuoteSingle(trimmed, characterName, isFromScript))
            end
        end
    end
    return table.concat(results, "\n")
end
createMovieQuote = function(text, characterName, isFromScript)
    if not text then return "" end
    -- 检查是否包含分隔符
    if text:find("#%$[be]#") then
        return processMovieDialogueWithSeparators(text, characterName, isFromScript)
    else
        return createMovieQuoteSingle(text, characterName, isFromScript)
    end
end
createMovieQuotes = function(dialogues, characterName, isFromScript)
    if not dialogues or #dialogues == 0 then return "" end
    local result = {}
    for _, dialogue in ipairs(dialogues) do
        if dialogue:find("%^") or dialogue:find("%$%{[^%^}]+%^[^}]+%}%$") then
            local quote = processMovieGenderDialogue(dialogue, characterName,
                                                     isFromScript)
            if quote and quote ~= "" then table.insert(result, quote) end
        else
            local quote =
                createMovieQuote(dialogue, characterName, isFromScript)
            if quote and quote ~= "" then table.insert(result, quote) end
        end
    end
    return table.concat(result, "\n")
end
local function processDialogueCategory(title, dialogues, englishName)
    if not dialogues then return "" end
    local dialogueContent = {}
    if dialogues.BeforeMovie then
        local result = getMovieDialogueTexts(dialogues.BeforeMovie.Text,
                                             dialogues.BeforeMovie.Script)
        local allQuotes = {}
        if #result.textDialogues > 0 then
            table.insert(allQuotes, createMovieQuotes(result.textDialogues,
                                                      englishName, false))
        end
        if #result.scriptDialogues > 0 then
            table.insert(allQuotes, createMovieQuotes(result.scriptDialogues,
                                                      englishName, true))
        end
        if #allQuotes > 0 then
            table.insert(dialogueContent, table.concat(allQuotes, "\n"))
        end
    end
    if dialogues.DuringMovie then
        local result = getMovieDialogueTexts(dialogues.DuringMovie.Text,
                                             dialogues.DuringMovie.Script)
        local allQuotes = {}
        if #result.textDialogues > 0 then
            table.insert(allQuotes, createMovieQuotes(result.textDialogues,
                                                      englishName, false))
        end
        if #result.scriptDialogues > 0 then
            table.insert(allQuotes, createMovieQuotes(result.scriptDialogues,
                                                      englishName, true))
        end
        if #allQuotes > 0 then
            table.insert(dialogueContent, table.concat(allQuotes, "\n"))
        end
    end
    if dialogues.AfterMovie then
        local result = getMovieDialogueTexts(dialogues.AfterMovie.Text,
                                             dialogues.AfterMovie.Script)
        local allQuotes = {}
        if #result.textDialogues > 0 then
            table.insert(allQuotes, createMovieQuotes(result.textDialogues,
                                                      englishName, false))
        end
        if #result.scriptDialogues > 0 then
            table.insert(allQuotes, createMovieQuotes(result.scriptDialogues,
                                                      englishName, true))
        end
        if #allQuotes > 0 then
            table.insert(dialogueContent, table.concat(allQuotes, "\n"))
        end
    end
    if #dialogueContent > 0 then
        local eventLabelContent =
            '<div class="event-label show" style="display: block;">\n' ..
                table.concat(dialogueContent, "\n") .. '\n</div>'
        return utils.expandTemplate("Collapse",
                                     {title, content = eventLabelContent})
    end
    return ""
end
function p.getNPCMovieDialogue(frame)
    local npcName = frame.args[1] or frame.args.npc
    if not npcName then return "<!-- 错误:请提供NPC名称 -->" end
    local displayName = npcName
    local englishName = npcName
    if NPC.getEnglishName(npcName) then
        englishName = NPC.getEnglishName(npcName)
    else
        displayName = getCharacterName(npcName)
    end
    -- 收集电影邀约对话
    local inviteDialogues = {}
    -- 1. 从NPC个人数据中获取MovieInvitation
    if dialogueData and dialogueData[englishName] and dialogueData[englishName].MovieInvitation then
        table.insert(inviteDialogues, {
            type = "regular",
            dialogue = dialogueData[englishName].MovieInvitation
        })
    end
    -- 2. 从Strings/Characters获取婚后对话
    if dialogueData and dialogueData["Strings/Characters"] then
        local stringsData = dialogueData["Strings/Characters"]
        local spouseKey = "MovieInvite_Spouse_" .. englishName
        if stringsData[spouseKey] then
            table.insert(inviteDialogues, {
                type = "spouse",
                dialogue = stringsData[spouseKey]
            })
        end
        -- 3. 获取通用邀约对话(如果没有个人对话且没有婚后对话)
        local hasRegularDialogue = false
        for _, invite in ipairs(inviteDialogues) do
            if invite.type == "regular" then
                hasRegularDialogue = true
                break
            end
        end
        if not hasRegularDialogue then
            -- 定义 child 和 rude NPC 列表
            local childNPCs = {"Vincent", "Jas"}
            local rudeNPCs = {"Abigail", "Alex", "Clint", "George", "Haley", "Pam", "Sebastian", "Shane", "Wizard"}
            -- 检查是否为 child
            local isChild = false
            for _, child in ipairs(childNPCs) do
                if englishName == child then
                    isChild = true
                    break
                end
            end
            -- 检查是否为 rude
            local isRude = false
            for _, rude in ipairs(rudeNPCs) do
                if englishName == rude then
                    isRude = true
                    break
                end
            end
            -- 按优先级查找通用对话
            local fallbackKeys = {
                "MovieInvite_Invited_" .. englishName
            }
            -- 根据 NPC 类型添加相应的回退对话
            if isChild then
                table.insert(fallbackKeys, "MovieInvite_Invited_Child")
            elseif isRude then
                table.insert(fallbackKeys, "MovieInvite_Invited_Rude")
            else
                table.insert(fallbackKeys, "MovieInvite_Invited")
            end
            for _, key in ipairs(fallbackKeys) do
                if stringsData[key] then
                    table.insert(inviteDialogues, {
                        type = "regular",
                        dialogue = stringsData[key]
                    })
                    break
                end
            end
        end
    end
    local result = ""
    -- 处理邀约对话
    if #inviteDialogues > 0 then
        local inviteContent = {}
        local regularDialogues = {}
        local spouseDialogues = {}
        for _, invite in ipairs(inviteDialogues) do
            if invite.type == "spouse" then
                table.insert(spouseDialogues, invite.dialogue)
            else
                table.insert(regularDialogues, invite.dialogue)
            end
        end
        -- 先输出常规对话
        if #regularDialogues > 0 then
            for _, dialogue in ipairs(regularDialogues) do
                local quote = createMovieQuote(dialogue, englishName, false)
                if quote and quote ~= "" then
                    table.insert(inviteContent, quote)
                end
            end
        end
        -- 再输出婚后对话
        if #spouseDialogues > 0 then
            table.insert(inviteContent, "'''婚后'''")
            for _, dialogue in ipairs(spouseDialogues) do
                local quote = createMovieQuote(dialogue, englishName, false)
                if quote and quote ~= "" then
                    table.insert(inviteContent, quote)
                end
            end
        end
        if #inviteContent > 0 then
            local eventLabelContent =
                '<div class="event-label show" style="display: block;">\n' ..
                    table.concat(inviteContent, "\n") .. '\n</div>'
            result = result .. utils.expandTemplate("Collapse",
                                         {"发起邀约", content = eventLabelContent}) .. "\n"
        end
    end
    local npcReactions = nil
    for _, npcData in ipairs(moviesReactionsData) do
        if npcData.NPCName == englishName then
            npcReactions = npcData.Reactions
            break
        end
    end
    if not npcReactions then
        return result .. "<!-- 错误:未找到NPC " .. displayName ..
                   " 的电影反应数据 -->"
    end
    local movieDialogues = {}
    local reactionDialogues = {}
    local genreDialogues = {}
    local generalDialogues = {}
    for _, reaction in ipairs(npcReactions) do
        if reaction.SpecialResponses then
            local tag = reaction.Tag
            local movieName = movieNames[tag]
            if movieName then
                movieDialogues[movieName] = reaction.SpecialResponses
            elseif tag == "*" then
                generalDialogues["通用"] = reaction.SpecialResponses
            elseif tag == "love" or tag == "like" or tag == "dislike" or tag ==
                "seen_love" or tag == "seen_like" or tag == "seen_dislike" then
                local categoryTitle = ""
                if tag == "love" or tag == "seen_love" then
                    categoryTitle = "最爱的电影"
                elseif tag == "dislike" or tag == "seen_dislike" then
                    categoryTitle = "不喜欢的电影"
                elseif tag == "like" or tag == "seen_like" then
                    categoryTitle = "喜欢的电影"
                end
                if tag:match("^seen_") then
                    categoryTitle = categoryTitle .. "(已看过)"
                end
                reactionDialogues[categoryTitle] = reaction.SpecialResponses
            else
                local categoryTitle = ""
                if tag == "family" then
                    categoryTitle = "家庭类电影"
                elseif tag == "comedy" then
                    categoryTitle = "喜剧类电影"
                elseif tag == "horror" then
                    categoryTitle = "恐怖类电影"
                elseif tag == "action" then
                    categoryTitle = "动作类电影"
                elseif tag == "sci-fi" then
                    categoryTitle = "科幻类电影"
                elseif tag == "classic" then
                    categoryTitle = "经典类电影"
                elseif tag == "romance" then
                    categoryTitle = "浪漫类电影"
                elseif tag == "documentary" then
                    categoryTitle = "纪录片类电影"
                elseif tag == "art" then
                    categoryTitle = "艺术类电影"
                else
                    categoryTitle = tag .. "类电影"
                end
                genreDialogues[categoryTitle] = reaction.SpecialResponses
            end
        end
    end
    local outputTemplates = {}
    for movieName, dialogues in pairs(movieDialogues) do
        local collapseTemplate = processDialogueCategory(movieName, dialogues,
                                                         englishName)
        if collapseTemplate ~= "" then
            table.insert(outputTemplates, collapseTemplate)
        end
    end
    for categoryTitle, dialogues in pairs(genreDialogues) do
        local collapseTemplate = processDialogueCategory(categoryTitle,
                                                         dialogues, englishName)
        if collapseTemplate ~= "" then
            table.insert(outputTemplates, collapseTemplate)
        end
    end
    local reactionOrder = {
        "最爱的电影", "最爱的电影(已看过)", "喜欢的电影",
        "喜欢的电影(已看过)", "不喜欢的电影",
        "不喜欢的电影(已看过)"
    }
    for _, categoryTitle in ipairs(reactionOrder) do
        if reactionDialogues[categoryTitle] then
            local collapseTemplate = processDialogueCategory(categoryTitle,
                                                             reactionDialogues[categoryTitle],
                                                             englishName)
            if collapseTemplate ~= "" then
                table.insert(outputTemplates, collapseTemplate)
            end
        end
    end
    for dialogueType, dialogues in pairs(generalDialogues) do
        local collapseTemplate = processDialogueCategory("通用及特殊",
                                                         dialogues, englishName)
        if collapseTemplate ~= "" then
            table.insert(outputTemplates, collapseTemplate)
        end
    end
    if #outputTemplates > 0 then
        result = result .. "\n" .. table.concat(outputTemplates, "\n\n")
    end
    return result
end
function p.getAllMoviesInfo(frame)
    local result = "=== 所有电影信息 ===\n"
    for _, movie in ipairs(moviesData) do
        local movieName = movieNames[movie.Id] or movie.Id
        local yearSeason = getMovieYearSeason(movie.Id)
        result = result .. "\n==== " .. movieName .. "(" .. yearSeason ..
                     ") ====\n"
        result = result .. "* '''ID:'''" .. movie.Id .. "\n"
        result = result .. "* '''季节:'''" ..
                     table.concat(movie.Seasons or {}, "、") .. "\n"
        if movie.YearModulus and movie.YearRemainder then
            local yearDesc = ""
            if movie.YearModulus == 2 then
                if movie.YearRemainder == 0 then
                    yearDesc = "第一年"
                else
                    yearDesc = "第二年"
                end
            else
                yearDesc = "每" .. movie.YearModulus .. "年,余数" ..
                               movie.YearRemainder
            end
            result = result .. "* '''年份:'''" .. yearDesc .. "\n"
        end
        if movie.Tags and #movie.Tags > 0 then
            local tagNames = {}
            for _, tag in ipairs(movie.Tags) do
                local tagName = tag
                if tag == "family" then
                    tagName = "家庭"
                elseif tag == "comedy" then
                    tagName = "喜剧"
                elseif tag == "horror" then
                    tagName = "恐怖"
                elseif tag == "action" then
                    tagName = "动作"
                elseif tag == "sci-fi" then
                    tagName = "科幻"
                elseif tag == "classic" then
                    tagName = "经典"
                elseif tag == "romance" then
                    tagName = "浪漫"
                elseif tag == "documentary" then
                    tagName = "纪录片"
                elseif tag == "art" then
                    tagName = "艺术"
                end
                table.insert(tagNames, tagName)
            end
            result = result .. "* '''类型:'''" ..
                         table.concat(tagNames, "、") .. "\n"
        end
        if movie.CranePrizes and #movie.CranePrizes > 0 then
            result = result .. "* '''娃娃机特殊物品:'''" ..
                         #movie.CranePrizes .. "种\n"
        end
    end
    return result
end
function p.getNPCCinemaTable(frame)
    local npcName = frame.args[1] or frame.args.npc
    if not npcName then return "<!-- 错误:请提供NPC名称 -->" end
    local displayName = npcName
    local englishName = npcName
    if NPC.getEnglishName(npcName) then
        englishName = NPC.getEnglishName(npcName)
    else
        displayName = getCharacterName(npcName)
    end
    local moviePreferences = {loved = {}, liked = {}, disliked = {}}
    for _, movie in ipairs(moviesData) do
        local movieName = movieNames[movie.Id] or movie.Id
        local preference = calculateMoviePreference(englishName, movie.Id)
        local yearSeason = getMovieYearSeason(movie.Id)
        local movieDisplayName = movieName .. "(" .. yearSeason .. ")"
        local movieEnglishName = movieNamesEn[movie.Id]:gsub(':', '')
        local movieImageName = ""
        if movieEnglishName then
            movieImageName = "'" .. movieEnglishName .. "'.png"
        end
        local movieEntry = {
            image = movieImageName,
            name = movieName,
            season = yearSeason,
            yearRemainder = movie.YearRemainder or 0,
            seasons = movie.Seasons or {}
        }
        if preference == "love" then
            table.insert(moviePreferences.loved, movieEntry)
        elseif preference == "like" then
            table.insert(moviePreferences.liked, movieEntry)
        elseif preference == "dislike" then
            table.insert(moviePreferences.disliked, movieEntry)
        end
    end
    local function sortMovies(movieList)
        table.sort(movieList, function(a, b)
            if a.yearRemainder ~= b.yearRemainder then
                return a.yearRemainder < b.yearRemainder
            end
            local seasonOrder = {
                spring = 0,
                summer = 1,
                fall = 2,
                winter = 3,
                Spring = 0,
                Summer = 1,
                Fall = 2,
                Winter = 3
            }
            local aMinSeason = 4
            local bMinSeason = 4
            for _, season in ipairs(a.seasons) do
                local seasonValue = seasonOrder[season]
                if seasonValue and seasonValue < aMinSeason then
                    aMinSeason = seasonValue
                end
            end
            for _, season in ipairs(b.seasons) do
                local seasonValue = seasonOrder[season]
                if seasonValue and seasonValue < bMinSeason then
                    bMinSeason = seasonValue
                end
            end
            if aMinSeason == bMinSeason then return a.name < b.name end
            return aMinSeason < bMinSeason
        end)
    end
    sortMovies(moviePreferences.loved)
    sortMovies(moviePreferences.liked)
    sortMovies(moviePreferences.disliked)
    local concessionPreferences = {loved = {}, liked = {}, disliked = {}}
    for _, concession in ipairs(concessionsData) do
        local preference = calculateConcessionPreference(englishName,
                                                         concession.Id)
        local concessionName = concessionNames[concession.Id] or concession.Name
        local concessionImageName = concession.Name .. ".png"
        if concession.Name == "Joja Cola" then
            concessionImageName = "Joja Cola (large).png"
        end
        local concessionEntry = {
            image = concessionImageName,
            name = concessionName
        }
        if preference == "love" then
            table.insert(concessionPreferences.loved, concessionEntry)
        elseif preference == "like" then
            table.insert(concessionPreferences.liked, concessionEntry)
        elseif preference == "dislike" then
            table.insert(concessionPreferences.disliked, concessionEntry)
        end
    end
    local movieTableHTML =
        '<table class="wikitable" style="margin: 0; margin-top: 1em;">\n'
    if #moviePreferences.loved > 0 then
        movieTableHTML = movieTableHTML ..
                             '<tr><th colspan="2">最爱 <span style="font-size:smaller;">+200</span></th></tr>\n'
        for _, movie in ipairs(moviePreferences.loved) do
            movieTableHTML =
                movieTableHTML .. '<tr><td>[[File:' .. movie.image ..
                    '|24px|link=]] ' .. movie.name ..
                    '</td><td style="text-align: center; padding: 2px 16px;">' ..
                    movie.season .. '</td></tr>\n'
        end
    end
    if #moviePreferences.liked > 0 then
        movieTableHTML = movieTableHTML ..
                             '<tr><th colspan="2">喜欢 <span style="font-size:smaller;">+100</span></th></tr>\n'
        for _, movie in ipairs(moviePreferences.liked) do
            movieTableHTML =
                movieTableHTML .. '<tr><td>[[File:' .. movie.image ..
                    '|24px|link=]] ' .. movie.name ..
                    '</td><td style="text-align: center; padding: 2px 16px;">' ..
                    movie.season .. '</td></tr>\n'
        end
    end
    if #moviePreferences.disliked > 0 then
        movieTableHTML = movieTableHTML ..
                             '<tr><th colspan="2">不喜欢<span style="font-size:smaller;"></span></th></tr>\n'
        for _, movie in ipairs(moviePreferences.disliked) do
            movieTableHTML =
                movieTableHTML .. '<tr><td>[[File:' .. movie.image ..
                    '|24px|link=]] ' .. movie.name ..
                    '</td><td style="text-align: center; padding: 2px 16px;">' ..
                    movie.season .. '</td></tr>\n'
        end
    end
    movieTableHTML = movieTableHTML .. '</table>'
    local lovedItems = {}
    local likedItems = {}
    local dislikedItems = {}
    for i, concession in ipairs(concessionPreferences.loved) do
        table.insert(lovedItems, '[[File:' .. concession.image ..
                         '|24px|link=]] ' .. concession.name .. '')
    end
    for i, concession in ipairs(concessionPreferences.liked) do
        table.insert(likedItems, '[[File:' .. concession.image ..
                         '|24px|link=]] ' .. concession.name .. '')
    end
    for i, concession in ipairs(concessionPreferences.disliked) do
        table.insert(dislikedItems, '[[File:' .. concession.image ..
                         '|24px|link=]] ' .. concession.name .. '')
    end
    local concessionTableHTML =
        '<table class="wikitable" style="margin: 0; margin-top: 1em;">\n'
    local likedCount = #likedItems
    local dislikedCount = #dislikedItems
    local leftBottomTitle, leftBottomItems, leftBottomIcon, leftBottomScore
    local rightTitle, rightItems, rightIcon, rightScore
    if likedCount <= dislikedCount then
        leftBottomTitle = "喜欢"
        leftBottomItems = likedItems
        leftBottomIcon = "ConcessionLike.png"
        leftBottomScore = "+25"
        rightTitle = "不喜欢"
        rightItems = dislikedItems
        rightIcon = "ConcessionDislike.png"
        rightScore = ""
    else
        leftBottomTitle = "不喜欢"
        leftBottomItems = dislikedItems
        leftBottomIcon = "ConcessionDislike.png"
        leftBottomScore = ""
        rightTitle = "喜欢"
        rightItems = likedItems
        rightIcon = "ConcessionLike.png"
        rightScore = "+25"
    end
    concessionTableHTML = concessionTableHTML .. '<tr>\n'
    concessionTableHTML = concessionTableHTML ..
                              '<th>[[File:ConcessionLove.png|24px|link=]] 最爱 <span style="font-size:smaller;">+50</span></th>\n'
    concessionTableHTML = concessionTableHTML ..
                              '<th rowspan="1" style="vertical-align: top;">[[File:' ..
                              rightIcon .. '|24px|link=]] ' .. rightTitle ..
                              ' <span style="font-size:smaller;">' .. rightScore ..
                              '</span></th>\n'
    concessionTableHTML = concessionTableHTML .. '</tr>\n'
    concessionTableHTML = concessionTableHTML ..
                              '<tr style="vertical-align: top;">\n'
    concessionTableHTML = concessionTableHTML ..
                              '<td style="padding: 4px 6px;">' ..
                              table.concat(lovedItems, '<br>') .. '</td>\n'
    concessionTableHTML = concessionTableHTML ..
                              '<td rowspan="3" style="padding: 4px 6px; vertical-align: top;">' ..
                              table.concat(rightItems, '<br>') .. '</td>\n'
    concessionTableHTML = concessionTableHTML .. '</tr>\n'
    concessionTableHTML = concessionTableHTML .. '<tr>\n'
    concessionTableHTML = concessionTableHTML ..
                              '<th style="height: 30px;">[[File:' ..
                              leftBottomIcon .. '|24px|link=]] ' ..
                              leftBottomTitle ..
                              ' <span style="font-size:smaller;">' ..
                              leftBottomScore .. '</span></th>\n'
    concessionTableHTML = concessionTableHTML .. '</tr>\n'
    concessionTableHTML = concessionTableHTML ..
                              '<tr style="vertical-align: top;">\n'
    concessionTableHTML = concessionTableHTML ..
                              '<td style="padding: 4px 6px;">' ..
                              table.concat(leftBottomItems, '<br>') .. '</td>\n'
    concessionTableHTML = concessionTableHTML .. '</tr>\n'
    concessionTableHTML = concessionTableHTML .. '</table>'
    local cinemaTableHTML =
        '<div style="display: flex; flex-wrap: wrap; gap: 16px;">\n'
    cinemaTableHTML = cinemaTableHTML .. '<div>\n' .. movieTableHTML ..
                          '\n</div>\n'
    cinemaTableHTML = cinemaTableHTML .. '<div>\n' .. concessionTableHTML ..
                          '\n</div>\n'
    cinemaTableHTML = cinemaTableHTML .. '</div>'
    return cinemaTableHTML
end
return p
                
                    沪公网安备 31011002002714 号