Module:Sandbox/LVL: Difference between revisions

LVL (talk | contribs)
No edit summary
Tag: Undo
LVL (talk | contribs)
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
    -- process inputs
local power_increases = frame.args[1]
    local power_increases = frame.args[1]
if (power_increases == nil) then power_increases = 0 end
    if (power_increases == nil) then power_increases = 0 end
local spirit_power = frame.args[2]
    local spirit_power = frame.args[2]
if (spirit_power == nil) then spirit_power = 0 end
    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
    -- Show scaling icons if power/increases/spirit power are 0/not specified
if (power_increases == 0 and spirit_power == 0) then
    local display_scaling_icons = false
display_scaling_icons = true
    if (power_increases == 0 and spirit_power == 0) then
end
        display_scaling_icons = true
    end
-- Initializations and declarations
   
local row_str = ""
    -- Initializations and declarations
local body_str = ""
    local row_str = ""
local template_title = ""
    local body_str = ""
local template_args = {}
    local template_title = ""
local hero_icon
    local template_args = {}
    local hero_icon
-- Add hero comp stats to  
   
local stats_to_include = {
    -- Add hero comp stats to  
Weapon = {
    local stats_to_include = {
"DPS",
        Weapon = {
            "DPS",
             "SustainedDPS",
             "SustainedDPS",
             "BulletDamage",
             "BulletDamage",
Line 599: Line 602:
             "SpinAcceleration",
             "SpinAcceleration",
             "SpinDeceleration"
             "SpinDeceleration"
},
        },
Vitality = {
        Vitality = {
"MaxHealth",
            "MaxHealth",
             "BaseHealthRegen",
             "BaseHealthRegen",
             "BulletResist",
             "BulletResist",
Line 613: Line 616:
             "Stamina",
             "Stamina",
             "GroundDashSpeed"
             "GroundDashSpeed"
},
        },
    Spirit = {
        Spirit = {
"TechPower"
            "TechPower"
}
        }
}
    }


-- Collect and sort heroes alphabetically by name
    -- Collect and sort heroes alphabetically by name
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
if not hero_data["InDevelopment"] and not hero_data["IsDisabled"] then
        if not hero_data["InDevelopment"] and not hero_data["IsDisabled"] then
table.insert(sorted_heroes, {key = hero_key, data = hero_data})
            table.insert(sorted_heroes, {key = hero_key, data = hero_data})
end
        end
end
    end
table.sort(sorted_heroes, function(a, b)
    table.sort(sorted_heroes, function(a, b)
return (a.data["Name"] or a.key) < (b.data["Name"] or b.key)
        return (a.data["Name"] or a.key) < (b.data["Name"] or b.key)
end)
    end)


-- Iterate heroes
    -- Iterate heroes
for _, hero_entry in ipairs(sorted_heroes) do
    for _, hero_entry in ipairs(sorted_heroes) do
local hero_key = hero_entry.key
        local hero_key = hero_entry.key
local hero_data = hero_entry.data
        local hero_data = hero_entry.data


row_str = ""
        row_str = ""
       
-- Retrieve hero's localized name
        -- Retrieve hero's localized name
hero_name = localize(hero_key, hero_key)
        hero_name = localize(hero_key, hero_key)
       
-- Retrieve hero's english name, used for hero icon
        -- Retrieve hero's english name, used for hero icon
hero_name_en = hero_data["Name"]
        hero_name_en = hero_data["Name"]
       
-- Expand hero icon
        -- Expand hero icon
template_title = "Template:HeroIcon"
        template_title = "Template:HeroIcon"
template_args[1] = hero_name_en
        template_args[1] = hero_name_en
template_args[2] = hero_name
        template_args[2] = hero_name
hero_icon = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
        hero_icon = mw.getCurrentFrame():expandTemplate{ title = template_title, args = template_args }
       
-- First column in each row is hero name (sticky)
        -- First column in each row is hero name
row_str = row_str .. '<td style="position: sticky; left: 0; z-index: 10; background-color: #202122; isolation: isolate;">' .. hero_icon .. "</td>"
        row_str = row_str .. "<td>" .. hero_icon .. "</td>"
       
-- Consecutive columns are then stats
        -- Consecutive columns are then stats
-- Iterate categories
        -- Iterate categories
for _, category in ipairs(attribute_orders["category_order"]) do
        for _, category in ipairs(attribute_orders["category_order"]) do
category_attrs = attributes_data[category]
            category_attrs = attributes_data[category]
           
-- Iterate attributes within the category
            -- Iterate attributes within the category
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
                   
--Retrieve the stats value from hero data
                    -- Retrieve the raw base value (unscaled)
stat_value = get_nested_stat_value(hero_data, attr_key)
                    local base_value = get_nested_stat_value(hero_data, attr_key)
                   
-- Retrieve scaling val and str if it scales
                    -- Retrieve scaling coefficients (always, needed for both modes)
local scaling_data = p.get_hero_scaling_data(hero_data, attr_key)
                    local scaling_data = p.get_hero_scaling_data(hero_data, attr_key)
local scaling_strs = ""
                   
if (scaling_data ~= nil) then
                    -- Extract individual scale values
for scaling_value, scaling_type in pairs(scaling_data) do
                    local spirit_scale = 0
local scaling_str = p.write_scalar_str(scaling_value, scaling_type)
                    local level_scale = 0
if (scaling_str ~= "") then scaling_str = " " .. scaling_str end
                    if (scaling_data ~= nil) then
if (scaling_str ~= nil) then
                        for sv, stype in pairs(scaling_data) do
scaling_strs = scaling_strs .. scaling_str
                            if (stype == "Spirit") then spirit_scale = sv end
end
                            if (stype == "Level") then level_scale = sv end
-- Scale the stat value
                        end
if (scaling_type == "Spirit") then
                end
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
-- Add row to the body
row_str = "<tr>" .. row_str .. "</tr>"
body_str = body_str .. row_str
end
-- First header is Hero (sticky)
local headers_str = '<th style="position: sticky; left: 0; top: 0; z-index: 12; background-color: #27292d; isolation: isolate;">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="position: sticky; top: 0; z-index: 3; 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 class="wikitable sortable" style="table-layout: auto; width: 100%;">' .. headers_str .. body_str .. "</table>"
                    if (data_attrs == "true") then
local output_str = '<div style="overflow: auto; max-height: 70vh; width: 100%;">' .. table_str .. '</div>'
                        -- DATA ATTR MODE: output base value, embed scaling coefficients for JS
return output_str
                        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