米斯特利亚Wiki正在建设中,本WIKI编辑权限开放!欢迎参与~!

全站通知:

模块:Quest

来自米斯特利亚WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

此模块的文档可以在模块:Quest/doc创建

local quests = require('模块:Quest/Data')
local items_data = require('模块:Items/Data')

local p = {}

-- 内部辅助函数:通过ID查找物品对象
local function findItemById(id)
    if not id then return nil end
    for _, subcategories in pairs(items_data) do
        if type(subcategories) == "table" then
            for _, items in pairs(subcategories) do
                if type(items) == "table" and items[id] then
                    return items[id]
                end
            end
        end
    end
    return nil
end

-- data: 你的任务总表(类似你贴的示例结构)
-- item_id: 例如 "golden_cookies"
-- 返回:所有需要该物品的任务对象列表(即任务的 table)
function questsRequiringItem(data, item_id)
    local function stageRequiresItem(stage, id)
        if type(stage) ~= "table" then return false end
        local req = stage.requirements
        if type(req) ~= "table" then return false end

        local hi = req.has_item
        if hi == nil then return false end

        local t = type(hi)
        if t == "string" then
            return hi == id
        elseif t == "table" then
            -- 既支持字典 { item = count } 也支持数组 { "itemA", "itemB" }
            for k, v in pairs(hi) do
                if type(k) == "string" and k == id then
                    return true
                end
                if type(v) == "string" and v == id then
                    return true
                end
            end
        end
        return false
    end

    local function questRequiresItem(quest, id)
        if type(quest) ~= "table" then return false end
        local stages = quest.stages
        if type(stages) ~= "table" then return false end

        -- stages 通常是数组
        for _, stage in ipairs(stages) do
            if stageRequiresItem(stage, id) then
                return true
            end
        end
        return false
    end

    local results = {}
    local seen = setmetatable({}, { __mode = "k" })  -- 弱键去重,避免重复插入同一表

    for category_name, category in pairs(data) do
        if type(category) == "table" then
            for quest_key, quest_obj in pairs(category) do
                if type(quest_obj) == "table" and questRequiresItem(quest_obj, item_id) then
                    if not seen[quest_obj] then
                        table.insert(results, quest_obj)
                        seen[quest_obj] = true
                    end
                end
            end
        end
    end

    return results
end

--[[
local frame = mw.getCurrentFrame()
frame.args[1] = "golden_cookies"
mw.logObject(p.questsRequiringItem(frame))
--]]
-- 根据物品ID获取涉及的任务,返回结果是用逗号分割的任务名称字符串
function p.questsRequiringItem(frame)
    local arg = frame.args[1]
    if not arg or arg == '' then
        return '<!-- 参数为空 -->'
    end

    local matched = questsRequiringItem(quests, arg)

    if #matched == 0 then
        return '<!-- 未找到任务 -->'
    end

    local names = {}
    for _, quest in ipairs(matched) do
        if quest.name then
            table.insert(names, quest.name)
        end
    end

    return table.concat(names, ", ")
end

-- 内部辅助函数:格式化奖励表
local function format_rewards(rewards_table, frame)
    if not rewards_table or #rewards_table == 0 then
        return ""
    end
    local parts = {}
    for _, reward in ipairs(rewards_table) do
        if reward.item then
            local count = reward.count or 1
            local item_obj = findItemById(reward.item)
            if item_obj and item_obj.name and item_obj.icon_sprite then
                table.insert(parts, frame:expandTemplate{
                    title = '物品',
                    args = { item_obj.icon_sprite, item_obj.name, count }
                })
            else
                table.insert(parts, count .. 'x ' .. reward.item)
            end
        elseif reward.gold then
            table.insert(parts, reward.gold .. ' 泰瑟币')
        elseif reward.renown then
            table.insert(parts, reward.renown .. ' 声望')
        elseif reward.unlock then
            table.insert(parts, '解锁: ' .. reward.unlock)
        elseif reward.recipe_scroll then
            local recipe_id = reward.recipe_scroll
            local item_obj = findItemById(recipe_id)
            if item_obj and item_obj.name and item_obj.icon_sprite then
                table.insert(parts, frame:expandTemplate{
                    title = '配方',
                    args = { item_obj.icon_sprite, item_obj.name, 1 }
                })
            else
                table.insert(parts, '配方: ' .. recipe_id)
            end
        end
    end
    return table.concat(parts, ', ')
