Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Join the Playtest on Steam Now: SpiritVale

Module:GameSkills: Difference between revisions

From SpiritVale Wiki
No edit summary
No edit summary
Line 1: Line 1:
-- Module:GameSkills
-- Module:GameSkills
--
--
-- Renders active skill data (from Data:skills.json) into an infobox-style table
-- Upgrades:
-- and can also list all skills for a given user/class.
--  - Adds a per-skill Level Select slider (client-side JS updates fields)
--  - Default level = Max Level
-- - Adds .sv-skill-card + data-max-level/data-level hooks for JS
--  - Replaces large Lv1/Lv2/... lists with data-series dynamic spans
--
--
-- Usage (single skill):
-- Requires the JS you installed in MediaWiki:Common.js.
--  {{Skill|Heal}}
--  {{Skill|name=Heal}}
--  {{Skill|id=Heal_InternalId}}
--
-- Usage (auto-list on class page, e.g. "Acolyte"):
--  {{Skill}}                  -> lists all Acolyte skills (page name)
--  {{Skill|Acolyte}}          -> same, if no skill literally called "Acolyte"


local GameData = require("Module:GameData")
local GameData = require("Module:GameData")
Line 72: Line 68:
if type(v) == "number" then return v end
if type(v) == "number" then return v end
if type(v) == "string" then return tonumber(v) end
if type(v) == "string" then return tonumber(v) end
if type(v) == "table" and v.Value ~= nil then
return toNum(v.Value)
end
return nil
return nil
end
local function clamp(n, lo, hi)
if type(n) ~= "number" then return lo end
if n < lo then return lo end
if n > hi then return hi end
return n
end
end


Line 117: Line 123:


----------------------------------------------------------------------
----------------------------------------------------------------------
-- Lookups
-- Dynamic field helpers (JS-driven)
----------------------------------------------------------------------
----------------------------------------------------------------------


