Editing Module:Sandbox/VergirGive feedback
Jump to navigation
Jump to search
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 1: | Line 1: | ||
-- Builds tables of hero base stats that scale with a given scaling type, | |||
-- read from [[Data:HeroData.json]]. | |||
-- | |||
-- Each public function below renders one table. The shared work lives in | |||
-- build_scaling_table(), so adding a table for another scaling type (Boons, | |||
-- for instance) only takes a new wrapper naming its scaling type. | |||
-- | |||
-- {{#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') | |||
-- Never listed: both are derived from the weapon stats that are already | |||
-- listed in their own right, so they only ever duplicate a row. | |||
local EXCLUDED_STATS = { | |||
DPS = true, | |||
SustainedDPS = true, | |||
} | |||
-- Per stat handling of the base value shown before the scaling. | |||
-- text: printed verbatim, for stats the data holds no base value for | |||
-- sig_figs: rounds an over-precise base value | |||
local BASE_DISPLAY = { | |||
BulletResist = {text = "0%"}, | |||
TechResist = {text = "0%"}, | |||
RoundsPerSecond = {sig_figs = 3}, | |||
} | |||
-------------------------------------------------------------------------------- | |||
-- 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 | |||
-- 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) .. | |||
mw.getCurrentFrame():expandTemplate{title = "MissingValveTranslationTooltip"} | |||
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) | |||
return mw.getCurrentFrame():expandTemplate{ | |||
title = "HeroIcon", | |||
args = {[1] = hero_data["Name"] or hero_key, l1 = lang_module.get_string(hero_key)}, | |||
} | |||
end | |||
-- Base value as displayed, or nil when the stat has none to show. | |||
local function base_value(hero_data, stat) | |||
local display = BASE_DISPLAY[stat] | |||
if (display ~= nil and display.text ~= nil) then return display.text end | |||
local base = hero_data_module.get_stat(hero_data, stat) | |||
if (type(base) ~= "number") then return nil end | |||
if (display ~= nil and display.sig_figs ~= nil) then | |||
base = util_module.round_to_sig_fig(base, display.sig_figs) | |||
end | |||
return tostring(base) | |||
end | |||
-- "6.3 + x0.0084", or the scaling alone when there is no base value to show. | |||
local function scaling_cell(hero_data, stat, value, scaling_type) | |||
local scaling_str = hero_data_module.write_scalar_str(value, scaling_type) | |||
local base = base_value(hero_data, stat) | |||
if (base == nil) then return scaling_str end | |||
return base .. ' + ' .. scaling_str | |||
end | |||
-- Every scaling a hero has of one type, minus the excluded stats and any | |||
-- zeroes, ordered by localized stat name: | |||
-- { { stat = , label = , value = }, ... } | |||
local function hero_scalings(hero_data, scaling_key) | |||
local scalings = {} | |||
local hero_scaling_data = hero_data[scaling_key] | |||
for _, stat in ipairs(sorted_keys(hero_scaling_data)) do | |||
local value = hero_scaling_data[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 | |||
-------------------------------------------------------------------------------- | |||
-- Shared table builder | |||
-------------------------------------------------------------------------------- | |||
-- scaling_type: "Spirit" or "Level", picking both the <type>Scaling data key | |||
-- and the scaling template ({{Ss}} or {{PI}}). | |||
local function build_scaling_table(scaling_type) | |||
local scaling_key = scaling_type .. "Scaling" | |||
-- Collect the heroes that have anything to show | |||
local rows = {} | |||
for _, hero_key in ipairs(sorted_keys(heroes_data)) do | |||
local hero_data = heroes_data[hero_key] | |||
if (not hero_data["InDevelopment"] and not hero_data["IsDisabled"]) then | |||
local scalings = hero_scalings(hero_data, scaling_key) | |||
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 sortable"', | |||
'! ' .. 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 attributes = 'data-sort-value="' .. row.sort_name .. '"' | |||
if (#row.scalings > 1) then | |||
attributes = attributes .. ' rowspan="' .. #row.scalings .. '"' | |||
end | |||
table.insert(output, '| ' .. attributes .. ' | ' .. hero_icon(row.key, row.data)) | |||
end | |||
table.insert(output, '| ' .. scaling.label) | |||
table.insert(output, '| ' .. scaling_cell(row.data, scaling.stat, scaling.value, scaling_type)) | |||
end | |||
end | |||
table.insert(output, '|}') | |||
return table.concat(output, '\n') | |||
end | |||
-------------------------------------------------------------------------------- | |||
-- Wikitext entry points | |||
-------------------------------------------------------------------------------- | |||
-- Hero base stats that scale with Spirit Power. | |||
--{{#invoke:Sandbox/Vergir|write_spirit_scaling_table}} | |||
function p.write_spirit_scaling_table(frame) | |||
return build_scaling_table("Spirit") | |||
end | |||
return p | return p | ||