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 30: | Line 30: | ||
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 39: | ||
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 104: | ||
end) | end) | ||
-- Build | -- Build table body rows | ||
for _, hero_entry in ipairs(sorted_heroes) do | |||
local hero_key = hero_entry.key | |||
local | local hero_data = hero_entry.data | ||
local row_str = "" | |||
hero_name = localize(hero_key, hero_key) | |||
hero_name_en = hero_data["Name"] | |||
template_args = {[1] = hero_name_en, l1 = hero_name} | |||
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) | |||
local stat_value = base_value | |||
local scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key) | |||
local scaling_strs = "" | |||
local spirit_scale = 0 | |||
local level_scale = 0 | |||
local | |||
local | |||
local | |||
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 | 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") | |||
and hero_data.Weapon | |||
and hero_data.Weapon.HitOnceAcrossAllBullets | |||
then | |||
hit_once = "true" | |||
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 | ||
body_str = body_str .. "<tr>" .. row_str .. "</tr>" | |||
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 207: | ||
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 | if scaling_data ~= nil and next(scaling_data) ~= nil then | ||
stats_with_any_scaling[attr_key] = true | stats_with_any_scaling[attr_key] = true | ||
local has_spirit | |||
local has_spirit = false | |||
local has_level = false | |||
for _, scaling_type in pairs(scaling_data) do | for _, scaling_type in pairs(scaling_data) do | ||
if scaling_type == "Spirit" then has_spirit = true | if scaling_type == "Spirit" then has_spirit = true end | ||
if scaling_type == "Level" then has_level = true end | |||
end | end | ||
if has_spirit and has_level then | if has_spirit and has_level then | ||
stats_with_both_scaling[attr_key] = true | |||
end | end | ||
end | end | ||
| Line 419: | Line 226: | ||
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 260: | ||
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 282: | ||
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 295: | ||
'<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 | ||