end

-- 内部辅助函数:格式化解锁条件表
local function format_requirements(req_table)
    if not req_table then return "" end
    local parts = {}
    if req_table.is_season then
        table.insert(parts, '季节: ' .. req_table.is_season)
    end
    if req_table.has_progression then
        table.insert(parts, '进度要求: ' .. req_table.has_progression)
    end
    if req_table.completed_quest then
        table.insert(parts, '前置任务: ' .. req_table.completed_quest)
    end
    if req_table.reached_skill_level then
        for skill, level in pairs(req_table.reached_skill_level) do
            table.insert(parts, '技能等级: ' .. skill .. ' ' .. level .. '级')
        end
    end
    return table.concat(parts, '<br>')
end

-- 内部辅助函数:格式化完成条件表 (从stages提取)
local function format_completion_conditions(stages)
    if not stages or #stages == 0 then
        return ""
    end
    local parts = {}
    for i, stage in ipairs(stages) do
        if stage.objective_description then
            if #stages > 1 then
                table.insert(parts, i .. '. ' .. stage.objective_description)
            else
                table.insert(parts, stage.objective_description)
            end
        end
    end
    return table.concat(parts, '<br>')
end

-- 内部辅助函数:格式化需求物品 (从stages提取)
local function format_required_items(stages, frame)
    if not stages or #stages == 0 then
        return ""
    end
    local items = {}
        local function process_item_table(item_table)
        for item_id, count in pairs(item_table) do
            local count_str
            if type(count) == 'table' and count.min and count.max then
                count_str = count.min .. '-' .. count.max
            else
                count_str = tostring(count)
            end

            local item_obj = findItemById(item_id)
            if item_obj and item_obj.name and item_obj.icon_sprite then
                table.insert(items, frame:expandTemplate{
                    title = '物品',
                    args = { item_obj.icon_sprite, item_obj.name, count_str }
                })
            else
                table.insert(items, count_str .. 'x ' .. item_id)
            end
        end
    end
    for _, stage in ipairs(stages) do
        if stage.requirements then
            if stage.requirements.has_item then
                local required = stage.requirements.has_item
                if type(required) == 'table' then
                    process_item_table(required)
                elseif type(required) == 'string' then
                    local item_obj = findItemById(required)
                    if item_obj and item_obj.name and item_obj.icon_sprite then
                        table.insert(items, frame:expandTemplate{
                            title = '物品',
                            args = { item_obj.icon_sprite, item_obj.name, 1 }
                        })
                    else
                        table.insert(items, '1x ' .. required)
                    end
                end
            end
            if stage.requirements.filled_chest and stage.requirements.filled_chest.items then
                process_item_table(stage.requirements.filled_chest.items)
            end
        end
    end
    return table.concat(items, ', ')
end

-- 处理主线任务 (story_quests)
local function process_story_quest(quest_data, quest_id, frame)
    return {
        ["任务NPC"] = quest_data.npc_for_icon or "",
        ["任务名字"] = quest_data.name or "",
        ["任务描述"] = quest_data.description or "",
        ["任务解锁条件"] = "完成前置主线任务",
        ["任务完成条件"] = format_completion_conditions(quest_data.stages),
        ["任务需求物品"] = format_required_items(quest_data.stages, frame),
        ["任务奖励"] = format_rewards(quest_data.rewards, frame)
    }
end

-- 处理收集任务 (fetch_quests)
local function process_fetch_quest(quest_data, quest_id, frame)
    local request_board_data = quests.request_board and quests.request_board[quest_id] or nil
    return {
        ["任务NPC"] = quest_data.npc_for_icon or "",
        ["任务名字"] = quest_data.name or "",
        ["任务描述"] = quest_data.description or "",
        ["任务解锁条件"] = format_requirements(request_board_data and request_board_data.requirements or nil),
        ["任务完成条件"] = format_completion_conditions(quest_data.stages),
        ["任务需求物品"] = format_required_items(quest_data.stages, frame),
        ["任务奖励"] = format_rewards(quest_data.rewards, frame)
    }
