Module:Sandbox/LVL: Difference between revisions

Jump to navigation Jump to search
LVL (talk | contribs)
No edit summary
LVL (talk | contribs)
No edit summary
Line 80: Line 80:
     local scaling_source = hero_data[scaling_type .. "Scaling"] or {}
     local scaling_source = hero_data[scaling_type .. "Scaling"] or {}
      
      
    -- Collect all alt-fire component scalings that exist
     local component_scalings = {}
     local component_scalings = {}
     local possible_keys = {
     local possible_keys = {
Line 93: Line 92:
     end
     end


     -- If no component scalings, scaling is zero
     -- ClipSize is shared between primary and alt-fire. If no AltFire scaling,
    -- use primary ClipSize scaling (the alt-fire row already uses primary base value)
    if base_stats.ClipSize and not component_scalings.ClipSize then
        local primary_clip_scale = scaling_source["ClipSize"]
        if primary_clip_scale then
            component_scalings.ClipSize = primary_clip_scale
        end
    end
 
     if next(component_scalings) == nil then
     if next(component_scalings) == nil then
         return 0
         return 0
     end
     end


    -- Compute base DPS from the original base stats
     local base_dps = calculate_dps(base_stats, dps_type)
     local base_dps = calculate_dps(base_stats, dps_type)
    local scaled_stats = {}
    for k, v in pairs(base_stats) do scaled_stats[k] = v end
    for comp_key, scale_val in pairs(component_scalings) do
        if scaled_stats[comp_key] ~= nil then
            scaled_stats[comp_key] = scaled_stats[comp_key] + scale_val
        end
    end
    local scaled_dps = calculate_dps(scaled_stats, dps_type)
    return scaled_dps - base_dps
end


     -- Apply all scalings to a copy of base stats
local function compute_dps_scaling_from_components(base_stats, dps_type, component_scalings)
     -- component_scalings is a table like { BulletDamage = 0.165 }
     local scaled_stats = {}
     local scaled_stats = {}
     for k, v in pairs(base_stats) do
     for k, v in pairs(base_stats) do scaled_stats[k] = v end
        scaled_stats[k] = v
    end
     for comp_key, scale_val in pairs(component_scalings) do
     for comp_key, scale_val in pairs(component_scalings) do
         if scaled_stats[comp_key] ~= nil then
         if scaled_stats[comp_key] ~= nil then
Line 111: Line 126:
         end
         end
     end
     end
 
    local base_dps = calculate_dps(base_stats, dps_type)
     local scaled_dps = calculate_dps(scaled_stats, dps_type)
     local scaled_dps = calculate_dps(scaled_stats, dps_type)
     return scaled_dps - base_dps
     return scaled_dps - base_dps
Line 184: Line 199:
             elseif hero_key == "hero_shiv" and attr_key == "BulletDamage" then
             elseif hero_key == "hero_shiv" and attr_key == "BulletDamage" then
                 scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key)
                 scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key)
            -- NEW: Shiv alt-fire DPS/SustainedDPS uses primary BulletDamage scaling
            elseif hero_key == "hero_shiv" and (attr_key == "DPS" or attr_key == "SustainedDPS")
                and hero_data.Weapon and hero_data.Weapon.AltFire then
                local alt_stats = hero_data.Weapon.AltFire
                local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                -- get primary BulletDamage scaling
                local bd_scaling = hero_data_module.get_hero_scaling_data(hero_data, "BulletDamage")
                local level_scale = 0
                local spirit_scale = 0
                if bd_scaling and next(bd_scaling) ~= nil then
                    for val, stype in pairs(bd_scaling) do
                        local component = { ["BulletDamage"] = val }
                        local delta = compute_dps_scaling_from_components(alt_stats, dps_type, component)
                        if stype == "Level" then
                            level_scale = delta
                        elseif stype == "Spirit" then
                            spirit_scale = delta
                        end
                    end
                end
                level_scale = util_module.round_to_sig_fig(level_scale, 5)
                spirit_scale = util_module.round_to_sig_fig(spirit_scale, 5)
                scaling_data = {}
                if level_scale ~= 0 then scaling_data[level_scale] = "Level" end
                if spirit_scale ~= 0 then scaling_data[spirit_scale] = "Spirit" end
                if next(scaling_data) == nil then scaling_data = nil end
            -- General DPS computation for other heroes
             elseif (attr_key == "DPS" or attr_key == "SustainedDPS")
             elseif (attr_key == "DPS" or attr_key == "SustainedDPS")
                 and hero_data.Weapon and hero_data.Weapon.AltFire then
                 and hero_data.Weapon and hero_data.Weapon.AltFire then
                -- try direct scaling key first
                 local direct_scaling = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire")
                 local direct_scaling = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire")
                 if direct_scaling and next(direct_scaling) ~= nil then
                 if direct_scaling and next(direct_scaling) ~= nil then
                     scaling_data = direct_scaling
                     scaling_data = direct_scaling
                 else
                 else
                    -- compute from component scalings
                     local alt_stats = hero_data.Weapon.AltFire
                     local alt_stats = hero_data.Weapon.AltFire
                     local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                     local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                     local level_scale = compute_dps_scaling(hero_data, alt_stats, dps_type, "Level")
                     local level_scale = compute_dps_scaling(hero_data, alt_stats, dps_type, "Level")
                     local spirit_scale = compute_dps_scaling(hero_data, alt_stats, dps_type, "Spirit")
                     local spirit_scale = compute_dps_scaling(hero_data, alt_stats, dps_type, "Spirit")
                    -- round to 5 sig figs to match bot output
                     level_scale = util_module.round_to_sig_fig(level_scale, 5)
                     level_scale = util_module.round_to_sig_fig(level_scale, 5)
                     spirit_scale = util_module.round_to_sig_fig(spirit_scale, 5)
                     spirit_scale = util_module.round_to_sig_fig(spirit_scale, 5)
Line 376: Line 415:
                                 end
                                 end
                             else
                             else
                                 -- Compute DPS scaling if direct key is missing/empty
                                 -- Special handling for Shiv alt-fire DPS/SustainedDPS
                                 if attr_key == "DPS" or attr_key == "SustainedDPS" then
                                if hero_entry.key == "hero_shiv" and (attr_key == "DPS" or attr_key == "SustainedDPS") then
                                    local bd_scaling = hero_data_module.get_hero_scaling_data(hero_data, "BulletDamage")
                                    if bd_scaling and next(bd_scaling) ~= nil then
                                        local alt_stats = hero_data.Weapon.AltFire
                                        local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                                        for val, stype in pairs(bd_scaling) do
                                            local component = { BulletDamage = val }
                                            local delta = compute_dps_scaling_from_components(alt_stats, dps_type, component)
                                            if delta ~= 0 then
                                                stats_with_any_scaling[attr_key] = true
                                                -- assume if only one type, it's not both
                                                if stype == "Level" then
                                                    -- could still be both if both level and spirit scaling exist, but Shiv doesn't have spirit
                                                end
                                            end
                                        end
                                    end
                                -- General DPS computation for other heroes
                                 elseif attr_key == "DPS" or attr_key == "SustainedDPS" then
                                     local alt_stats = hero_data.Weapon.AltFire
                                     local alt_stats = hero_data.Weapon.AltFire
                                     local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                                     local dps_type = attr_key == "DPS" and 'burst' or 'sustained'