维护提醒
BWIKI 全站将于 9 月 3 日(全天)进行维护,期间无法编辑任何页面或发布新的评论。
全站通知:
模块:GiftTastesNPC
刷
历
编
跳到导航
跳到搜索
-- #############################################################################
-- # Module:GiftTastesNPC (V6 - Final) - 最终版 v6 (优先级修正)
-- # ---------------------------------------------------------------------------
-- # 最终版逻辑模块,能够处理数字ID、字符串ID和上下文标签。
-- # 依赖: Module:Object/data, Module:GiftTastesNPC/data
-- # 经过C#逻辑审查后修订,加入了对古物、默认规则(Edibility/Price)等的处理。
-- #############################################################################
p = {}
-- 1. 加载依赖模块
local objectData = require('Module:Object/data')
local giftTasteData = require('Module:GiftTastesNPC/data')
-- #############################################################################
-- # 内部辅助函数与数据映射
-- #############################################################################
local categoryNames = {
[-103] = "技能书", -- skillBook_Category
[-102] = "书", -- Book_Category
[-100] = "服装", -- category_clothes
[-99] = "工具", -- Tool.cs.14307
[-97] = "鞋类", -- Boots.cs.12501
[-96] = "戒指", -- Ring.cs.1
[-81] = "采集品", -- Object.cs.12869
[-80] = "花", -- Object.cs.12866
[-79] = "水果", -- Object.cs.12854
[-75] = "蔬菜", -- Object.cs.12851
[-74] = "种子", -- Object.cs.12855
[-28] = "怪物战利品", -- Object.cs.12867
[-27] = "工匠物品", -- Object.cs.12862
[-26] = "工匠物品", -- Object.cs.12862
[-25] = "菜品", -- Object.cs.12853
[-24] = "装饰", -- Object.cs.12859 / Furniture_Decoration
[-22] = "钓具", -- Object.cs.12858
[-21] = "鱼饵", -- Object.cs.12857
[-20] = "垃圾", -- Object.cs.12860
[-19] = "化肥", -- Object.cs.12856
[-18] = "动物制品", -- Object.cs.12864
[-16] = "资源", -- Object.cs.12868
[-15] = "资源", -- Object.cs.12868
[-14] = "动物制品", -- Object.cs.12864
[-12] = "矿物", -- Object.cs.12850
[-8] = "制造品", -- Object.cs.12863
[-7] = "菜品", -- Object.cs.12853
[-6] = "动物制品", -- Object.cs.12864
[-5] = "动物制品", -- Object.cs.12864
[-4] = "鱼", -- Object.cs.12852
[-2] = "矿物" -- Object.cs.12850
}
local contextTagNames = {
book_item = "所有书",
category_trinket = "所有饰品",
category_bait = "所有鱼饵",
category_monster_loot = "所有怪物战利品",
}
local function _getItemInfo(itemId)
return objectData[tostring(itemId)]
end
local function _getCategoryName(categoryId)
return categoryNames[categoryId] or "未知分类"
end
local function _getContextTagName(tag)
return contextTagNames[tag] or tag
end
local function _itemMatchesRule(itemInfo, ruleId)
if not itemInfo then return false end
if type(ruleId) == 'number' then
if ruleId >= 0 then return false
else return itemInfo.Category == ruleId end
elseif type(ruleId) == 'string' then
if objectData[ruleId] then return false
else
if itemInfo.ContextTags then
for _, tag in ipairs(itemInfo.ContextTags) do
if tag == ruleId then return true end
end
end
return false
end
end
return false
end
local function _isItemCoveredBySpecificTaste(itemInfo, itemId, personalTastes, universalTastes)
local allTasteLevels = {"love", "like", "neutral", "dislike", "hate"}
itemId = tostring(itemId)
if not personalTastes then return false end
for _, level in ipairs(allTasteLevels) do
local tasteList = (personalTastes[level] and personalTastes[level].items) or {}
for _, rule in ipairs(tasteList) do
if tostring(rule) == itemId or _itemMatchesRule(itemInfo, rule) then
return true
end
end
end
if itemInfo.Type == "Arch" then return true end
for _, level in ipairs(allTasteLevels) do
local tasteList = universalTastes[level] or {}
for _, rule in ipairs(tasteList) do
if tostring(rule) == itemId or _itemMatchesRule(itemInfo, rule) then
return true
end
end
end
return false
end
-- #############################################################################
-- # 主要外部函数
-- #############################################################################
function p.describeTastes(frame)
local args = frame.args
local npcName = args.npc or args[1]
local targetLevel = args.taste or args[2]
if not npcName or not targetLevel then
return '<strong class="error">错误:必须提供 "npc" 和 "taste" 参数。</strong>'
end
npcName = mw.text.trim(npcName)
targetLevel = mw.text.trim(targetLevel):lower()
local personalTastes = giftTasteData.npcs[npcName]
if not personalTastes then
return string.format('<strong class="error">错误:未找到NPC "%s"。</strong>', npcName)
end
local universalTastes = giftTasteData.universal
local tasteLevels = {"love", "like", "neutral", "dislike", "hate"}
local outputPhrases = {}
local outputItems = {}
local seenItems = {}
local function addItem(name)
if name and not seenItems[name] then
table.insert(outputItems, name)
seenItems[name] = true
end
end
-- 1. 处理个人偏好
local personalIds = (personalTastes[targetLevel] and personalTastes[targetLevel].items) or {}
for _, id in ipairs(personalIds) do
if type(id) == 'number' and id >= 0 or (type(id) == 'string' and objectData[id]) then
local itemInfo = _getItemInfo(id)
if itemInfo and itemInfo.Name then addItem(itemInfo.Name) end
else
local ruleName
if type(id) == 'number' and id < 0 then ruleName = "所有" .. _getCategoryName(id)
elseif type(id) == 'string' then ruleName = _getContextTagName(id) end
if ruleName then
local exceptions = {}
local seenExceptions = {}
local function addException(name)
if name and not seenExceptions[name] then
table.insert(exceptions, name)
seenExceptions[name] = true
end
end
for _, level in ipairs(tasteLevels) do
if level ~= targetLevel then
local levelItems = (personalTastes[level] and personalTastes[level].items) or {}
for _, exceptionId in ipairs(levelItems) do
local itemInfo = _getItemInfo(exceptionId)
if _itemMatchesRule(itemInfo, id) then addException(itemInfo.Name) end
end
end
end
local universalOverrides = {"love", "like"}
for _, uni_level in ipairs(universalOverrides) do
local universalItems = universalTastes[uni_level] or {}
for _, universalId in ipairs(universalItems) do
if type(universalId) == 'number' and universalId >= 0 or (type(universalId) == 'string' and objectData[universalId]) then
local itemInfo = _getItemInfo(universalId)
if _itemMatchesRule(itemInfo, id) then addException(itemInfo.Name) end
end
end
end
local text = ruleName
if #exceptions > 0 then text = text .. "(" .. table.concat(exceptions, "、") .. "除外)" end
table.insert(outputPhrases, text)
end
end
end
-- 2. 处理古物 (Arch/Artifact) 的硬编码规则
local function processArchRule(likeRule)
local exceptions = {}
local seenExceptions = {}
for _, level in ipairs(tasteLevels) do
if (likeRule and level ~= "like") or (not likeRule and level ~= "dislike") then
local levelItems = (personalTastes[level] and personalTastes[level].items) or {}
for _, exceptionId in ipairs(levelItems) do
local itemInfo = _getItemInfo(exceptionId)
if itemInfo and itemInfo.Type == "Arch" and not seenExceptions[itemInfo.Name] then
table.insert(exceptions, itemInfo.Name)
seenExceptions[itemInfo.Name] = true
end
end
end
end
local text = "所有古物"
if #exceptions > 0 then text = text .. "(" .. table.concat(exceptions, "、") .. "除外)" end
table.insert(outputPhrases, text)
end
if targetLevel == "like" and (npcName == "Penny" or npcName == "Dwarf") then
processArchRule(true)
elseif targetLevel == "dislike" and not (npcName == "Penny" or npcName == "Dwarf") then
processArchRule(false)
end
-- 3. 处理默认规则 (Edibility / Price)
if targetLevel == "hate" then
for itemId, itemInfo in pairs(objectData) do
if itemInfo.Category ~= -999 and itemInfo.CanBeGivenAsGift and itemInfo.Price ~= 0 then
if itemInfo.Edibility and itemInfo.Edibility < 0 and itemInfo.Edibility ~= -300 then
if not _isItemCoveredBySpecificTaste(itemInfo, itemId, personalTastes, universalTastes) then
addItem(itemInfo.Name)
end
end
end
end
elseif targetLevel == "dislike" then
for itemId, itemInfo in pairs(objectData) do
if itemInfo.Category ~= -999 and itemInfo.CanBeGivenAsGift and itemInfo.Price ~= 0 then
if itemInfo.Price and itemInfo.Price < 20 then
if not _isItemCoveredBySpecificTaste(itemInfo, itemId, personalTastes, universalTastes) then
addItem(itemInfo.Name)
end
end
end
end
end
-- 4. 处理通用偏好并寻找个人例外
local universalExceptions = {}
local seenUniversalExceptions = {}
local function addUniversalException(name) if name and not seenUniversalExceptions[name] then table.insert(universalExceptions, name); seenUniversalExceptions[name] = true end end
local universalTargetRules = universalTastes[targetLevel] or {}
for _, u_rule in ipairs(universalTargetRules) do
local u_info = _getItemInfo(u_rule)
local u_is_specific_item = u_info ~= nil
for _, level in ipairs(tasteLevels) do
if level ~= targetLevel then
local personalRules = (personalTastes[level] and personalTastes[level].items) or {}
for _, p_rule in ipairs(personalRules) do
local p_is_specific_item = _getItemInfo(p_rule) ~= nil
-- 检查是否存在冲突
local conflict = false
if u_is_specific_item and _itemMatchesRule(u_info, p_rule) then -- 通用物品 vs 个人分类/标签
conflict = true
elseif not u_is_specific_item and p_is_specific_item and _itemMatchesRule(_getItemInfo(p_rule), u_rule) then -- 通用分类/标签 vs 个人物品
conflict = true
elseif tostring(u_rule) == tostring(p_rule) then -- 规则完全相同
conflict = true
end
if conflict then
local exceptionName = nil
if u_is_specific_item then -- 如果通用规则是具体物品,那么例外就是这个物品
-- 优先级判断:只有当个人规则也是具体物品时,才能覆盖通用的具体物品规则
if p_is_specific_item or (type(p_rule) == 'string' and not objectData[p_rule]) then
exceptionName = u_info.Name
end
else -- 如果通用规则是分类/标签
if p_is_specific_item then -- 而个人规则是具体物品,那么例外就是这个个人物品
exceptionName = _getItemInfo(p_rule).Name
else -- 两者都是分类/标签,则例外是这个分类/标签
if type(u_rule) == 'number' then exceptionName = "所有" .. _getCategoryName(u_rule) else exceptionName = _getContextTagName(u_rule) end
end
end
if exceptionName then addUniversalException(exceptionName) end
end
end
end
end
end
local universalTextMapping = { love = "最爱", like = "喜欢", neutral = "一般", dislike = "不喜欢", hate = "讨厌" }
local universalText = "所有普遍" .. (universalTextMapping[targetLevel] or targetLevel) .. "的礼物"
if #universalExceptions > 0 then
table.sort(universalExceptions)
universalText = universalText .. "(" .. table.concat(universalExceptions, "、") .. "除外)"
end
table.insert(outputPhrases, universalText)
-- 5. 如果查询的是中立,则全面检查并补充说明“默认中立”的物品和分类
if targetLevel == "neutral" then
local defaultNeutralPhrases = {}
local function isRuleDefined(ruleId)
for _, level in ipairs(tasteLevels) do
for _, p_rule in ipairs((personalTastes[level] and personalTastes[level].items) or {}) do
if tostring(p_rule) == tostring(ruleId) then return true end
end
for _, u_rule in ipairs(universalTastes[level] or {}) do
if tostring(u_rule) == tostring(ruleId) then return true end
end
end
return false
end
-- 5a. 动态检查所有通用可赠送分类
local giftableCategoryIds = { -2, -4, -5, -6, -12, -75, -79, -80, -81 }
local handledCategories = {} -- 记录已被“所有XX”描述覆盖的分类
for _, catId in ipairs(giftableCategoryIds) do
if not isRuleDefined(catId) then
handledCategories[catId] = true -- 将此分类标记为已处理
local exceptions = {}
local seenExceptions = {}
local function findExceptionsInList(list) for _, rule in ipairs(list) do local itemInfo = _getItemInfo(rule) if itemInfo and itemInfo.Category == catId and not seenExceptions[itemInfo.Name] then table.insert(exceptions, itemInfo.Name); seenExceptions[itemInfo.Name] = true end end end
for _, level in ipairs(tasteLevels) do findExceptionsInList((personalTastes[level] and personalTastes[level].items) or {}); findExceptionsInList(universalTastes[level] or {}) end
local text = "所有" .. _getCategoryName(catId)
if #exceptions > 0 then table.sort(exceptions); text = text .. "(" .. table.concat(exceptions, "、") .. "除外)" end
table.insert(defaultNeutralPhrases, text)
end
end
-- 5b. 动态遍历所有物品,筛选出未被分类覆盖的、值得关注的独立中立物品
for itemId, itemInfo in pairs(objectData) do
-- 条件1: 物品本身是“默认中立”的
if not isRuleDefined(itemId) then
-- 条件2: 物品不属于任何一个已被“所有XX”覆盖的分类
if not handledCategories[itemInfo.Category] then
-- 条件3: 物品是“值得关注的”
-- local isNotable = (itemInfo.Category == 0) -- (分类为0)
local isNotable = false
if not isNotable and itemInfo.ContextTags then
for _, tag in ipairs(itemInfo.ContextTags) do
-- if tag == 'flower_item' then -- (花)
-- isNotable = true; break
-- end
if tag == 'forage_item' then -- (采集品)
isNotable = true; break
end
end
end
if isNotable then
addItem(itemInfo.Name)
end
end
end
end
if #defaultNeutralPhrases > 0 then
table.sort(defaultNeutralPhrases)
for _, phrase in ipairs(defaultNeutralPhrases) do
table.insert(outputPhrases, phrase)
end
end
end
-- 6. 格式化并返回
local finalOutput = {}
for _, phrase in ipairs(outputPhrases) do table.insert(finalOutput, phrase) end
table.sort(outputItems)
for _, itemName in ipairs(outputItems) do table.insert(finalOutput, itemName) end
if #finalOutput == 0 then return "" end
return mw.text.listToText(finalOutput)
end
return p