Module:Sandbox/Vergir: Difference between revisions

Vergir (talk | contribs)
Test draft of the Spirit scaling table module (with help from vergir-bot LLM)
Cleanup after audit (with help from vergir-bot LLM)
Tag: Replaced
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
-- Renders a table of hero base stats that scale with Spirit Power, built from
-- the SpiritScaling entries in [[Data:HeroData.json]].
-- {{#invoke:Sandbox/Vergir|write_spirit_scaling_table}}
local p = {}
local p = {}
local heroes_data      = mw.loadJsonData("Data:HeroData.json")
local attributes_data  = mw.loadJsonData("Data:AttributeData.json")
local util_module      = require('Module:Utilities')
local lang_module      = require('Module:Lang')
local dictionary_module = require('Module:Dictionary')
local hero_data_module  = require('Module:HeroData')
-- Stats left out by default. Both are derived from stats that are already
-- listed in their own right, so including them would duplicate every row.
local DEFAULT_EXCLUDED_STATS = "DPS,SustainedDPS"
--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
-- next() ignores metamethods and mw.loadJsonData hands back proxy tables, so
-- keys always have to be collected through pairs() rather than tested with next().
local function sorted_keys(tbl)
local keys = {}
if (tbl == nil) then return keys end
for key in pairs(tbl) do table.insert(keys, key) end
table.sort(keys)
return keys
end
-- Comma or whitespace separated list to a set.
local function to_set(list)
local set = {}
if (list == nil) then return set end
for entry in string.gmatch(list, "[^,%s]+") do set[entry] = true end
return set
end
local function missing_translation_tooltip()
return mw.getCurrentFrame():expandTemplate{title = "MissingValveTranslationTooltip"}
end
-- Localized name of a stat, resolved in three steps:
--  1. [[Data:AttributeData.json]] label -> Valve's own translation
--  2. [[Data:Dictionary]] entry, for stats Valve has no label for
--  3. the raw key, spaced out, with a missing-translation tooltip
local function stat_label(stat)
for _, category in pairs(attributes_data) do
local attribute = category[stat]
if (attribute ~= nil and attribute["label"] ~= nil) then
local localized = lang_module.get_string(attribute["label"])
if (localized ~= nil and localized ~= '') then return localized end
end
end
local translated = dictionary_module.translate(stat)
if (translated ~= nil and translated:sub(1, 5) ~= "Key '") then return translated end
return util_module.add_space_before_cap(stat) .. missing_translation_tooltip()
end
-- Heroes are listed as they are read, so "The Doorman" sorts under D.
local function sort_name(hero_data, hero_key)
return (hero_data["Name"] or hero_key):gsub("^The ", "")
end
local function hero_icon(hero_key, hero_data)
local template_args = {
[1] = hero_data["Name"] or hero_key,
l1 = lang_module.get_string(hero_key),
}
return mw.getCurrentFrame():expandTemplate{title = "HeroIcon", args = template_args}
end
-- Every Spirit scaling a hero has, minus the excluded stats and any zeroes,
-- ordered by localized stat name: { { stat = , label = , value = }, ... }
local function hero_scalings(hero_data, excluded_stats)
local scalings = {}
for _, stat in ipairs(sorted_keys(hero_data["SpiritScaling"])) do
local value = hero_data["SpiritScaling"][stat]
if (not excluded_stats[stat] and type(value) == "number" and value ~= 0) then
table.insert(scalings, {stat = stat, label = stat_label(stat), value = value})
end
end
table.sort(scalings, function(a, b)
if (a.label == b.label) then return a.stat < b.stat end
return a.label < b.label
end)
return scalings
end
--------------------------------------------------------------------------------
-- Wikitext entry point
--------------------------------------------------------------------------------
--{{#invoke:Sandbox/Vergir|write_spirit_scaling_table|exclude=|include_unreleased=}}
-- exclude:            comma separated stat keys to leave out, defaults to
--                    "DPS,SustainedDPS". Pass an empty value to list every stat.
-- include_unreleased: "yes" to also list heroes flagged InDevelopment or IsDisabled.
function p.write_spirit_scaling_table(frame)
local args = frame.args or {}
local excluded_stats = to_set(args["exclude"] or DEFAULT_EXCLUDED_STATS)
local include_unreleased = (args["include_unreleased"] == "yes" or args["include_unreleased"] == "true")
-- Collect the heroes that have anything left to show
local rows = {}
for _, hero_key in ipairs(sorted_keys(heroes_data)) do
local hero_data = heroes_data[hero_key]
local released = not hero_data["InDevelopment"] and not hero_data["IsDisabled"]
if (released or include_unreleased) then
local scalings = hero_scalings(hero_data, excluded_stats)
if (#scalings > 0) then
table.insert(rows, {
key = hero_key,
data = hero_data,
sort_name = sort_name(hero_data, hero_key),
scalings = scalings,
})
end
end
end
if (#rows == 0) then return '' end
table.sort(rows, function(a, b) return a.sort_name < b.sort_name end)
-- Build the table
local output = {
'{| class="wikitable"',
'|+ ' .. dictionary_module.translate("SpiritScalingTableCaption"),
'! ' .. dictionary_module.translate("Hero"),
'! ' .. dictionary_module.translate("Stats"),
'! ' .. dictionary_module.translate("Scaling"),
}
for _, row in ipairs(rows) do
for index, scaling in ipairs(row.scalings) do
table.insert(output, '|-')
if (index == 1) then
local rowspan = ''
if (#row.scalings > 1) then rowspan = ' rowspan="' .. #row.scalings .. '" |' end
table.insert(output, '|' .. rowspan .. ' ' .. hero_icon(row.key, row.data))
end
table.insert(output, '| ' .. scaling.label)
table.insert(output, '| ' .. hero_data_module.write_scalar_str(scaling.value, "Spirit"))
end
end
table.insert(output, '|}')
return table.concat(output, '\n')
end
return p
return p