Module:GameSkills: Difference between revisions
From SpiritVale Wiki
More actions
Created page with "-- Module:GameSkills -- -- Renders skill data (from Data:skills.json) into a nice infobox/table. -- Data is loaded via Module:GameData. -- -- Typical usage (via Template:Skill): -- {{Skill|id=Dispell}} -- {{Skill|name=Absolution}} -- slower fallback by display name local GameData = require('Module:GameData') local p = {} ---------------------------------------------------------------------- -- Internal helpers: lookups --------------------------------------------..." |
No edit summary |
||
| Line 1: | Line 1: | ||
local GameData = require("Module:GameData") | |||
local GameData = require( | |||
local p = {} | local p = {} | ||
---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||
-- Internal helpers | -- Internal helpers | ||
---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||
local | local skillsCache | ||
local function getSkills() | |||
local function | if not skillsCache then | ||
if not | skillsCache = GameData.loadSkills() | ||
end | end | ||
return skillsCache | |||
return | |||
end | end | ||
local function getArgs(frame) | |||
local function | -- Prefer parent template args if present (usual #invoke pattern) | ||
if | local parent = frame:getParent() | ||
if parent then | |||
return parent.args | |||
local | |||
end | end | ||
return | return frame.args | ||
end | end | ||
local function listToText(list) | |||
if not list or #list == 0 then | |||
local function | |||
if | |||
return nil | return nil | ||
end | end | ||
return table.concat(list, ", ") | |||
end | |||
local function addRow(tbl, label, value) | |||
if value == nil or value == "" then | |||
return | |||
end | end | ||
local row = tbl:tag("tr") | |||
row:tag("th"):wikitext(label):done() | |||
row:tag("td"):wikitext(value):done() | |||
end | |||
local function formatBasePer(block) | |||
if | if not block or type(block) ~= "table" then | ||
return nil | |||
end | end | ||
local base = block.Base | |||
local per = block["Per Level"] | |||
if base and per then | |||
if | return string.format("%.2f (%.2f / Lv)", base, per) | ||
elseif base then | |||
return string.format("%.2f", base) | |||
elseif per then | |||
return string.format("%.2f / Lv", per) | |||
else | |||
return nil | return nil | ||
end | end | ||
end | end | ||
local function | local function formatManaCost(block) | ||
if type( | if not block or type(block) ~= "table" then | ||
return nil | return nil | ||
end | end | ||
local | local base = block.Base | ||
local per = block["Per Level"] | |||
local | |||
if base and per then | |||
if | return string.format("%.0f (%.0f / Lv)", base, per) | ||
elseif base then | |||
return string.format("%.0f", base) | |||
elseif per then | |||
return string.format("%.0f / Lv", per) | |||
else | |||
return nil | return nil | ||
end | end | ||
end | end | ||
local function | local function formatMainDamage(damageTable) | ||
if | if not damageTable or #damageTable == 0 then | ||
return nil | return nil | ||
end | end | ||
-- Current schema only has one entry, but we’ll still handle arrays. | |||
local parts = {} | local parts = {} | ||
for _, entry in ipairs(damageTable) do | |||
local base = entry["Base %"] | |||
local per = entry["Per Level %"] | |||
local txt | |||
if base and per then | |||
txt = string.format("Base %.2f, +%.2f / Lv", base, per) | |||
elseif base then | |||
txt = string.format("Base %.2f", base) | |||
elseif per then | |||
txt = string.format("+%.2f / Lv", per) | |||
end | |||
if txt then | |||
table.insert(parts, txt) | |||
end | |||
end | end | ||
| Line 138: | Line 106: | ||
return nil | return nil | ||
end | end | ||
return table.concat(parts, "; ") | |||
return table.concat(parts, " | |||
end | end | ||
local function | local function formatScaling(scalingList) | ||
if | if not scalingList or #scalingList == 0 then | ||
return nil | return nil | ||
end | end | ||
local parts = {} | local parts = {} | ||
for _, s in ipairs(scalingList) do | |||
local name = s["Scaling Name"] or s["Scaling ID"] or "Unknown" | |||
local pct = s.Percent | |||
local | if pct then | ||
table.insert(parts, string.format("%s: %.2f", name, pct)) | |||
else | |||
table.insert(parts, name) | |||
local | |||
if | |||
table.insert(parts, string.format(" | |||
table.insert(parts, | |||
end | end | ||
end | end | ||
| Line 224: | Line 128: | ||
return nil | return nil | ||
end | end | ||
return table.concat(parts, ", ") | |||
return table.concat(parts, " | |||
end | end | ||
local function | local function formatStatusApplications(list) | ||
if not list or #list == 0 then | |||
if | |||
return nil | return nil | ||
end | end | ||
local | local parts = {} | ||
for _, | for _, s in ipairs(list) do | ||
local scope = s.Scope or "Target" | |||
local name = s["Status Name"] or s["Status ID"] or "Unknown status" | |||
local dur = s.Duration and s.Duration.Base | |||
local chance = s.Chance and s.Chance.Base | |||
local seg = name | |||
if scope and scope ~= "" then | |||
end | seg = scope .. ": " .. seg | ||
end | |||
local | local detailParts = {} | ||
if dur then | |||
table.insert(detailParts, string.format("Dur %.2f", dur)) | |||
end | |||
if chance then | |||
-- Stored as 0–1; we’ll keep it raw to avoid guessing units. | |||
table.insert(detailParts, string.format("Chance %.2f", chance)) | |||
end | |||
if #detailParts > 0 then | |||
seg = seg .. " (" .. table.concat(detailParts, ", ") .. ")" | |||
if | |||
end | end | ||
table.insert(parts, seg) | |||
end | end | ||
return table.concat( | return table.concat(parts, "; ") | ||
end | end | ||
---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||
-- | -- Public: infobox | ||
---------------------------------------------------------------------- | ---------------------------------------------------------------------- | ||
function p.infobox(frame) | |||
local | local args = getArgs(frame) | ||
local id = args.id or args[1] | |||
if not id or id == "" then | |||
return "[[Category:Pages with missing skill id]]" | |||
end | |||
local dataset = getSkills() | |||
local rec = dataset.byId[id] | |||
if not rec then | |||
return "[[Category:Pages with unknown skill id|" .. mw.text.nowiki(id) .. "]]" | |||
end | end | ||
local root = mw.html.create("table") | |||
local | root:addClass("wikitable spiritvale-skill-infobox") | ||
-- | -- Header: icon + name | ||
local | local icon = rec.Icon | ||
local title = rec.Name or id | |||
local header = root:tag("tr") | |||
local | local headerCell = header:tag("th") | ||
headerCell:attr("colspan", 2) | |||
local titleText = "" | |||
local | if icon and icon ~= "" then | ||
if | titleText = string.format("[[File:%s|64px|link=]] ", icon) | ||
end | end | ||
titleText = titleText .. (title or id) | |||
headerCell:wikitext(titleText) | |||
-- | ------------------------------------------------------------------ | ||
-- Basic info | |||
------------------------------------------------------------------ | |||
addRow(root, "Description", rec.Description) | |||
addRow(root, "Max level", rec["Max Level"] and tostring(rec["Max Level"])) | |||
------------------------------------------------------------------ | |||
-- Users | |||
------------------------------------------------------------------ | |||
local users = rec.Users or {} | |||
addRow(root, "Classes", listToText(users.Classes)) | |||
addRow(root, "Summons", listToText(users.Summons)) | |||
addRow(root, "Monsters", listToText(users.Monsters)) | |||
addRow(root, "Events", listToText(users.Events)) | |||
------------------------------------------------------------------ | |||
-- Requirements | |||
------------------------------------------------------------------ | |||
local req = rec.Requirements or {} | |||
local | if req["Required Skills"] and #req["Required Skills"] > 0 then | ||
local skillParts = {} | |||
for _, rs in ipairs(req["Required Skills"]) do | |||
local name = rs["Skill Name"] or rs["Skill ID"] or "Unknown" | |||
local level = rs["Required Level"] | |||
if level then | |||
table.insert(skillParts, string.format("%s (Lv.%s)", name, level)) | |||
else | |||
table.insert(skillParts, name) | |||
end | |||
end | end | ||
addRow(root, "Required skills", table.concat(skillParts, ", ")) | |||
end | end | ||
-- | addRow(root, "Required weapons", listToText(req["Required Weapons"])) | ||
local | addRow(root, "Required stances", listToText(req["Required Stances"])) | ||
------------------------------------------------------------------ | |||
-- Type | |||
------------------------------------------------------------------ | |||
local t = rec.Type or {} | |||
local damageType = t["Damage Type"] and t["Damage Type"].Name | |||
local element = t["Element Type"] and t["Element Type"].Name | |||
local target = t["Target Type"] and t["Target Type"].Name | |||
local castType = t["Cast Type"] and t["Cast Type"].Name | |||
addRow(root, "Damage type", damageType) | |||
addRow(root, "Element", element) | |||
addRow(root, "Targeting", target) | |||
addRow(root, "Cast type", castType) | |||
------------------------------------------------------------------ | |||
-- Mechanics | |||
------------------------------------------------------------------ | |||
local mech = rec.Mechanics or {} | |||
local basicTimings = mech["Basic Timings"] or {} | |||
local resource = mech["Resource Cost"] or {} | |||
if mech.Range then | |||
addRow(root, "Range", string.format("%.2f", mech.Range)) | |||
end | |||
addRow(root, "Cast time", formatBasePer(basicTimings["Cast Time"])) | |||
addRow(root, "Cooldown", formatBasePer(basicTimings["Cooldown"])) | |||
addRow(root, "Duration", formatBasePer(basicTimings["Duration"])) | |||
local | local manaCost = formatManaCost(resource["Mana Cost"]) | ||
addRow(root, "Mana cost", manaCost) | |||
local rec = | ------------------------------------------------------------------ | ||
-- Damage + Scaling | |||
------------------------------------------------------------------ | |||
local dmg = rec.Damage or {} | |||
local mainDmg = formatMainDamage(dmg["Main Damage"]) | |||
local scaling = formatScaling(dmg.Scaling) | |||
addRow(root, "Main damage", mainDmg) | |||
addRow(root, "Scaling", scaling) | |||
------------------------------------------------------------------ | |||
-- Status interactions | |||
------------------------------------------------------------------ | |||
local statusApps = formatStatusApplications(rec["Status Applications"]) | |||
addRow(root, "Status applications", statusApps) | |||
if | -- We could add Status Removal here later if you want it visible. | ||
return | return tostring(root) | ||
end | end | ||
return p | return p | ||