Module:GameSkills: Difference between revisions
From SpiritVale Wiki
More actions
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
-- Module:GameSkills | -- Module:GameSkills | ||
-- | |||
-- Renders active skill data (Data:skills.json) into an infobox-style table, | |||
-- and can also list all skills for a given user/class (page name). | |||
-- | -- | ||
-- Upgrades: | -- Upgrades: | ||
-- - | -- - Per-skill Level Select slider (client-side JS updates .sv-dyn spans) | ||
-- - Default level = Max Level | -- - Default level = Max Level | ||
-- - | -- - .sv-skill-card + data-max-level / data-level hooks for JS | ||
-- - | -- - Uses dynamic spans instead of long Lv1/Lv2/... lists | ||
-- - | -- - Unified top band: Level Select (left) + Type/Element/Target/Cast (right) | ||
-- - List-mode: wraps all skills in one wrapper panel + stable .sv-skill-item divs | |||
-- - | |||
-- | -- | ||
-- Requires the JS you installed in MediaWiki:Common.js. | -- Requires the JS you installed in MediaWiki:Common.js. | ||
| Line 40: | Line 41: | ||
end | end | ||
return frame.args | return frame.args | ||
end | |||
local function trim(s) | |||
if type(s) ~= "string" then | |||
return nil | |||
end | |||
s = mw.text.trim(s) | |||
if s == "" then | |||
return nil | |||
end | |||
return s | |||
end | end | ||
| Line 56: | Line 68: | ||
row:tag("th"):wikitext(label):done() | row:tag("th"):wikitext(label):done() | ||
row:tag("td"):wikitext(value):done() | row:tag("td"):wikitext(value):done() | ||
end | end | ||
| Line 119: | Line 112: | ||
-- Handles either a scalar OR { Value = ..., Unit = ... } | -- Handles either a scalar OR { Value = ..., Unit = ... } | ||
-- IMPORTANT: | -- IMPORTANT: wikiprep often converts unit-wrapped pairs into strings already. | ||
local function formatUnitValue(v) | local function formatUnitValue(v) | ||
if type(v) == "table" and v.Value ~= nil then | if type(v) == "table" and v.Value ~= nil then | ||
| Line 198: | Line 191: | ||
end | end | ||
-- | -- Base/Per Level renderer: | ||
-- - | -- - Per Level list -> dynamic span (or single value if flat) | ||
-- - | -- - Per Level scalar -> "Base" + "Per Level" lines (short) | ||
local function valuePairDynamicLines(name, block, maxLevel, level) | local function valuePairDynamicLines(name, block, maxLevel, level) | ||
if type(block) ~= "table" then | if type(block) ~= "table" then | ||
| Line 209: | Line 202: | ||
local per = block["Per Level"] | local per = block["Per Level"] | ||
-- Per Level list (expanded) | -- Per Level list (expanded by wikiprep) | ||
if type(per) == "table" then | if type(per) == "table" then | ||
-- empty list -> show Base only | -- empty list -> show Base only | ||
| Line 244: | Line 237: | ||
end | end | ||
-- scalar Per Level ( | -- scalar Per Level (old style) | ||
local lines = {} | local lines = {} | ||
local baseText = formatUnitValue(base) | local baseText = formatUnitValue(base) | ||
| Line 308: | Line 301: | ||
-- Formatting helpers | -- Formatting helpers | ||
---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||
-- Forward declarations (avoid nil upvalue issues) | |||
local basisLabel | |||
local valuePairRawText | |||
local function valuePairDynamicValueOnly(block, maxLevel, level) | local function valuePairDynamicValueOnly(block, maxLevel, level) | ||
| Line 341: | Line 338: | ||
end | end | ||
-- Source (new unified Damage/Flat/Reflect/Healing) formatter | |||
local function formatSource(src, maxLevel, level) | local function formatSource(src, maxLevel, level) | ||
if type(src) ~= "table" then | if type(src) ~= "table" then | ||
| Line 351: | Line 349: | ||
local basis = basisLabel(src, isHealing) | local basis = basisLabel(src, isHealing) | ||
if basis and mw.ustring.lower(tostring(basis)) == mw.ustring.lower(tostring(kind)) then | if basis and mw.ustring.lower(tostring(basis)) == mw.ustring.lower(tostring(kind)) then | ||
basis = nil | basis = nil | ||
end | end | ||
| Line 367: | Line 365: | ||
end | end | ||
basisLabel = function(entry, isHealing) | |||
if isHealing then | if isHealing then | ||
return "Healing" | return "Healing" | ||
| Line 386: | Line 384: | ||
end | end | ||
-- | -- BACKCOMPAT: old damage list formatter (safe to remove once all JSON is migrated) | ||
local function formatDamageEntry(entry, maxLevel, level) | local function formatDamageEntry(entry, maxLevel, level) | ||
if type(entry) ~= "table" then | if type(entry) ~= "table" then | ||
| Line 394: | Line 391: | ||
local isHealing = (entry.Type == "Healing") | local isHealing = (entry.Type == "Healing") | ||
local basis = isHealing and "Healing" or basisLabel(entry) | local basis = isHealing and "Healing" or basisLabel(entry, false) | ||
local baseRaw = entry["Base %"] | local baseRaw = entry["Base %"] | ||
| Line 470: | Line 467: | ||
end | end | ||
-- Scaling supports either: | |||
-- - single dict: {Percent=..., Scaling ID/Name...} | |||
-- - list of dicts: [{...},{...}] | |||
local function formatScaling(scaling, basisOverride) | local function formatScaling(scaling, basisOverride) | ||
if type(scaling) ~= "table" then | if type(scaling) ~= "table" then | ||
return nil | return nil | ||
| Line 478: | Line 477: | ||
local list = scaling | local list = scaling | ||
if #list == 0 then | if #list == 0 then | ||
if scaling.Percent ~= nil or scaling["Scaling ID"] or scaling["Scaling Name"] then | if scaling.Percent ~= nil or scaling["Scaling ID"] or scaling["Scaling Name"] then | ||
list = { scaling } | list = { scaling } | ||
| Line 551: | Line 549: | ||
add("Cast Time", "Cast Time") | add("Cast Time", "Cast Time") | ||
add("Cooldown", "Cooldown") | add("Cooldown", "Cooldown") | ||
add("Duration", "Duration") | add("Duration", "Duration") | ||
if bt["Effect Cast Time"] ~= nil then | if bt["Effect Cast Time"] ~= nil then | ||
| Line 622: | Line 620: | ||
end | end | ||
valuePairRawText = function(block) | |||
if type(block) ~= "table" then | if type(block) ~= "table" then | ||
return nil | return nil | ||
| Line 685: | Line 683: | ||
end | end | ||
local txt = valuePairRawText(block) | |||
local txt = valuePairRawText( | |||
return txt and mw.text.nowiki(txt) or nil | return txt and mw.text.nowiki(txt) or nil | ||
end | end | ||
| Line 760: | Line 757: | ||
for _, s in ipairs(list) do | for _, s in ipairs(list) do | ||
if type(s) == "table" then | if type(s) == "table" then | ||
-- Scope renamed to Type; keep fallback for older JSON | |||
local typ = s.Type or s.Scope or "Target" | local typ = s.Type or s.Scope or "Target" | ||
local name = s["Status External Name"] or s["Status Internal Name"] or "Unknown status" | local name = s["Status External Name"] or s["Status Internal Name"] or "Unknown status" | ||
| Line 811: | Line 809: | ||
end | end | ||
local amt | local amt | ||
if type(r["Per Level"]) == "table" and #r["Per Level"] > 0 and not isFlatList(r["Per Level"]) then | if type(r["Per Level"]) == "table" and #r["Per Level"] > 0 and not isFlatList(r["Per Level"]) then | ||
local series = {} | local series = {} | ||
| Line 932: | Line 930: | ||
local function buildLevelSelectUI(level, maxLevel) | local function buildLevelSelectUI(level, maxLevel) | ||
local wrap = mw.html.create("div") | local wrap = mw.html.create("div") | ||
wrap:addClass("sv-level-ui") | wrap:addClass("sv-level-ui") | ||
| Line 987: | Line 982: | ||
end | end | ||
-- NEW keys (with fallback to old keys | -- NEW keys (with fallback to old keys) | ||
addChunk("Damage", typeBlock.Damage or typeBlock["Damage Type"]) | addChunk("Damage", typeBlock.Damage or typeBlock["Damage Type"]) | ||
addChunk("Element", typeBlock.Element or typeBlock["Element Type"]) | addChunk("Element", typeBlock.Element or typeBlock["Element Type"]) | ||
| Line 1,010: | Line 1,005: | ||
cell:addClass("sv-topband-cell") | cell:addClass("sv-topband-cell") | ||
local inner = cell:tag("table") | local inner = cell:tag("table") | ||
inner:addClass("sv-topband-table") | inner:addClass("sv-topband-table") | ||
| Line 1,039: | Line 1,033: | ||
local root = mw.html.create("table") | local root = mw.html.create("table") | ||
root:addClass("spiritvale-skill-infobox") | root:addClass("spiritvale-skill-infobox") | ||
| Line 1,049: | Line 1,040: | ||
root:attr("data-level", tostring(level)) | root:attr("data-level", tostring(level)) | ||
if opts.inList then | if opts.inList then | ||
root:addClass("sv-skill-inlist") | root:addClass("sv-skill-inlist") | ||
end | end | ||
-- | -- Hero rows | ||
local icon = rec.Icon | local icon = rec.Icon | ||
local title = rec["External Name"] or rec.Name or rec["Internal Name"] or "Unknown Skill" | local title = rec["External Name"] or rec.Name or rec["Internal Name"] or "Unknown Skill" | ||
local desc = rec.Description or "" | local desc = rec.Description or "" | ||
local heroRow = root:tag("tr") | local heroRow = root:tag("tr") | ||
heroRow:addClass("spiritvale-infobox-main") | heroRow:addClass("spiritvale-infobox-main") | ||
| Line 1,071: | Line 1,058: | ||
local heroInner = heroCell:tag("div") | local heroInner = heroCell:tag("div") | ||
heroInner:addClass("spiritvale-infobox-main-left-inner") | heroInner:addClass("spiritvale-infobox-main-left-inner") | ||
if icon and icon ~= "" then | if icon and icon ~= "" then | ||
| Line 1,081: | Line 1,068: | ||
:wikitext(title) | :wikitext(title) | ||
if desc ~= "" then | if desc ~= "" then | ||
local descRow = root:tag("tr") | local descRow = root:tag("tr") | ||
| Line 1,155: | Line 1,141: | ||
end | end | ||
addRow(root, "Timing", | addRow(root, "Timing", formatTimingBlock(mech["Basic Timings"], maxLevel, level)) | ||
addRow(root, "Resource Cost", | addRow(root, "Resource Cost", formatResourceCost(mech["Resource Cost"], maxLevel, level)) | ||
addRow(root, "Combo", | addRow(root, "Combo", formatCombo(mech.Combo)) | ||
addRow(root, "Special Mechanics", formatMechanicEffects(mech.Effects, maxLevel, level)) | addRow(root, "Special Mechanics", formatMechanicEffects(mech.Effects, maxLevel, level)) | ||
end | end | ||
------------------------------------------------------------------ | ------------------------------------------------------------------ | ||
-- Source (new | -- Source (new) + Scaling, with backcompat for old Damage | ||
------------------------------------------------------------------ | ------------------------------------------------------------------ | ||
if type(rec.Source) == "table" then | if type(rec.Source) == "table" then | ||
addRow(root, "Source", formatSource(rec.Source, maxLevel, level)) | |||
local basisOverride = | |||
(rec.Source.Type == "Healing" or rec.Source.Healing == true) and "Healing" or nil | |||
else | addRow(root, "Scaling", formatScaling(rec.Source.Scaling, basisOverride)) | ||
else | |||
-- BACKCOMPAT: old Damage block (safe to delete once migrated) | |||
local dmg = rec.Damage or {} | |||
if next(dmg) ~= nil then | |||
local main = dmg["Main Damage"] | |||
local mainNonHeal, healOnly = {}, {} | |||
if type(main) == "table" then | |||
for _, d in ipairs(main) do | |||
if type(d) == "table" and d.Type == "Healing" then | |||
table.insert(healOnly, d) | |||
else | |||
table.insert(mainNonHeal, d) | |||
end | |||
end | end | ||
end | end | ||
local flatList = dmg["Flat Damage"] | |||
local reflList = dmg["Reflect Damage"] | |||
local flatHas = (type(flatList) == "table" and #flatList > 0) | |||
local reflHas = (type(reflList) == "table" and #reflList > 0) | |||
local pureHealing = | |||
(#healOnly > 0) and (#mainNonHeal == 0) and (not flatHas) and (not reflHas) | |||
addRow(root, "Main Damage", formatDamageList(mainNonHeal, maxLevel, level, (#mainNonHeal > 1))) | |||
addRow(root, "Flat Damage", formatDamageList(flatList, maxLevel, level, false)) | |||
addRow(root, "Reflect Damage", formatDamageList(reflList, maxLevel, level, false)) | |||
addRow(root, "Healing", formatDamageList(healOnly, maxLevel, level, false)) | |||
addRow(root, "Scaling", formatScaling(dmg.Scaling, pureHealing and "Healing" or nil)) | |||
end | |||
end | end | ||
------------------------------------------------------------------ | ------------------------------------------------------------------ | ||
| Line 1,272: | Line 1,260: | ||
end | end | ||
local root = mw.html.create("div") | local root = mw.html.create("div") | ||
root:addClass("sv-skill-collection") | root:addClass("sv-skill-collection") | ||
for _, rec in ipairs(matches) do | for _, rec in ipairs(matches) do | ||
local item = root:tag("div"):addClass("sv-skill-item") | local item = root:tag("div"):addClass("sv-skill-item") | ||
item:wikitext(buildInfobox(rec, { showUsers = false, inList = true })) | item:wikitext(buildInfobox(rec, { showUsers = false, inList = true })) | ||