Module:GameSkills: Difference between revisions
From SpiritVale Wiki
More actions
No edit summary |
No edit summary |
||
| Line 918: | Line 918: | ||
return nil | return nil | ||
end | end | ||
-- ============================================================ | |||
-- Module 3 – Skill Source (Source + Scaling) | |||
-- - Shows Source (big) + Scaling (smaller list) | |||
-- - If BOTH are missing, module 3 stays empty | |||
-- ============================================================ | |||
-- Compact scaling list: | |||
-- "1% Vitality" | |||
-- "8% Intelligence" | |||
local function formatScalingCompactLines(scaling, basisOverride) | |||
if type(scaling) ~= "table" then | |||
return {} | |||
end | |||
local list = scaling | |||
if #list == 0 then | |||
-- support "single dict" style scaling | |||
if scaling.Percent ~= nil or scaling["Scaling ID"] or scaling["Scaling Name"] then | |||
list = { scaling } | |||
else | |||
return {} | |||
end | |||
end | |||
local out = {} | |||
for _, s in ipairs(list) do | |||
if type(s) == "table" then | |||
local stat = s["Scaling Name"] or s["Scaling ID"] or "Unknown" | |||
local pct = s.Percent | |||
local pctN = toNum(pct) | |||
if pctN ~= nil and pctN ~= 0 then | |||
table.insert(out, string.format("%s%% %s", fmtNum(pctN), stat)) | |||
elseif pct ~= nil and tostring(pct) ~= "" and tostring(pct) ~= "0" then | |||
table.insert(out, string.format("%s%% %s", tostring(pct), stat)) | |||
end | |||
end | |||
end | |||
return out | |||
end | |||
-- Prefer the “current level value” (and dynSpan when we have series lists) | |||
-- Falls back gracefully if Source is not in the expanded-per-level-list form. | |||
local function sourceValueForLevel(src, maxLevel, level) | |||
if type(src) ~= "table" then | |||
return nil | |||
end | |||
local base = src.Base | |||
local per = src["Per Level"] | |||
-- Preferred: expanded series list (wikiprep) | |||
if type(per) == "table" and #per > 0 then | |||
if isFlatList(per) then | |||
local one = formatUnitValue(per[1]) or tostring(per[1]) | |||
local show = formatUnitValue(base) or one | |||
return show and mw.text.nowiki(show) or nil | |||
end | |||
local series = {} | |||
for _, v in ipairs(per) do | |||
table.insert(series, formatUnitValue(v) or tostring(v)) | |||
end | |||
return dynSpan(series, level) | |||
end | |||
-- Fallback: if we can’t build a proper series, use the existing “value only” formatter | |||
-- (may look like "X (Per Level: Y)" if not expanded by wikiprep) | |||
return valuePairDynamicValueOnly(src, maxLevel, level) | |||
end | |||
-- Legacy (pre-Source) damage entry: compute a simple % value at the current level | |||
local function legacyPercentAtLevel(entry, level) | |||
if type(entry) ~= "table" then | |||
return nil | |||
end | |||
local baseRaw = entry["Base %"] | |||
local perRaw = entry["Per Level %"] | |||
local baseN = toNum(baseRaw) | |||
local perN = toNum(perRaw) | |||
-- If Per Level exists, level 1 should show at least per*1 even when base is 0/missing. | |||
if perN ~= nil and perN ~= 0 then | |||
local total = (baseN or 0) + (perN * level) | |||
return fmtNum(total) .. "%" | |||
end | |||
-- Otherwise show base if present | |||
if baseN ~= nil then | |||
return fmtNum(baseN) .. "%" | |||
end | |||
if baseRaw ~= nil and tostring(baseRaw) ~= "" then | |||
return tostring(baseRaw) .. "%" | |||
end | |||
return nil | |||
end | |||
local function buildModuleSkillSource(rec, level, maxLevel) | |||
local sourceKind = nil | |||
local sourceVal = nil | |||
local scaling = nil | |||
-- Preferred: new unified Source block | |||
if type(rec.Source) == "table" then | |||
local src = rec.Source | |||
sourceKind = src.Type or ((src.Healing == true) and "Healing") or "Damage" | |||
sourceVal = sourceValueForLevel(src, maxLevel, level) | |||
scaling = src.Scaling | |||
end | |||
-- Backcompat: legacy Damage block | |||
if (sourceVal == nil or sourceVal == "") and type(rec.Damage) == "table" then | |||
local dmg = rec.Damage | |||
scaling = scaling or dmg.Scaling | |||
local main = dmg["Main Damage"] | |||
local refl = dmg["Reflect Damage"] | |||
local flat = dmg["Flat Damage"] | |||
-- priority: Main Damage > Reflect > Flat > Healing-only main | |||
if type(main) == "table" and #main > 0 then | |||
-- pick first non-healing if possible | |||
local pick = nil | |||
for _, d in ipairs(main) do | |||
if type(d) == "table" and d.Type ~= "Healing" then | |||
pick = d | |||
break | |||
end | |||
end | |||
pick = pick or main[1] | |||
if type(pick) == "table" then | |||
sourceKind = (pick.Type == "Healing") and "Healing" or "Damage" | |||
sourceVal = legacyPercentAtLevel(pick, level) | |||
end | |||
elseif type(refl) == "table" and #refl > 0 and type(refl[1]) == "table" then | |||
sourceKind = "Reflect" | |||
sourceVal = legacyPercentAtLevel(refl[1], level) | |||
elseif type(flat) == "table" and #flat > 0 and type(flat[1]) == "table" then | |||
sourceKind = "Flat" | |||
sourceVal = legacyPercentAtLevel(flat[1], level) | |||
end | |||
end | |||
local scalingLines = formatScalingCompactLines(scaling) | |||
local hasSource = (sourceVal ~= nil and tostring(sourceVal) ~= "") | |||
local hasScaling = (type(scalingLines) == "table" and #scalingLines > 0) | |||
-- RULE: if neither exists, leave module 3 empty | |||
if (not hasSource) and (not hasScaling) then | |||
return nil | |||
end | |||
local wrap = mw.html.create("div") | |||
wrap:addClass("sv-source-grid") | |||
-- Source column (left) | |||
if hasSource then | |||
local left = wrap:tag("div"):addClass("sv-source-col"):addClass("sv-source-main") | |||
left:tag("div"):addClass("sv-source-title"):wikitext(mw.text.nowiki(sourceKind or "Source")) | |||
left:tag("div"):addClass("sv-source-value"):wikitext(sourceVal) | |||
end | |||
-- Scaling column (right) | |||
if hasScaling then | |||
local right = wrap:tag("div"):addClass("sv-source-col"):addClass("sv-source-scaling") | |||
right:tag("div"):addClass("sv-scaling-title"):wikitext("Scaling") | |||
local list = right:tag("div"):addClass("sv-scaling-list") | |||
for _, line in ipairs(scalingLines) do | |||
list:tag("div") | |||
:addClass("sv-scaling-item") | |||
:wikitext(mw.text.nowiki(line)) | |||
end | |||
end | |||
-- If only one side exists, let CSS make it single-column | |||
local extra = { "skill-source-module" } | |||
if hasSource ~= hasScaling then | |||
table.insert(extra, "sv-source-only") | |||
end | |||
return moduleBox(3, extra, tostring(wrap), false) | |||
end | |||
local grid = mw.html.create("div") | local grid = mw.html.create("div") | ||
| Line 954: | Line 1,142: | ||
grid:wikitext(buildModuleLevelSelector(level, maxLevel)) | grid:wikitext(buildModuleLevelSelector(level, maxLevel)) | ||
grid:wikitext(buildModuleSkillType(rec.Type or {})) | grid:wikitext(buildModuleSkillType(rec.Type or {})) | ||
local m3 = buildModuleSkillSource(rec, level, maxLevel) | |||
grid:wikitext(m3 or buildEmptyModule(3)) | |||
grid:wikitext(buildEmptyModule(4)) | grid:wikitext(buildEmptyModule(4)) | ||