-- Lookup by Internal Name
local function dynSpan(series, level)
local function getSkillById(id)
if type(series) ~= "table" or #series == 0 then
id = trim(id)
return nil
if not id then return nil end
end
local dataset = getSkills()
level = clamp(level or #series, 1, #series)
local byId = dataset.byId or {}
return byId[id]
end
 
-- Lookup by display/external Name (for editors)
local function findSkillByName(name)
name = trim(name)
if not name then return nil end
local dataset = getSkills()


-- Fast path if GameData built byName
local span = mw.html.create("span")
local byName = dataset.byName or {}
span:addClass("sv-dyn")
if byName[name] then
span:attr("data-series", mw.text.jsonEncode(series))
return byName[name]
span:wikitext(mw.text.nowiki(series[level] or ""))
end


-- Fallback scan (older GameData)
return tostring(span)
for _, rec in ipairs(dataset.records or {}) do
if type(rec) == "table" then
if rec["External Name"] == name or rec["Name"] == name or rec["Display Name"] == name then
return rec
end
end
end
return nil
end
end
----------------------------------------------------------------------
-- Formatting helpers
----------------------------------------------------------------------


local function isFlatList(list)
local function isFlatList(list)
Line 185: Line 169:
end
end


-- Turns a {Base, Per Level} block into lines:
-- Like your old valuePairLines/valuePairText, but:
--   - If Per Level is a list: "Name: v1 / v2 / v3 ..."
-- - if Per Level is a list, we render a dynamic span instead of "v1 / v2 / ...".
--   - If Per Level scalar and non-zero: add "Name Per Level: X"
-- - scalar Per Level stays as the old "Base" and "Per Level" lines (still short).
local function valuePairLines(name, block)
local function valuePairDynamicLines(name, block, maxLevel, level)
if type(block) ~= "table" then
if type(block) ~= "table" then
return {}
return {}
Line 196: Line 180:
local per  = block["Per Level"]
local per  = block["Per Level"]


-- Per Level list (wikiprep expansion)
-- Per Level list (expanded)
if type(per) == "table" then
if type(per) == "table" then
-- empty list -> show Base only
if #per == 0 then
if #per == 0 then
local baseText = formatUnitValue(base)
local baseText = formatUnitValue(base)
if baseText then
if baseText then
return { string.format("%s: %s", name, baseText) }
return { string.format("%s: %s", name, mw.text.nowiki(baseText)) }
end
end
return {}
return {}
end
end


-- If it's a flat list, treat as "no scaling" (show single value)
-- Flat list -> show single value
if isFlatList(per) then
if isFlatList(per) then
local baseText = formatUnitValue(base)
local baseText = formatUnitValue(base)
local one = tostring(per[1])
local one = formatUnitValue(per[1]) or tostring(per[1])
local show = baseText or one
local show = baseText or one
if show then
if show then
return { string.format("%s: %s", name, show) }
return { string.format("%s: %s", name, mw.text.nowiki(show)) }
end
end
return {}
return {}
end
end


local vals = {}
-- Dynamic series
local series = {}
for _, v in ipairs(per) do
for _, v in ipairs(per) do
table.insert(vals, formatUnitValue(v) or tostring(v))
table.insert(series, formatUnitValue(v) or tostring(v))
end
end
if #vals == 0 then
 
return {}
local dyn = dynSpan(series, level)
if dyn then
return { string.format("%s: %s", name, dyn) }
end
end
return { string.format("%s: %s", name, table.concat(vals, " / ")) }
return {}
end
end


-- Scalar Per Level
-- Scalar Per Level (keep old style)
local lines = {}
local lines = {}
local baseText = formatUnitValue(base)
local baseText = formatUnitValue(base)
Line 233: Line 221:


if baseText then
if baseText then
table.insert(lines, string.format("%s: %s", name, baseText))
table.insert(lines, string.format("%s: %s", name, mw.text.nowiki(baseText)))
end
end
if perText and isNonZeroScalar(per) then
if perText and isNonZeroScalar(per) then
table.insert(lines, string.format("%s Per Level: %s", name, perText))
table.insert(lines, string.format("%s Per Level: %s", name, mw.text.nowiki(perText)))
end
end


Line 242: Line 230:
end
end


local function valuePairText(name, block, sep)
local function valuePairDynamicText(name, block, maxLevel, level, sep)
local lines = valuePairLines(name, block)
local lines = valuePairDynamicLines(name, block, maxLevel, level)
if #lines == 0 then
if #lines == 0 then return nil end
return nil
end
return table.concat(lines, sep or "<br />")
return table.concat(lines, sep or "<br />")
end
end


local function valuePairRawText(block)
----------------------------------------------------------------------
if type(block) ~= "table" then
-- Lookups
return nil
----------------------------------------------------------------------
end
 
local function getSkillById(id)
id = trim(id)
if not id then return nil end
local dataset = getSkills()
local byId = dataset.byId or {}
return byId[id]
end


local base = block.Base
local function findSkillByName(name)
local per  = block["Per Level"]
name = trim(name)
if not name then return nil end
local dataset = getSkills()


if type(per) == "table" then
local byName = dataset.byName or {}
if #per == 0 then
if byName[name] then
return formatUnitValue(base)
return byName[name]
end
if isFlatList(per) then
return formatUnitValue(base) or tostring(per[1])
end
local vals = {}
for _, v in ipairs(per) do
table.insert(vals, formatUnitValue(v) or tostring(v))
end
return (#vals > 0) and table.concat(vals, " / ") or nil
end
end


local baseText = formatUnitValue(base)
for _, rec in ipairs(dataset.records or {}) do
local perText  = formatUnitValue(per)
if type(rec) == "table" then
 
if rec["External Name"] == name or rec["Name"] == name or rec["Display Name"] == name then
if baseText and perText and isNonZeroScalar(per) then
return rec
return string.format("%s (Per Level: %s)", baseText, perText)
end
end
end
end
return baseText or perText
return nil
end
end
----------------------------------------------------------------------
-- Formatting helpers
----------------------------------------------------------------------


local function basisLabel(entry, isHealing)
local function basisLabel(entry, isHealing)
Line 297: Line 288:
end
end


-- Damage formatting:
-- Dynamic damage entry:
--   - Avoid "nil%" when Base is missing/0 and only Per Level exists.
-- Build a series for Lv1..LvMax, and show only the selected one.
--  - For non-healing: "100% + 40 / 80 / 120 ... Attack Per Level"
local function formatDamageEntry(entry, maxLevel, level)
--  - For healing: "10 / 20 / 30 ... Healing Per Level"
local function formatDamageEntry(entry, maxLevel)
if type(entry) ~= "table" then
if type(entry) ~= "table" then
return nil
return nil
Line 307: Line 296:


local isHealing = (entry.Type == "Healing")
local isHealing = (entry.Type == "Healing")
local basis = isHealing and "Healing" or basisLabel(entry)


local baseRaw = entry["Base %"]
local baseRaw = entry["Base %"]
Line 313: Line 303:
local baseN = toNum(baseRaw)
local baseN = toNum(baseRaw)
local perN  = toNum(perRaw)
local perN  = toNum(perRaw)
local basis = isHealing and "Healing" or basisLabel(entry)


local function baseIsPresent()
local function baseIsPresent()
Line 336: Line 324:
end
end


-- No scaling -> just show base
if perN == nil or perN == 0 or not maxLevel or maxLevel <= 0 then
if perN == nil or perN == 0 or not maxLevel or maxLevel <= 0 then
return baseText and (baseText .. " " .. basis) or nil
return baseText and (mw.text.nowiki(baseText .. " " .. basis)) or nil
end
end


local vals = {}
-- Build series strings for each level
local series = {}
for lv = 1, maxLevel do
for lv = 1, maxLevel do
table.insert(vals, fmtNum(perN * lv) .. "%")
local perPart = perN * lv
 
if baseText and baseN ~= nil then
-- numeric base: show total
local total = baseN + perPart
table.insert(series, string.format("%s%% %s", fmtNum(total), basis))
elseif baseText then
-- nonnumeric base: can't total, show additive
table.insert(series, string.format("%s + %s%% %s", baseText, fmtNum(perPart), basis))
else
-- no base: show perPart only
table.insert(series, string.format("%s%% %s", fmtNum(perPart), basis))
end
end
end
local listText = table.concat(vals, " / ")


if not baseText then
return dynSpan(series, level)
return string.format("%s %s Per Level", listText, basis)
end
return string.format("%s + %s %s Per Level", baseText, listText, basis)
end
end


local function formatDamageList(list, maxLevel, includeTypePrefix, isHealing)
local function formatDamageList(list, maxLevel, level, includeTypePrefix)
if type(list) ~= "table" or #list == 0 then
if type(list) ~= "table" or #list == 0 then
return nil
return nil
Line 360: Line 358:
for _, d in ipairs(list) do
for _, d in ipairs(list) do
if type(d) == "table" then
if type(d) == "table" then
local txt = formatDamageEntry(d, maxLevel, isHealing)
local txt = formatDamageEntry(d, maxLevel, level)
if txt then
if txt then
if includeTypePrefix and d.Type and d.Type ~= "" then
if includeTypePrefix and d.Type and d.Type ~= "" then
table.insert(parts, tostring(d.Type) .. ": " .. txt)
table.insert(parts, mw.text.nowiki(tostring(d.Type) .. ": ") .. txt)
else
else
table.insert(parts, txt)
table.insert(parts, txt)
Line 377: Line 375:
end
end


-- Scaling should read like:
--  "2% Attack Per Vitality"
-- For pure healing skills:
--  "1% Healing Per Vitality"
local function formatScaling(list, basisOverride)
local function formatScaling(list, basisOverride)
if type(list) ~= "table" or #list == 0 then
if type(list) ~= "table" or #list == 0 then
Line 409: Line 403:
end
end


-- Area: Distance then Size (no Effective)
local function formatArea(area, maxLevel, level)
local function formatArea(area)
if type(area) ~= "table" then
if type(area) ~= "table" then
return nil
return nil
Line 416: Line 409:
local parts = {}
local parts = {}


local distLine = valuePairText("Distance", area["Area Distance"], "<br />")
local distLine = valuePairDynamicText("Distance", area["Area Distance"], maxLevel, level, "<br />")
if distLine then
if distLine then
table.insert(parts, distLine)
table.insert(parts, distLine)
Line 423: Line 416:
local size = area["Area Size"]
local size = area["Area Size"]
if size and size ~= "" then
if size and size ~= "" then
table.insert(parts, "Size: " .. tostring(size))
table.insert(parts, "Size: " .. mw.text.nowiki(tostring(size)))
end
end


Line 432: Line 425:
end
end


local function formatTimingBlock(bt)
local function formatTimingBlock(bt, maxLevel, level)
if type(bt) ~= "table" then
if type(bt) ~= "table" then
return nil
return nil
Line 443: Line 436:
return
return
end
end
local lines = valuePairLines(label, block)
local lines = valuePairDynamicLines(label, block, maxLevel, level)
for _, line in ipairs(lines) do
for _, line in ipairs(lines) do
table.insert(parts, line)
table.insert(parts, line)
Line 454: Line 447:


if bt["Effect Cast Time"] ~= nil then
if bt["Effect Cast Time"] ~= nil then
table.insert(parts, "Effect Cast Time: " .. tostring(bt["Effect Cast Time"]))
table.insert(parts, "Effect Cast Time: " .. mw.text.nowiki(tostring(bt["Effect Cast Time"])))
end
end
if bt["Damage Delay"] ~= nil then
if bt["Damage Delay"] ~= nil then
table.insert(parts, "Damage Delay: " .. tostring(bt["Damage Delay"]))
table.insert(parts, "Damage Delay: " .. mw.text.nowiki(tostring(bt["Damage Delay"])))
end
end
if bt["Effect Remove Delay"] ~= nil then
if bt["Effect Remove Delay"] ~= nil then
table.insert(parts, "Effect Remove Delay: " .. tostring(bt["Effect Remove Delay"]))
table.insert(parts, "Effect Remove Delay: " .. mw.text.nowiki(tostring(bt["Effect Remove Delay"])))
end
end


Line 469: Line 462:
end
end


local function formatResourceCost(rc)
local function formatResourceCost(rc, maxLevel, level)
if type(rc) ~= "table" then
if type(rc) ~= "table" then
return nil
return nil
Line 475: Line 468:
local parts = {}
local parts = {}


local manaLines = valuePairLines("MP", rc["Mana Cost"])
local manaLines = valuePairDynamicLines("MP", rc["Mana Cost"], maxLevel, level)
for _, line in ipairs(manaLines) do
for _, line in ipairs(manaLines) do
table.insert(parts, line)
table.insert(parts, line)
end
end


local hpLines = valuePairLines("HP", rc["Health Cost"])
local hpLines = valuePairDynamicLines("HP", rc["Health Cost"], maxLevel, level)
for _, line in ipairs(hpLines) do
for _, line in ipairs(hpLines) do
table.insert(parts, line)
table.insert(parts, line)
Line 498: Line 491:


if combo.Type then
if combo.Type then
table.insert(parts, "Type: " .. tostring(combo.Type))
table.insert(parts, "Type: " .. mw.text.nowiki(tostring(combo.Type)))
end
end


local durText = formatUnitValue(combo.Duration)
local durText = formatUnitValue(combo.Duration)
if durText then
if durText then
table.insert(parts, "Duration: " .. durText)
table.insert(parts, "Duration: " .. mw.text.nowiki(durText))
end
end


Line 509: Line 502:
local pctText = formatUnitValue(combo.Percent)
local pctText = formatUnitValue(combo.Percent)
if pctText then
if pctText then
table.insert(parts, "Bonus: " .. pctText)
table.insert(parts, "Bonus: " .. mw.text.nowiki(pctText))
end
end
end
end
Line 519: Line 512:
end
end


-- Special Mechanics:
local function valuePairRawText(block)
-- If effect block has Type, render like:
if type(block) ~= "table" then
--  "Holy Light - Skill Area + 3"
return nil
end
 
local base = block.Base
local per  = block["Per Level"]
 
if type(per) == "table" then
if #per == 0 then
return formatUnitValue(base)
end
if isFlatList(per) then
return formatUnitValue(base) or tostring(per[1])
end
local vals = {}
for _, v in ipairs(per) do
table.insert(vals, formatUnitValue(v) or tostring(v))
end
return (#vals > 0) and table.concat(vals, " / ") or nil
end
 
local baseText = formatUnitValue(base)
local perText  = formatUnitValue(per)
 
if baseText and perText and isNonZeroScalar(per) then
return string.format("%s (Per Level: %s)", baseText, perText)
end
return baseText or perText
end
 
local function formatMechanicEffects(effects)
local function formatMechanicEffects(effects)
if type(effects) ~= "table" then
if type(effects) ~= "table" then
Line 547: Line 568:
end
end
else
else
local txt = valuePairText(name, block, ", ")
-- Keep old behavior here (usually not huge)
local txt = valuePairRawText(block)
if txt then
if txt then
table.insert(parts, txt)
table.insert(parts, mw.text.nowiki(tostring(name) .. ": " .. txt))
end
end
end
end
Line 593: Line 615:
end
end


local function formatStatusApplications(list)
local function formatStatusApplications(list, maxLevel, level)
if type(list) ~= "table" or #list == 0 then
if type(list) ~= "table" or #list == 0 then
return nil
return nil
Line 607: Line 629:


if type(s.Duration) == "table" then
if type(s.Duration) == "table" then
local t = valuePairText("Duration", s.Duration, "; ")
local t = valuePairDynamicText("Duration", s.Duration, maxLevel, level, "; ")
if t then table.insert(detail, t) end
if t then table.insert(detail, t) end
end
end


if type(s.Chance) == "table" then
if type(s.Chance) == "table" then
local t = valuePairText("Chance", s.Chance, "; ")
local t = valuePairDynamicText("Chance", s.Chance, maxLevel, level, "; ")
if t then table.insert(detail, t) end
if t then table.insert(detail, t) end
end
end
Line 633: Line 655:
end
end


local function formatStatusRemoval(list)
local function formatStatusRemoval(list, maxLevel, level)
if type(list) ~= "table" or #list == 0 then
if type(list) ~= "table" or #list == 0 then
return nil
return nil
Line 650: Line 672:
end
end


local amt = valuePairRawText(r)
local amt = nil
local seg = label
-- If this removal has a Per Level list, make it dynamic
if type(r["Per Level"]) == "table" and #r["Per Level"] > 0 and not isFlatList(r["Per Level"]) then
local series = {}
for _, v in ipairs(r["Per Level"]) do
table.insert(series, formatUnitValue(v) or tostring(v))
end
amt = dynSpan(series, level)
else
amt = valuePairRawText(r)
if amt then amt = mw.text.nowiki(amt) end
end
 
local seg = mw.text.nowiki(label)
if amt then
if amt then
seg = seg .. " – " .. amt
seg = seg .. " – " .. amt
Line 750: Line 784:
-- Infobox builder
-- Infobox builder
----------------------------------------------------------------------
----------------------------------------------------------------------
local function buildLevelSelectUI(level, maxLevel)
-- Matches the JS expectations:
--  .sv-level-slider placeholder for the <input type="range">
--  .sv-level-num span for updating the number
local wrap = mw.html.create("div")
wrap:addClass("sv-level-ui")
local label = wrap:tag("div"):addClass("sv-level-label")
label:wikitext("Level <span class=\"sv-level-num\">" .. tostring(level) .. "</span> / " .. tostring(maxLevel))
wrap:tag("div"):addClass("sv-level-slider")
return tostring(wrap)
end


local function buildInfobox(rec, opts)
local function buildInfobox(rec, opts)
opts = opts or {}
opts = opts or {}
local showUsers = (opts.showUsers ~= false)
local showUsers = (opts.showUsers ~= false)
local maxLevel = tonumber(rec["Max Level"]) or 1
if maxLevel < 1 then maxLevel = 1 end
-- Always default to max level (your requirement)
local level = maxLevel
level = clamp(level, 1, maxLevel)


local root = mw.html.create("table")
local root = mw.html.create("table")
root:addClass("wikitable spiritvale-skill-infobox")
root:addClass("wikitable spiritvale-skill-infobox")
-- JS hook: treat the table itself as the "card"
root:addClass("sv-skill-card")
root:attr("data-max-level", tostring(maxLevel))
root:attr("data-level", tostring(level))


-- ==========================================================
-- ==========================================================
Line 798: Line 858:
------------------------------------------------------------------
------------------------------------------------------------------
addSectionHeader(root, "General")
addSectionHeader(root, "General")
addRow(root, "Max Level", rec["Max Level"] and tostring(rec["Max Level"]))
 
-- Replace Max Level -> Level Select (slider)
addRow(root, "Level Select", buildLevelSelectUI(level, maxLevel))


-- Hide Users on:
-- Hide Users on:
Line 824: Line 886:
local skillParts = {}
local skillParts = {}
for _, rs in ipairs(req["Required Skills"]) do
for _, rs in ipairs(req["Required Skills"]) do
local name = rs["Skill External Name"] or rs["Skill Internal Name"] or "Unknown"
local nameReq = rs["Skill External Name"] or rs["Skill Internal Name"] or "Unknown"
local level = rs["Required Level"]
local lvlReq = rs["Required Level"]
if level then
if lvlReq then
table.insert(skillParts, string.format("%s (Lv.%s)", name, level))
table.insert(skillParts, string.format("%s (Lv.%s)", nameReq, lvlReq))
else
else
table.insert(skillParts, name)
table.insert(skillParts, nameReq)
end
end
end
end
Line 877: Line 939:
addRow(root, "Range", rangeText)
addRow(root, "Range", rangeText)


local areaText = formatArea(mech.Area)
local areaText = formatArea(mech.Area, maxLevel, level)
addRow(root, "Area", areaText)
addRow(root, "Area", areaText)


Line 884: Line 946:
end
end


local btText = formatTimingBlock(mech["Basic Timings"])
local btText = formatTimingBlock(mech["Basic Timings"], maxLevel, level)
addRow(root, "Timing", btText)
addRow(root, "Timing", btText)


local rcText = formatResourceCost(mech["Resource Cost"])
local rcText = formatResourceCost(mech["Resource Cost"], maxLevel, level)
addRow(root, "Resource Cost", rcText)
addRow(root, "Resource Cost", rcText)


Line 903: Line 965:
if next(dmg) ~= nil then
if next(dmg) ~= nil then
addSectionHeader(root, "Damage and Scaling")
addSectionHeader(root, "Damage and Scaling")
local maxLevel = tonumber(rec["Max Level"]) or 0


-- Split healing out of Main Damage (Type == "Healing")
-- Split healing out of Main Damage (Type == "Healing")
Line 930: Line 990:
local pureHealing = (#healOnly > 0) and (#mainNonHeal == 0) and (not flatHas) and (not reflHas)
local pureHealing = (#healOnly > 0) and (#mainNonHeal == 0) and (not flatHas) and (not reflHas)


local mainText = formatDamageList(mainNonHeal, maxLevel, (#mainNonHeal > 1), false)
local mainText = formatDamageList(mainNonHeal, maxLevel, level, (#mainNonHeal > 1))
addRow(root, "Main Damage", mainText)
addRow(root, "Main Damage", mainText)


local flatText = formatDamageList(flatList, maxLevel, false, false)
local flatText = formatDamageList(flatList, maxLevel, level, false)
addRow(root, "Flat Damage", flatText)
addRow(root, "Flat Damage", flatText)


local reflText = formatDamageList(reflList, maxLevel, false, false)
local reflText = formatDamageList(reflList, maxLevel, level, false)
addRow(root, "Reflect Damage", reflText)
addRow(root, "Reflect Damage", reflText)


local healText = formatDamageList(healOnly, maxLevel, false, true)
local healText = formatDamageList(healOnly, maxLevel, level, false)
addRow(root, "Healing", healText)
addRow(root, "Healing", healText)


Line 958: Line 1,018:
-- Status
-- Status
------------------------------------------------------------------
------------------------------------------------------------------
local statusApps = formatStatusApplications(rec["Status Applications"])
local statusApps = formatStatusApplications(rec["Status Applications"], maxLevel, level)
local statusRem  = formatStatusRemoval(rec["Status Removal"])
local statusRem  = formatStatusRemoval(rec["Status Removal"], maxLevel, level)
if statusApps or statusRem then
if statusApps or statusRem then
addSectionHeader(root, "Status Effects")
addSectionHeader(root, "Status Effects")
Line 993: Line 1,053:
local args = getArgs(frame)
local args = getArgs(frame)


-- Prefer explicit param, then unnamed, then fall back to the current page name.
local userName = args.user or args[1]
local userName = args.user or args[1]
if not userName or userName == "" then
if not userName or userName == "" then
Line 1,022: Line 1,081:
root:addClass("spiritvale-skill-list")
root:addClass("spiritvale-skill-list")


-- On class pages / list mode, hide Users (redundant on that page).
for _, rec in ipairs(matches) do
for _, rec in ipairs(matches) do
root:wikitext(buildInfobox(rec, { showUsers = false }))
root:wikitext(buildInfobox(rec, { showUsers = false }))
Line 1,037: Line 1,095:
local args = getArgs(frame)
local args = getArgs(frame)


-- Allow three styles:
--  {{Skill|Bash}}              -> args[1] = "Bash"  (External Name)
--  {{Skill|name=Bash}}        -> args.name = "Bash"
--  {{Skill|id=Bash_Internal}}  -> args.id = "Bash_Internal"
local raw1 = args[1]
local raw1 = args[1]
local name = args.name or raw1
local name = args.name or raw1
Line 1,047: Line 1,101:
local rec
local rec


-- 1) Prefer External/Display Name
if name and name ~= "" then
if name and name ~= "" then
rec = findSkillByName(name)
rec = findSkillByName(name)
end
end
-- 2) Fallback: internal ID
if not rec and id and id ~= "" then
if not rec and id and id ~= "" then
rec = getSkillById(id)
rec = getSkillById(id)
end
end


-- 3) If still nothing, decide if this is "list mode" or truly unknown.
if not rec then
if not rec then
local pageTitle = mw.title.getCurrentTitle()
local pageTitle = mw.title.getCurrentTitle()
Line 1,067: Line 1,117:
(not id or id == "")
(not id or id == "")


-- Case A: {{Skill}} with no parameters on a page → list for that page name.
if noExplicitArgs then
if noExplicitArgs then
return p.listForUser(frame)
return p.listForUser(frame)
end
end


-- Case B: {{Skill|Acolyte}} on the "Acolyte" page and no id → treat as list.
if name and name ~= "" and name == pageName and (not id or id == "") then
if name and name ~= "" and name == pageName and (not id or id == "") then
return p.listForUser(frame)
return p.listForUser(frame)
end
end


-- Otherwise, genuinely unknown skill.
local label = name or id or "?"
local label = name or id or "?"
return string.format(
return string.format(
Line 1,086: Line 1,133:
end
end


-- For single-skill rendering:
-- - hide Users if this is the skill's own page
-- - otherwise show Users
local showUsers = not isDirectSkillPage(rec)
local showUsers = not isDirectSkillPage(rec)
return buildInfobox(rec, { showUsers = showUsers })
return buildInfobox(rec, { showUsers = showUsers })