Editing Module:Sandbox/LVLGive feedback
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 10: | Line 10: | ||
local hero_data_module = require('Module:HeroData') | local hero_data_module = require('Module:HeroData') | ||
local function get_nested_stat_value(hero_data, stat_key) | local function get_nested_stat_value(hero_data, stat_key, prefer_alt_fire) | ||
if prefer_alt_fire and hero_data.Weapon and hero_data.Weapon.AltFire and hero_data.Weapon.AltFire[stat_key] ~= nil then | |||
return hero_data.Weapon.AltFire[stat_key] | |||
end | |||
if hero_data[stat_key] ~= nil then | if hero_data[stat_key] ~= nil then | ||
return hero_data[stat_key] | return hero_data[stat_key] | ||
| Line 30: | Line 33: | ||
end | end | ||
-- | -- {{#invoke:HeroComparisonTable|write_hero_comparison_table|POWER_INCREASES|SPIRIT_POWER|MAX_POWER|MAX_SPIRIT}} | ||
p.write_hero_comparison_table = function(frame) | p.write_hero_comparison_table = function(frame) | ||
local power_increases = tonumber(frame.args[1]) or 0 | local power_increases = tonumber(frame.args[1]) or 0 | ||
| Line 123: | Line 42: | ||
local display_scaling_icons = (power_increases == 0 and spirit_power == 0) | local display_scaling_icons = (power_increases == 0 and spirit_power == 0) | ||
local body_str = "" | local body_str = "" | ||
local hero_name | |||
local hero_name_en | |||
local hero_icon | |||
local template_args = {} | |||
local stats_to_include = { | local stats_to_include = { | ||
Weapon = { | Weapon = { | ||
"DPS", "SustainedDPS", "BulletDamage", "RoundsPerSecond", "FireRate", | "DPS", | ||
"ClipSize", "ReloadTime", "ReloadDelay", | "SustainedDPS", | ||
"BurstInterShotInterval", "LightMeleeDamage", "HeavyMeleeDamage", | "BulletDamage", | ||
"BulletSpeed", "FalloffStartRange", "FalloffEndRange", | "RoundsPerSecond", | ||
" | "FireRate", | ||
"ClipSize", | |||
"ReloadTime", | |||
"ReloadDelay", | |||
"BulletsPerShot", | |||
"BulletsPerBurst", | |||
"BurstInterShotInterval", | |||
"LightMeleeDamage", | |||
"HeavyMeleeDamage", | |||
"ReloadSingle", | |||
"BulletSpeed", | |||
"BulletGravityScale", | |||
"FalloffStartRange", | |||
"FalloffEndRange", | |||
"BonusAttackRange", | |||
"CritDamageBonusPercent", | |||
"RoundsPerSecondAtMaxSpin", | |||
"SpinAcceleration", | |||
"SpinDeceleration" | |||
}, | }, | ||
Vitality = { | Vitality = { | ||
"MaxHealth", "BaseHealthRegen", "BulletResist", "TechResist", "MeleeResist", | "MaxHealth", | ||
" | "BaseHealthRegen", | ||
"SprintSpeed", "StaminaCooldown", "Stamina", "GroundDashSpeed | "BulletResist", | ||
"TechResist", | |||
"MeleeResist", | |||
"BulletLifesteal", | |||
"CritDamageReceivedPercent", | |||
"MaxMoveSpeed", | |||
"SprintSpeed", | |||
"StaminaCooldown", | |||
"Stamina", | |||
"GroundDashSpeed" | |||
}, | }, | ||
Spirit = { "TechPower" } | Spirit = { | ||
"TechPower" | |||
} | |||
} | } | ||
-- Collect and sort heroes alphabetically, | -- Collect and sort heroes alphabetically, skipping disabled/in-development | ||
local sorted_heroes = {} | local sorted_heroes = {} | ||
for hero_key, hero_data in pairs(heroes_data) do | for hero_key, hero_data in pairs(heroes_data) do | ||
| Line 155: | Line 107: | ||
end) | end) | ||
local function build_hero_row(hero_entry, is_alt_row) | |||
local function | local hero_key = hero_entry.key | ||
local hero_data = hero_entry.data | |||
local | local row_str = "" | ||
local | |||
local | local hero_name = localize(hero_key, hero_key) | ||
local hero_name_en = hero_data["Name"] | |||
if is_alt_row then | |||
hero_name = hero_name .. " (Alt-fire)" | |||
end | end | ||
local template_args = {[1] = hero_name_en, l1 = hero_name} | |||
local hero_icon = mw.getCurrentFrame():expandTemplate{ title = "Template:HeroIcon", args = template_args } | |||
row_str = row_str .. | |||
'<td style="position: sticky; left: 0; z-index: 10; background-color: #202122; isolation: isolate; overflow: hidden;">' .. | |||
hero_icon .. "</td>" | |||
for _, category in ipairs(attribute_orders["category_order"]) do | |||
if stats_to_include[category] ~= nil then | |||
for _, attr_key in ipairs(stats_to_include[category]) do | |||
local base_value = get_nested_stat_value(hero_data, attr_key, is_alt_row) | |||
local stat_value = base_value | |||
local scaling_data = nil | |||
local scaling_strs = "" | |||
local spirit_scale = 0 | |||
local level_scale = 0 | |||
if is_alt_row then | |||
scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire") | |||
if scaling_data ~= nil and next(scaling_data) == nil then | |||
scaling_data = nil | |||
end | |||
end | |||
if scaling_data == nil then | |||
scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key) | |||
if scaling_data ~= nil and next(scaling_data) == nil then | |||
scaling_data = nil | |||
end | |||
end | end | ||
if scaling_data ~= nil then | |||
for scaling_val, scaling_type in pairs(scaling_data) do | |||
local scaling_str = hero_data_module.write_scalar_str(scaling_val, scaling_type, true) | |||
if scaling_str ~= "" then scaling_str = " " .. scaling_str end | |||
if scaling_str ~= nil then | |||
scaling_strs = scaling_strs .. scaling_str | |||
end | |||
if scaling_type == "Spirit" then | |||
spirit_scale = scaling_val | |||
stat_value = stat_value + (spirit_power * scaling_val) | |||
elseif scaling_type == "Level" then | |||
level_scale = scaling_val | |||
stat_value = stat_value + (power_increases * scaling_val) | |||
end | |||
end | |||
end | |||
if attr_key == "TechPower" and spirit_scale == 0 then | |||
spirit_scale = 1.0 | |||
stat_value = stat_value + (spirit_power * spirit_scale) | |||
end | |||
local innate_spirit_scale = | |||
(hero_data["LevelScaling"] and hero_data["LevelScaling"]["TechPower"]) or 0 | |||
if not display_scaling_icons then scaling_strs = "" end | |||
if type(stat_value) == "boolean" then | |||
stat_value = tostring(stat_value) | |||
else | |||
stat_value = util_module.round_to_sig_fig(stat_value, 3) | |||
end | |||
local cell_inner = string.format( | |||
'<span class="stat-num">%s</span><span class="stat-scaling">%s</span>', | |||
stat_value, | |||
scaling_strs | |||
) | |||
local hit_once = "false" | |||
if (attr_key == "DPS" or attr_key == "SustainedDPS") then | |||
if not is_alt_row then | |||
if hero_data.Weapon and hero_data.Weapon.HitOnceAcrossAllBullets then | |||
hit_once = "true" | |||
end | |||
else | |||
if hero_data.Weapon and hero_data.Weapon.AltFire and hero_data.Weapon.AltFire.HitOnceAcrossAllBullets then | |||
hit_once = "true" | |||
end | |||
end | |||
end | |||
local data_attrs = string.format( | |||
'data-stat-name="%s" data-base="%s" data-spirit-scale="%s" data-level-scale="%s" data-innate-spirit-scale="%s" data-sort-value="%s" data-hit-once="%s"', | |||
attr_key, | |||
tostring(type(base_value) == "number" and util_module.round_to_sig_fig(base_value, 3) or base_value), | |||
tonumber(spirit_scale) or 0, | |||
(attr_key == "TechPower") and "0" or (tonumber(level_scale) or 0), | |||
innate_spirit_scale or 0, | |||
stat_value, | |||
hit_once | |||
) | |||
row_str = row_str .. | |||
string.format('<td style="white-space: nowrap;" %s>%s</td>', data_attrs, cell_inner) | |||
end | end | ||
end | end | ||
end | end | ||
return "<tr>" .. row_str .. "</tr>" | return "<tr>" .. row_str .. "</tr>" | ||
end | end | ||
-- | -- Build table body rows | ||
for _, hero_entry in ipairs(sorted_heroes) do | |||
body_str = body_str .. build_hero_row(hero_entry, false) | |||
if hero_entry.data.Weapon and hero_entry.data.Weapon.AltFire then | |||
body_str = body_str .. build_hero_row(hero_entry, true) | |||
body_str = body_str .. | |||
end | end | ||
end | end | ||
-- Pre-pass: determine which stats have any scaling, and which have both types | -- Pre-pass: determine which stats have any scaling, and which have both types | ||
local stats_with_any_scaling = {} | local stats_with_any_scaling = {} | ||
local stats_with_both_scaling = {} | local stats_with_both_scaling = {} | ||
| Line 369: | Line 239: | ||
if stats_to_include[category] ~= nil then | if stats_to_include[category] ~= nil then | ||
for _, attr_key in ipairs(stats_to_include[category]) do | for _, attr_key in ipairs(stats_to_include[category]) do | ||
local scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key) | local scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key) | ||
if scaling_data ~= nil and next(scaling_data) ~= nil then | local alt_scaling_data = nil | ||
if hero_data.Weapon and hero_data.Weapon.AltFire then | |||
alt_scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire") | |||
end | |||
local has_scaling = (scaling_data ~= nil and next(scaling_data) ~= nil) | |||
local has_alt_scaling = (alt_scaling_data ~= nil and next(alt_scaling_data) ~= nil) | |||
if has_scaling or has_alt_scaling then | |||
stats_with_any_scaling[attr_key] = true | stats_with_any_scaling[attr_key] = true | ||
local has_spirit | |||
for _, scaling_type in pairs(scaling_data) do | local has_spirit = false | ||
local has_level = false | |||
if has_scaling then | |||
for _, scaling_type in pairs(scaling_data) do | |||
if scaling_type == "Spirit" then has_spirit = true end | |||
if scaling_type == "Level" then has_level = true end | |||
end | |||
end | end | ||
if | if has_alt_scaling then | ||
for _, scaling_type in pairs(alt_scaling_data) do | |||
if scaling_type == "Spirit" then has_spirit = true end | |||
if scaling_type == "Level" then has_level = true end | |||
if | |||
end | end | ||
end | |||
if has_spirit and has_level then | |||
stats_with_both_scaling[attr_key] = true | |||
end | end | ||
end | end | ||
| Line 419: | Line 274: | ||
end | end | ||
-- Build | -- Build header row | ||
local headers_str = '<th style="position: sticky; left: 0; top: -1px; z-index: 12; background-color: | local headers_str = '<th style="position: sticky; left: 0; top: -1px; z-index: 12; background-color: #27292d; isolation: isolate;">Hero</th>' | ||
local category_data = attribute_module.get_category_data() | local category_data = attribute_module.get_category_data() | ||
local postfix_key_map = { | local postfix_key_map = { | ||
["ReloadDelay"] = "StatDesc_ReloadTime_postfix", | ["ReloadDelay"] = "StatDesc_ReloadTime_postfix", | ||
["BulletsPerShot"] = "", | ["BulletsPerShot"] = "", | ||
["BulletsPerBurst"] = "", | ["BulletsPerBurst"] = "", | ||
["BurstInterShotInterval"] = "StatDesc_ReloadTime_postfix", | ["BurstInterShotInterval"] = "StatDesc_ReloadTime_postfix", | ||
["ReloadSingle"] = "", | ["ReloadSingle"] = "", | ||
["BonusAttackRange"] = "StatDesc_WeaponRangeFalloffMax_postfix", | ["BonusAttackRange"] = "StatDesc_WeaponRangeFalloffMax_postfix", | ||
["SustainedDPS"] = "DPS_postfix", | ["SustainedDPS"] = "DPS_postfix", | ||
["RoundsPerSecondAtMaxSpin"] = "", | ["RoundsPerSecondAtMaxSpin"] = "", | ||
[" | ["CritDamageBonusScale"] = "StatDesc_CritDamageBonusScale_postfix", | ||
["BonusCritDamagePercent"] = "BonusCritDamagePercent_postfix", | |||
["BulletLifesteal"] = "BulletLifestealPercentHero_postfix", | ["BulletLifesteal"] = "BulletLifestealPercentHero_postfix", | ||
["GroundDashSpeed"] = "DashSpeed_postfix" | ["GroundDashSpeed"] = "DashSpeed_postfix" | ||
} | } | ||
for _, category in ipairs(attribute_orders["category_order"]) do | for _, category in ipairs(attribute_orders["category_order"]) do | ||
local category_attrs = attributes_data[category] | local category_attrs = attributes_data[category] | ||
local category_rgb = category_data[category]["rgb"] | local category_rgb = category_data[category]["rgb"] | ||
if stats_to_include[category] ~= nil then | if stats_to_include[category] ~= nil then | ||
for _, attr_key in ipairs(stats_to_include[category]) do | for _, attr_key in ipairs(stats_to_include[category]) do | ||
local attr_data = category_attrs[attr_key] | local attr_data = category_attrs[attr_key] | ||
local attr_localized | |||
local postfix | |||
if attr_data ~= nil then | if attr_data ~= nil then | ||
attr_localized = lang_module.get_string(attr_data["label"]) | attr_localized = lang_module.get_string(attr_data["label"]) | ||
| Line 460: | Line 308: | ||
end | end | ||
postfix = lang_module.get_string(attr_data["postfix"]) | postfix = lang_module.get_string(attr_data["postfix"]) | ||
if postfix == nil or postfix == "" then postfix = "" | if postfix == nil or postfix == "" then | ||
else postfix = " (" .. postfix .. ")" | postfix = "" | ||
else | |||
postfix = " (" .. postfix .. ")" | |||
end | |||
else | else | ||
attr_localized = dictionary_module.translate(attr_key) | attr_localized = dictionary_module.translate(attr_key) | ||
postfix = lang_module.get_string(postfix_key_map[attr_key]) | postfix = lang_module.get_string(postfix_key_map[attr_key]) | ||
if postfix == nil then return "attr_key " .. attr_key .. " must be added to postfix_key_map" end | if postfix == nil then | ||
if postfix ~= "" then postfix = " (" .. postfix .. ")" end | return "attr_key " .. attr_key .. " must be added to postfix_key_map" | ||
end | |||
if postfix ~= "" then | |||
postfix = " (" .. postfix .. ")" | |||
end | |||
end | end | ||
local th_style = 'position: sticky; top: -1px; z-index: 3; background-color: rgb(' .. category_rgb .. ');' | local th_style = 'position: sticky; top: -1px; z-index: 3; background-color: rgb(' .. category_rgb .. ');' | ||
if stats_with_both_scaling[attr_key] then | if stats_with_both_scaling[attr_key] then | ||
| Line 475: | Line 330: | ||
th_style = th_style .. ' min-width: 75px;' | th_style = th_style .. ' min-width: 75px;' | ||
end | end | ||
headers_str = headers_str .. '<th style="' .. th_style .. '">' .. attr_localized .. postfix .. "</th>" | |||
headers_str = headers_str .. | |||
'<th style="' .. th_style .. '">' .. attr_localized .. postfix .. "</th>" | |||
end | end | ||
end | end | ||
| Line 486: | Line 343: | ||
'<table class="wikitable sortable" style="table-layout: auto; width: 100%%;" id="hero-comparison-table">%s%s</table>' .. | '<table class="wikitable sortable" style="table-layout: auto; width: 100%%;" id="hero-comparison-table">%s%s</table>' .. | ||
'</div></div>', | '</div></div>', | ||
max_power, max_spirit, headers_str, body_str | max_power, | ||
max_spirit, | |||
headers_str, | |||
body_str | |||
) | ) | ||
end | end | ||
return p | return p | ||