维护提醒
BWIKI 全站将于 9 月 3 日(全天)进行维护,期间无法编辑任何页面或发布新的评论。
全站通知:
模块:Templates
刷
历
编
跳到导航
跳到搜索
local cache = require "mw.ext.LuaCache"
local KEY_PREFIX = "Module:Templates"
local EXP_TIME = 172800
local p = {}
-- 表格行拼接(普通)
function p.row(frame)
local args = frame:getParent().args
local cells = {}
local count = 1
for i, value in ipairs(args) do
if value ~= 'row' then
cells[count] = '<td>' .. value .. '</td>'
count = count + 1
end
end
return '<tr>' .. table.concat(cells) .. '</tr>'
end
-- 表格行拼接(GiftRow)
function p.itemRow(frame)
local args = frame:getParent().args
local cells = {}
local count = 1
for i, value in ipairs(args) do
if value ~= 'row' then
if count == 1 then
cells[count] = '<td>{{Name|' .. value .. '}}</td>'
count = count + 1
cells[count] = '<td>{{Description|' .. value .. '}}</td>'
else
cells[count] = '<td>' .. value .. '</td>'
end
count = count + 1
end
end
return frame:preprocess('<tr>' .. table.concat(cells) .. '</tr>')
end
-- 检查页面是否存在
function p.checkPageExistence(frame)
local pageName = frame.args[1]
if mw.title.new(pageName).exists then
return "exists" -- return string.format('[[%s]]', pageName)
else
return "notexists" -- return pageName
end
end
-- 获取上层版本号
function p.getVerP(frame)
local versionMapping = {
["1.07a"] = "1.0",
["1.07"] = "1.0",
["1.06"] = "1.0",
["1.051b"] = "1.0",
["1.051"] = "1.0",
["1.05"] = "1.0",
["1.04"] = "1.0",
["1.03"] = "1.0",
["1.02"] = "1.0",
["1.01"] = "1.0",
["1.0"] = "1.0",
}
local version = frame.args[1] or "1.6"
if versionMapping[version] then
return "版本历史/1.0"
end
if version == "1.11" then
return "版本历史/1.1"
end
local major, minor = version:match("^(%d+)%.(%d+)")
if major and minor then
return "版本历史/" .. major .. "." .. minor
end
return "版本历史"
end
-- 获取主版本号
function p.getVer(frame)
local version = frame.args[1] or "stable"
local versions = {
stable = "1.6",
beta = "1.6",
wegame = "1.5.4"
}
return versions[version] or version
end
-- 移除内链
function p.removeLinks(frame)
local text = frame.args[1] or ""
text = text:gsub("%[%[([^\]]+)%]%]", "%1")
return text
end
-- SVE Quote 兼容性处理
function p.quoteSVE(frame)
local quote = frame.args[1] or ""
quote = quote:match("^%s*(.-)%s*$")
quote = quote:gsub('^%s*["]*(.-)["]*%s*$', '%1')
quote = quote:gsub('^%s*“*(.-)”*%s*$', '%1')
quote = quote:gsub('^%s*“(.-)”%s*$', '%1')
quote = quote:gsub('^%s*”(.-)“%s*$', '%1')
return quote
end
-- 获取首条链接
function p.getFirstLink(frame)
local source = frame.args.source or ""
if source == "" then return "" end
local first_link = source:match("%[%[([^%]]-)]%]")
return first_link or ""
end
-- 鱼塘 header 生成
function p.generateHeaders(frame)
local args = frame.args
local count = tonumber(args[1]) or 0
local result = {}
for i = 1, count do
table.insert(result, string.format('<th data-sort-type="number">%d</th>', i))
end
return table.concat(result)
end
-- 鱼塘 row 生成
function p.generateRow(frame)
local args = mw.text.split(frame.args[1], ",")
local result = {}
local i = 1
table.insert(result, string.format('<td data-sort-value="%s">%s</td>', args[i + 1], args[i]))
i = i + 2
while i <= #args - 3 do
local colspan = tonumber(args[i])
local content = args[i + 1] or "/"
table.insert(result, string.format('<td colspan="%d">%s</td>', colspan, content))
i = i + 2
end
table.insert(result, string.format('<td>%s</td>', args[i]))
table.insert(result, string.format('<td>%s</td>', args[i + 1]))
return "<tr>" .. table.concat(result) .. "</tr>"
end
-- 隐藏列按钮生成
function p.generateButtons(frame)
local n = tonumber(frame.args[1])
local result = ""
for i = 1, n do
result = result .. '<li class="btn btn-default control-column" role="button" data-column="' .. i .. '">第 ' .. i .. ' 列</li>'
end
return result
end
-- 鱼类一览 分类格式化
function p.formatCategories(frame)
local categories = frame.args.categories or ''
local result = {}
for category in mw.text.gsplit(categories, ',') do
local displayText = category:match("%[%[:分类:[^|]+|(.-)%]%]")
if displayText then
table.insert(result, mw.text.trim(displayText))
else
table.insert(result, mw.text.trim(category))
end
end
return table.concat(result, ", ")
end
-- 鱼类一览 检查地点
function p.checkLocations(frame)
local input = frame.args[1] or ""
local keywords = { "突变虫穴", "巫婆沼泽", "农场", "夜市" }
local result = {}
for _, keyword in ipairs(keywords) do
if input:find(keyword, 1, true) then
table.insert(result, keyword)
end
end
return table.concat(result, ", ")
end
-- 鱼类一览 检查是否全天
function p.checkAllDay(frame)
local input = frame.args[1] or ""
if input:find("全天") then
return "全天"
end
if input:find("任意") then
return "全天"
else
return "非全天"
end
end
-- ExtractStats
-- 从 {{Name}} 模板文本中提取特定属性的数值
-- 示例1:基础使用
-- {{#invoke:Templates|getStatValue|{{Name|Defense|+5}}{{Name|Immunity|+8}}|Immunity}}
-- 结果:8
-- 示例2:提取其他属性
-- {{#invoke:Templates|getStatValue|{{Name|Defense|+5}}{{Name|Immunity|+8}}|Defense}}
-- 结果:5
-- 示例3:处理多位数值
-- {{#invoke:Templates|getStatValue|{{Name|Immunity|+500}}|Immunity}}
-- 结果:500
function p.getStatValue(frame)
local text = frame.args[1] or '' -- 第一个参数是要搜索的文本
local stat = frame.args[2] or '' -- 第二个参数是要查找的属性名
-- 使用模式匹配,用 stat 变量构建匹配模式
local value = string.match(text, stat..".-(%+(%d+))")
return value or ''
end
-- Calcedibility
-- 用于计算可食用物品的能量值和生命值。
-- 使用了可食用性进行计算;可以用于输出未格式化的原始数字(用于表格排序的 data-sort-value),也可以输出用于实际显示的数字。
--ceh = calculate edibility (energy/health)
function p.ce(frame)
local item = string.lower(frame.args.im)
local edibility = tonumber(frame.args.ed)
local quality = tonumber(frame.args.q)
local ulang = string.upper(frame.args.ll)
local result, formattedresult, temp, length
if edibility == 0 then return 0 end
if item == "energy" then
result = math.floor(math.ceil(edibility*2.5) + edibility*quality)
else
result = math.floor(math.floor(math.ceil(edibility*2.5) + edibility*quality)*0.45)
end
formattedresult = mw.language.getContentLanguage():formatNum(result)
return formattedresult
end
-- Tcartprice
-- 用于计算给定物品出现在旅行货车时可能的价格范围。
function p.tprice(frame)
local price = frame.args["price"]
local separator = frame.args["separator"]
local gold = frame.args["gold"]
local space = string.lower(frame.args["spacearoundseparator"])
local ulang = string.upper(mw.language.getContentLanguage():getCode())
local formattedprice = "[[File:Gold.png|18px|link=]]"
if (string.lower(price) == "furniture") then
formattedprice = formattedprice .. "250"
if space == "true" then formattedprice = formattedprice .. " " .. separator .. " "
else formattedprice = formattedprice .. separator
end
formattedprice = formattedprice .. tostring(mw.language.getContentLanguage():formatNum(2500))
else
local lowprice = tonumber(price) * 3
local highprice = tonumber(price) * 5
local fmlowprice = tostring(mw.language.getContentLanguage():formatNum(lowprice))
local fmhighprice = tostring(mw.language.getContentLanguage():formatNum(highprice))
if (lowprice <= 100) then
formattedprice = formattedprice .. "100"
else
formattedprice = formattedprice .. fmlowprice
end
if space == "true" then formattedprice = formattedprice .. " " .. separator .. " "
else formattedprice = formattedprice .. separator
end
if (highprice <= 1000) then
formattedprice = formattedprice .. tostring(mw.language.getContentLanguage():formatNum(1000))
else
formattedprice = formattedprice .. fmhighprice
end
end
return formattedprice .. gold
end
-- Calcgrangepoints
-- 用于计算在星露谷展览会展览给定物品会获得的分数。
function p.cgp(frame)
--Template must call Calcsellprice and send result here
local price = tonumber(frame.args.p)
--quality must be 0, 1, 2, or 4
local quality = tonumber(frame.args.q)
local totalpoints = 0
totalpoints = quality + 1
if (price >= 20) then totalpoints = totalpoints + 1 end
if (price >= 90) then totalpoints = totalpoints + 1 end
if (price >= 200) then totalpoints = totalpoints + 1 end
if (price >= 300 and quality < 2) then
totalpoints = totalpoints + 1 end
if (price >= 400 and quality < 1) then
totalpoints = totalpoints + 1 end
return totalpoints
end
-- Calcsellprice
-- 用于计算出售给定物品可获得的收入。
-- 可以用于输出未格式化的原始数字(用于表格排序的 data-sort-value),也可以输出用于实际显示的数字。
-- Assumes baseprice is always an integer
-- Adds the language-appropriate letters/characters for 'gold'
-- csp = calculate sell price
function p.csp(frame)
local Float32Utils = require "Module:Utils/Float32"
local item = string.lower(frame.args.im)
local baseprice = tonumber(frame.args.bp)
local quality = tonumber(frame.args.q)
local profmult = tonumber(frame.args.pm)
local toFormatOrNotToFormat = string.lower(frame.args.fm)
local cacheKey = KEY_PREFIX .. "|" .. "csp" .. "|" .. (item or "") .. "|" .. (baseprice or "") .. "|" .. (quality or "") .. "|" .. (profmult or "") .. "|" .. (toFormatOrNotToFormat or "")
if (cache.get(cacheKey)) then
local result = cache.get(cacheKey)
return result
end
if ((baseprice == nil) or (baseprice == 0)) then return 0 end
local qualitymult, artisanprice
if (profmult == nil) or (item == "coffee") or (item == "oil") then profmult = 1 end
if (quality == 1) then qualitymult = 1.25
elseif (quality == 2) then qualitymult = 1.5
elseif (quality == 4) then qualitymult = 2
else qualitymult = 1
end
-- Calculate some artisan goods prices from base ingredient price
-- These are needed for data-sort-values on pages like Flowers, Fruit, Vegetables
if (string.find(item, "wine") ~= nil) then
artisanprice = (baseprice * 3)
elseif (string.find(item, "juice") ~= nil) then
artisanprice = math.floor(baseprice * 2.25)
elseif ((string.find(item, "jelly")) or (string.find(item, "pickles")) ~= nil) then
artisanprice = (50 + (baseprice * 2))
elseif (string.find(item, "dried") ~= nil) then
artisanprice = math.floor((baseprice * 7.5) + 25)
elseif (item == "honey") then
-- This is a hack that works only because
-- no flower has a base sell price of 100
if (baseprice ~= 100) then
artisanprice = (100 + (baseprice * 2))
else
artisanprice = 100
end
elseif (string.find(item, "aged roe") ~= nil) then
artisanprice = (2 * (30 + math.floor(baseprice / 2)))
elseif (string.find(item, "roe") ~= nil) then
artisanprice = (30 + math.floor(baseprice / 2))
elseif (string.find(item, "smoked") ~= nil) then
artisanprice = (baseprice * 2)
--[[elseif (item == "pale ale") then artisanprice = 300
elseif ((item == "beer") or (item == "mead")) then artisanprice = 200
elseif (item == "green tea") then artisanprice = 100
elseif (item == "caviar") then artisanprice = 500
elseif (item == "cheese") then artisanprice = 230
elseif (item == "goat cheese") then artisanprice = 400
elseif (item == "cloth") then artisanprice = 470
elseif (item == "mayonnaise") then artisanprice = 190
elseif (item == "duck mayonnaise") then artisanprice = 375
elseif (item == "void mayonnaise") then artisanprice = 275
elseif (item == "dinosaur mayonnaise") then artisanprice = 800
elseif (item == "truffle oil") then artisanprice = 1065
]]
else artisanprice = baseprice
end
local sum = math.floor(Float32Utils.MulFloat32(math.floor(qualitymult * artisanprice),profmult))
if toFormatOrNotToFormat == "false" then
cache.set(cacheKey, sum, EXP_TIME)
return sum
end
local formattedSum = mw.language.getContentLanguage():formatNum(sum)
local ulang = string.upper(mw.language.getContentLanguage():getCode())
if ulang == "ZH" then -- 保留用作参考
cache.set(cacheKey, formattedSum .. "金", EXP_TIME)
return formattedSum .. "金"
else
cache.set(cacheKey, formattedSum, EXP_TIME)
return formattedSum
end
end
-- Tabs
function p.tabs(frame)
local args = frame:getParent().args
local align = args.align or 'left'
local tabsData = {}
local i = 1
while true do
local title = args['title'.. i]
local content = args['content'.. i]
if not title or mw.text.trim(title) == '' then
break
end
table.insert(tabsData, { title = title, content = content or '' })
i = i + 1
end
if #tabsData == 0 then
return '<div class="errorbox">错误:未提供任何有效的标签页数据。请使用 "|title1=..." 和 "|content1=..." 格式提供参数。</div>'
end
local root = mw.html.create('div')
root:addClass('custom-tabs-container')
local tablist = root:tag('div')
tablist:addClass('custom-tabs-list'):addClass('justify-content-' .. align):attr('role', 'tablist')
local contentContainer = root:tag('div')
contentContainer:addClass('custom-tabs-content')
for index, data in ipairs(tabsData) do
-- 创建标签链接
local tabLink = tablist:tag('a')
tabLink:addClass('custom-tabs-tab')
:attr('role', 'tab')
:wikitext(data.title)
-- 创建内容面板
local tabPanel = contentContainer:tag('div')
tabPanel:addClass('custom-tabs-panel')
:attr('role', 'tabpanel')
-- 如果是第一个标签页,直接在模块中设置其为 active 状态
if index == 1 then
tabLink:addClass('is-active')
tabLink:attr('aria-selected', 'true')
end
-- 根据是否为第一个标签页,构建不同的 Widget 调用字符串
local detailsContent
if index == 1 then
-- 为第一个 details 添加 open=true,使其默认展开
detailsContent = table.concat({
'{{#Widget:Details1}}', -- 假设 Widget 支持 open 参数
'{{#Widget:Summary}}',
'{{#Widget:Summary2}}',
data.content,
'{{#Widget:Details2}}'
})
else
-- 其他标签页保持默认关闭状态
detailsContent = table.concat({
'{{#Widget:Details}}',
'{{#Widget:Summary}}',
'{{#Widget:Summary2}}',
data.content,
'{{#Widget:Details2}}'
})
end
-- 将包含 Widget 调用的字符串添加到面板中
tabPanel:wikitext(detailsContent)
end
-- 预处理整个 HTML 结构,解析其中的 Widget 调用
return frame:preprocess(tostring(root))
end
-- Buffs
-- 效果图标和链接的映射表
local effectData = {
-- 基础属性
["攻击"] = {icon = "Attack Buff.png", link = "攻击", aliases = {"attack"}},
["防御"] = {icon = "Defense Buff.png", link = "防御", aliases = {"defense"}},
["速度"] = {icon = "Speed Buff.png", link = "速度", aliases = {"speed"}},
["运气"] = {icon = "Luck Buff.png", link = "运气", aliases = {"幸运", "luck"}},
-- 技能相关
["采矿"] = {icon = "Mining Skill Icon.png", link = "采矿", aliases = {"挖矿", "mining", "mine"}},
["钓鱼"] = {icon = "Fishing Skill Icon.png", link = "钓鱼", aliases = {"fishing", "fish"}},
["耕种"] = {icon = "Farming Skill Icon.png", link = "耕种", aliases = {"farming", "farm"}},
["采集"] = {icon = "Foraging Skill Icon.png", link = "采集", aliases = {"觅食", "foraging", "forage"}},
-- 特殊效果
["磁力半径"] = {icon = "Magnetism Buff.png", link = "磁力半径", aliases = {"磁性", "magnetic"}},
["最大能量"] = {icon = "Max Energy Buff.png", link = "最大能量", aliases = {"能量", "energy", "max energy"}},
["战士能量"] = {icon = "Combat Skill Icon.png", link = "战士能量", aliases = {"warrior", "warrior energy"}},
["由巴的祝福"] = {icon = "Yoba's Blessing.png", link = "由巴的祝福", aliases = {"无敌", "yoba's blessing"}},
["肾上腺冲击"] = {icon = "Speed Buff.png", link = "肾上腺冲击", aliases = {"肾上腺", "adrenaline rush"}},
-- 负面效果
["眩晕"] = {icon = "Tipsy.png", link = "眩晕", aliases = {"醉酒", "tipsy"}},
["恶心"] = {icon = "Nauseated.png", link = "恶心", aliases = {"反胃", "nauseated"}},
["烧伤"] = {icon = "Burnt.png", link = "烧伤", aliases = {"灼烧", "burnt"}},
["黑暗"] = {icon = "Darkness.png", link = "黑暗", aliases = {"视野受限", "darkness"}},
["冻结"] = {icon = "Frozen.png", link = "冻结", aliases = {"冻结的", "frozen"}},
["虚弱"] = {icon = "Weakness.png", link = "虚弱", aliases = {"无力", "weakness"}},
["黏滑的"] = {icon = "Slimed.png", link = "黏滑的", aliases = {"史莱姆减速", "slimed"}},
["倒霉的"] = {icon = "Jinxed.png", link = "倒霉的", aliases = {"倒霉", "jinxed"}},
-- 食物效果
["蒜油"] = {icon = "Oil of Garlic Buff.png", link = "蒜油", aliases = {"garlic", "oil of garlic"}},
["墨汁意大利饺"] = {icon = "Squid Ink Ravioli Buff.png", link = "墨汁意大利饺", aliases = {"免负面", "ravioli", "squid ink ravioli"}},
["怪兽香水"] = {icon = "Monster Musk Buff.png", link = "怪兽香水", aliases = {"怪物吸引", "monster musk"}},
}
-- 创建别名到主名称的映射
local aliasMap = {}
for mainName, data in pairs(effectData) do
-- 主名称映射到自身
aliasMap[string.lower(mainName)] = mainName
-- 别名映射到主名称
for _, alias in ipairs(data.aliases or {}) do
aliasMap[string.lower(alias)] = mainName
end
end
function p.effectIcon(frame)
local args = frame:getParent().args
local effectName = mw.text.trim(args[1] or "")
local customIcon = mw.text.trim(args[2] or "")
if effectName == "" then
return ""
end
-- 查找效果数据
local normalizedName = string.lower(effectName)
local mainName = aliasMap[normalizedName]
if mainName and effectData[mainName] then
local data = effectData[mainName]
return string.format(
"[[File:%s|24px|link=]] [[效果|%s]]",
data.icon,
data.link
)
else
-- 如果没有找到匹配的效果,使用默认格式
local iconName = customIcon ~= "" and customIcon or effectName
return string.format(
"[[File:%s.png|24px|link=]] %s",
iconName,
effectName
)
end
end
return p