Module:Sandbox/LVL: Difference between revisionsGive feedback
No edit summary Tag: Undo |
No edit summary |
||
| Line 552: | Line 552: | ||
--writes the massive hero comparison table for a specific PI and SP | --writes the massive hero comparison table for a specific PI and SP | ||
-- scaling icons are shown only if PI and SP are both 0 or both not provided | -- scaling icons are shown only if PI and SP are both 0 or both not provided | ||
--{{#invoke:HeroData|write_hero_comparison_table|POWER_INCREASES|SPIRIT_POWER}} | --{{#invoke:HeroData|write_hero_comparison_table|POWER_INCREASES|SPIRIT_POWER|DATA_ATTRS}} | ||
-- DATA_ATTRS: pass "true" to output base values with data-base/data-spirit-scale/data-level-scale | |||
-- attributes on each <td> for client-side JavaScript recalculation | |||
p.write_hero_comparison_table = function(frame) | p.write_hero_comparison_table = function(frame) | ||
-- process inputs | |||
local power_increases = frame.args[1] | |||
if (power_increases == nil) then power_increases = 0 end | |||
local spirit_power = frame.args[2] | |||
if (spirit_power == nil) then spirit_power = 0 end | |||
local data_attrs = frame.args[3] -- "true" to embed data attributes for JS scaling | |||
-- Show scaling icons if power/increases/spirit power are 0/not specified | |||
local display_scaling_icons = false | |||
if (power_increases == 0 and spirit_power == 0) then | |||
display_scaling_icons = true | |||
end | |||
-- Initializations and declarations | |||
local row_str = "" | |||
local body_str = "" | |||
local template_title = "" | |||
local template_args = {} | |||
local hero_icon | |||
-- Add hero comp stats to | |||
local stats_to_include = { | |||
Weapon = { | |||
"DPS", | |||
"SustainedDPS", | "SustainedDPS", | ||
"BulletDamage", | "BulletDamage", | ||
| Line 599: | Line 602: | ||
"SpinAcceleration", | "SpinAcceleration", | ||
"SpinDeceleration" | "SpinDeceleration" | ||
}, | |||
Vitality = { | |||
"MaxHealth", | |||
"BaseHealthRegen", | "BaseHealthRegen", | ||
"BulletResist", | "BulletResist", | ||
| Line 613: | Line 616: | ||
"Stamina", | "Stamina", | ||
"GroundDashSpeed" | "GroundDashSpeed" | ||
}, | |||
Spirit = { | |||
"TechPower" | |||
} | |||
} | |||
-- Collect and sort heroes alphabetically by name | |||
local sorted_heroes = {} | |||
for hero_key, hero_data in pairs(heroes_data) do | |||
if not hero_data["InDevelopment"] and not hero_data["IsDisabled"] then | |||
table.insert(sorted_heroes, {key = hero_key, data = hero_data}) | |||
end | |||
end | |||
table.sort(sorted_heroes, function(a, b) | |||
return (a.data["Name"] or a.key) < (b.data["Name"] or b.key) | |||
end) | |||
-- Iterate heroes | |||
for _, hero_entry in ipairs(sorted_heroes) do | |||
local hero_key = hero_entry.key | |||
local hero_data = hero_entry.data | |||
row_str = "" | |||
-- Retrieve hero's localized name | |||
hero_name = localize(hero_key, hero_key) | |||
-- Retrieve hero's english name, used for hero icon | |||
hero_name_en = hero_data["Name"] | |||
-- Expand hero icon | |||
template_title = "Template:HeroIcon" | |||
template_args[1] = hero_name_en | |||
template_args[2] = hero_name | |||
hero_icon = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args } | |||
-- First column in each row is hero name | |||
row_str = row_str .. "<td>" .. hero_icon .. "</td>" | |||
-- Consecutive columns are then stats | |||
-- Iterate categories | |||
for _, category in ipairs(attribute_orders["category_order"]) do | |||
category_attrs = attributes_data[category] | |||
-- Iterate attributes within the category | |||
if (stats_to_include[category] ~= nil) then | |||
for _, attr_key in ipairs(stats_to_include[category]) do | |||
-- Retrieve the raw base value (unscaled) | |||
local base_value = get_nested_stat_value(hero_data, attr_key) | |||
-- Retrieve scaling coefficients (always, needed for both modes) | |||
local scaling_data = p.get_hero_scaling_data(hero_data, attr_key) | |||
-- Extract individual scale values | |||
local spirit_scale = 0 | |||
local level_scale = 0 | |||
if (scaling_data ~= nil) then | |||
for sv, stype in pairs(scaling_data) do | |||
if (stype == "Spirit") then spirit_scale = sv end | |||
if (stype == "Level") then level_scale = sv end | |||
end | |||
end | |||
if (data_attrs == "true") then | |||
-- DATA ATTR MODE: output base value, embed scaling coefficients for JS | |||
local display_value = base_value | |||
if (type(display_value) == 'boolean') then | |||
display_value = tostring(display_value) | |||
else | |||
display_value = util_module.round_to_sig_fig(display_value, 3) | |||
end | |||
-- Always show scaling icons in data attr mode | |||
local scaling_strs = "" | |||
if (scaling_data ~= nil) then | |||
for scaling_value, scaling_type in pairs(scaling_data) do | |||
local scaling_str = p.write_scalar_str(scaling_value, scaling_type) | |||
if (scaling_str ~= "") then scaling_str = " " .. scaling_str end | |||
if (scaling_str ~= nil) then | |||
scaling_strs = scaling_strs .. scaling_str | |||
end | |||
end | |||
end | |||
-- Build data attribute string | |||
local data_str = 'data-base="' .. base_value .. '"' | |||
.. ' data-spirit-scale="' .. spirit_scale .. '"' | |||
.. ' data-level-scale="' .. level_scale .. '"' | |||
.. ' data-spirit="' .. spirit_power .. '"' | |||
.. ' data-level="' .. power_increases .. '"' | |||
-- Mark resist cells so JS knows to use multiplicative formula | |||
if attr_key == 'TechResist' or attr_key == 'BulletResist' then | |||
data_str = data_str .. ' data-resist="1"' | |||
end | |||
row_str = row_str .. '<td ' .. data_str .. '>' | |||
.. display_value .. scaling_strs .. "</td>" | |||
else | |||
-- ORIGINAL MODE: compute scaled values (existing behavior) | |||
local stat_value = base_value | |||
local scaling_strs = "" | |||
if (scaling_data ~= nil) then | |||
for scaling_value, scaling_type in pairs(scaling_data) do | |||
local scaling_str = p.write_scalar_str(scaling_value, scaling_type) | |||
if (scaling_str ~= "") then scaling_str = " " .. scaling_str end | |||
if (scaling_str ~= nil) then | |||
scaling_strs = scaling_strs .. scaling_str | |||
end | |||
-- Scale the stat value | |||
if (scaling_type == "Spirit") then | |||
stat_value = stat_value + (spirit_power * scaling_value) | |||
elseif (scaling_type == "Level") then | |||
if attr_key == 'TechResist' or attr_key == 'BulletResist' then | |||
-- each PI for resists is multiplicative | |||
stat_value = (stat_value/100 + 1 - (1-scaling_value/100)^power_increases)*100 | |||
else | |||
stat_value = stat_value + (power_increases * scaling_value) | |||
end | |||
end | |||
end | |||
end | |||
if (not display_scaling_icons) then scaling_strs = "" end | |||
-- Convert from boolean to string, or round it | |||
if (type(stat_value)) == 'boolean' then | |||
stat_value = tostring(stat_value) | |||
else | |||
stat_value = util_module.round_to_sig_fig(stat_value, 3) | |||
end | |||
-- Add it to the row | |||
row_str = row_str .. "<td>" .. stat_value .. scaling_strs .. "</td>" | |||
end | |||
end | |||
end | |||
end | |||
-- Add row to the body | |||
row_str = "<tr>" .. row_str .. "</tr>" | |||
body_str = body_str .. row_str | |||
end | |||
-- First header is Hero | |||
local headers_str = "<th>" .. "Hero" .. "</th>" | |||
local header_style = "" | |||
local category_data = attribute_module.get_category_data() | |||
local postfix | |||
-- Used for keys that are not localized by Valve | |||
local postfix_key_map = { | |||
["ReloadDelay"] = "StatDesc_ReloadTime_postfix", | |||
["BulletsPerShot"] = "", | |||
["BulletsPerBurst"] = "", | |||
["BurstInterShotInterval"] = "StatDesc_ReloadTime_postfix", | |||
["ReloadSingle"] = "", | |||
["BonusAttackRange"] = "StatDesc_WeaponRangeFalloffMax_postfix", | |||
["SustainedDPS"] = "DPS_postfix", | |||
["RoundsPerSecondAtMaxSpin"] = "", | |||
["CritDamageBonusScale"] = "StatDesc_CritDamageBonusScale_postfix", | |||
["BonusCritDamagePercent"] = "BonusCritDamagePercent_postfix", | |||
["BulletLifesteal"] = "BulletLifestealPercentHero_postfix", | |||
["GroundDashSpeed"] = "DashSpeed_postfix" | |||
} | |||
-- Iterate stats again for displaying headers with their respective color | |||
for _, category in ipairs(attribute_orders["category_order"]) do | |||
category_attrs = attributes_data[category] | |||
category_rgb = category_data[category]["rgb"] | |||
-- Iterate attributes within the category | |||
if (stats_to_include[category] ~= nil) then | |||
for _, attr_key in ipairs(stats_to_include[category]) do | |||
attr_data = category_attrs[attr_key] | |||
if (attr_data ~= nil) then | |||
-- Localize, fallback to attr_key with proper spaces | |||
attr_localized = lang_module.get_string(attr_data["label"]) | |||
if (attr_localized == nil or attr_localized == "") then attr_localized = util_module.add_space_before_cap(attr_key) end | |||
-- Get postfix | |||
postfix = lang_module.get_string(attr_data["postfix"]) | |||
if (postfix == nil or postfix == "") then | |||
postfix = "" | |||
else | |||
postfix = " (" .. postfix .. ")" | |||
end | |||
else -- If not in attributes data, use dictionary translate and postfix | |||
attr_localized = dictionary_module.translate(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 ~= "" then | |||
postfix = " (" .. postfix .. ")" | |||
end | |||
end | |||
header_style = ' style="background-color: ' .. "rgb(" .. category_rgb .. ') ;"' | |||
headers_str = headers_str .. "<th" .. header_style .. ">" .. attr_localized .. postfix .. "</th>" | |||
end | |||
end | |||
end | |||
headers_str = "<tr>" .. headers_str .. "</tr>" | |||
local table_str = '<table id="hero-comparison-table" class="wikitable sortable" style="table-layout: auto; width: 100%;">' | |||
.. headers_str .. body_str .. "</table>" | |||
local output_str = '<div style="overflow-x: auto; width: 100%;">' .. table_str .. '</div>' | |||
return output_str | |||
end | end | ||