end

-- 处理好感度任务 (heart_quests)
local function process_heart_quest(quest_data, quest_id, frame)
     return {
        ["任务NPC"] = quest_data.npc_for_icon or "",
        ["任务名字"] = quest_data.name or "",
        ["任务描述"] = quest_data.description or "",
        ["任务解锁条件"] = "与 " .. (quest_data.npc_for_icon or 'NPC') .. " 的好感度达到特定等级",
        ["任务完成条件"] = format_completion_conditions(quest_data.stages),
        ["任务需求物品"] = format_required_items(quest_data.stages, frame),
        ["任务奖励"] = format_rewards(quest_data.rewards, frame)
    }
end

-- 处理皇冠任务 (crown_quests)
local function process_crown_quest(quest_data, quest_id, frame)
    return {
        ["任务NPC"] = quest_data.npc_for_icon or "",
        ["任务名字"] = quest_data.name or "",
        ["任务描述"] = quest_data.description or "",
        ["任务解锁条件"] = "由皇冠任务注册表顺序解锁",
        ["任务完成条件"] = format_completion_conditions(quest_data.stages),
        ["任务需求物品"] = format_required_items(quest_data.stages, frame),
        ["任务奖励"] = format_rewards(quest_data.rewards, frame)
    }
end

-- 处理塔利挑战 (tali_challenges)
local function process_tali_challenge(quest_data, quest_id, frame)
    return {
        ["任务NPC"] = quest_data.npc_for_icon or "",
        ["任务名字"] = quest_data.name or "",
        ["任务描述"] = quest_data.description or "",
        ["任务解锁条件"] = "由塔利挑战注册表顺序解锁",
        ["任务完成条件"] = format_completion_conditions(quest_data.stages),
        ["任务需求物品"] = format_required_items(quest_data.stages, frame),
        ["任务奖励"] = format_rewards(quest_data.rewards, frame)
    }
end

-- 数据处理函数,现在接收frame对象
function p._process(frame)
    local quest_type = frame.args[1]
    if not quest_type then
        return error('错误:未提供任务类型。')
    end

    if not quests or not quests[quest_type] then
        return error('错误:无效的任务类型或在数据模块中找不到数据:' .. quest_type)
    end

    local quests_to_process = quests[quest_type]
    local results_table = {}

    local processing_functions = {
        story_quests = process_story_quest,
        fetch_quests = process_fetch_quest,
        heart_quests = process_heart_quest,
        crown_quests = process_crown_quest,
        tali_challenges = process_tali_challenge
    }

    local process_func = processing_functions[quest_type]

    if not process_func then
        return error('错误:没有为任务类型 ' .. quest_type .. ' 定义处理函数。')
    end

    for quest_id, quest_data in pairs(quests_to_process) do
        -- 传入quest_id和frame
        local processed_quest = process_func(quest_data, quest_id, frame)
        if processed_quest then
            table.insert(results_table, processed_quest)
        end
    end

    return results_table
end

-- 渲染函数入口
-- @param frame table MediaWiki frame object. frame.args[1] 是任务类型 (例如 'story_quests')
-- @return string 渲染完成的wikitext表格
function p.renderProcess(frame)
    -- 1. 调用process获取处理后的数据table
    -- 注意:这里我们将frame传递给process,以便它能被传递给格式化函数
    local processed_quests_data = p._process(frame)

    if type(processed_quests_data) ~= 'table' then
        return processed_quests_data -- 返回错误信息
    end

    -- 2. 为table里的每个子table应用模板:{{任务/行}}
    local rendered_rows = {}
    for _, quest_table in ipairs(processed_quests_data) do
        local row_wikitext = frame:expandTemplate{
            title = '任务/行',
            args = quest_table
        }
        table.insert(rendered_rows, row_wikitext)
    end

    -- 3. 返回整个table应用模板:{{任务/表}}
    local all_rows_wikitext = table.concat(rendered_rows, '\n')
    local final_table = frame:expandTemplate{
        title = '任务/表',
        args = { [1] = all_rows_wikitext }
    }

    return final_table
end

-- mw.logObject(p.process({args={"story_quests"}}))

return p