Editing Module:Sandbox/LVL

Jump to navigation Jump to search
Warning: You are not logged in. Once you make an edit, a temporary account will be created for you. Learn more. Log in or create an account to continue receiving notifications after this account expires, and to access other features.
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 1: Line 1:
local p = {}
local p = {}
local lang = require "Module:Lang"
local util_module = require "Module:Utilities"
local data = mw.loadJsonData("Data:Monster_Domosed/ItemData.json")


local heroes_data      = mw.loadJsonData("Data:HeroData.json")
-- Get raw value, CSS class, and LocTokenOverride for a property
local attributes_data  = mw.loadJsonData("Data:AttributeData.json")
local function get_raw_value(item_name, prop)
local attribute_orders  = mw.loadJsonData("Data:StatInfoboxOrder.json")
    local item = data[item_name] or nil
local util_module      = require('Module:Utilities')
    if not item then
local lang_module      = require('Module:Lang')
        for k,v in pairs(data) do
local dictionary_module = require('Module:Dictionary')
            if v.Name == item_name then item = v; break end
local attribute_module  = require('Module:AttributeData')
        end
local hero_data_module  = require('Module:HeroData')
    end
    if not item then return nil, nil, nil end
 
    local raw_prop = item[prop]
    if type(raw_prop) == "table" then
        local loc_override = raw_prop.LocTokenOverride or prop
        return raw_prop.Value, raw_prop.CSSClass, loc_override
    else
        return raw_prop, nil, prop
    end
end


local function get_nested_stat_value(hero_data, stat_key)
-- Check if property exists in language data
    if hero_data[stat_key] ~= nil then
local function is_property_in_lang_data(prop)
         return hero_data[stat_key]
    local suffixes = {"_label", "_Label", "_prefix", "_Prefix", "_postfix", "_Postfix", "_postvalue_label", "_Postvalue_Label"}
     elseif hero_data.Weapon and hero_data.Weapon[stat_key] ~= nil then
    for _, suffix in ipairs(suffixes) do
         return hero_data.Weapon[stat_key]
        if lang.get_string(prop .. suffix) then
     elseif hero_data.Weapon and hero_data.Weapon.AltFire and hero_data.Weapon.AltFire[stat_key] ~= nil then
            return true
        return hero_data.Weapon.AltFire[stat_key]
         end
    end
    return false
end
 
-- Get item by name, ignoring disabled if possible
local function get_json_item(name)
     for _, v in pairs(data) do
        if v.Name == name and (v.IsDisabled == false or v.IsDisabled == nil) then
            return v
         end
    end
     for _, v in pairs(data) do
        if v.Name == name then return v end
     end
     end
     return 0
     return nil
end
end


local function localize(key, fallback)
-- Helper function: get first non-nil string from variants
     local result = lang_module.get_string(key)
local function first_non_nil(...)
    if result == "" or result == nil then
     for i = 1, select("#", ...) do
         result = util_module.add_space_before_cap(fallback) ..
         local v = select(i, ...)
                mw.getCurrentFrame():expandTemplate{title = "MissingValveTranslationTooltip"}
        if v and v ~= "" then return v end
     end
     end
     return result
     return ""
end
 
-- Helper: get lang string with case fallback
local function get_lang_with_fallback(base_key, suffix1, suffix2)
    return first_non_nil(
        lang.get_string(base_key .. suffix1),
        lang.get_string(base_key .. suffix2)
    )
end
end


-- =========================================================================
-- Enhanced stat wrapper - prefix and value get enhanced styling, postfix/label don't
--  DPS calculation helpers
local function format_stat_enhanced(item_name, prop, skip_label)
-- =========================================================================
skip_label = skip_label or false
local value, css, loc_key = get_raw_value(item_name, prop)
if value == nil or value == 0 or value == "" then
return nil
end


local function calculate_dps(stats, dps_type)
local numeric_val = tonumber(tostring(value):match("^-?%d+%.?%d*")) or 0
    local rps = stats.RoundsPerSecond or 0
if numeric_val == 0 then
    if rps == 0 then return 0 end
return nil
end


    local bullets_per_shot = (stats.HitOnceAcrossAllBullets and 1) or (stats.BulletsPerShot or 1)
local label_key = loc_key:gsub("^#", "")
    local burst_count = stats.BulletsPerBurst or 1
    local cycle_time = 1 / rps
    local total_cycle_time = cycle_time * burst_count


    if total_cycle_time == 0 then return 0 end
if not (
lang.get_string(label_key.."_label") or
lang.get_string(label_key.."_Label") or
lang.get_string(label_key.."_prefix") or
lang.get_string(label_key.."_Prefix") or
lang.get_string(label_key.."_postfix") or
lang.get_string(label_key.."_Postfix") or
lang.get_string(label_key.."_postvalue_label") or
lang.get_string(label_key.."_Postvalue_Label")
) then
return nil
end


    local base_damage = stats.BulletDamage or 0
if prop == "BuildUpPerShot" then label_key = "BuildupPerShot"
    if dps_type == 'burst' then
elseif prop == "DotDuration" then label_key = "DOTDuration"
        return base_damage * bullets_per_shot * burst_count / total_cycle_time
end
    end
 
local label = skip_label and "" or get_lang_with_fallback(label_key, "_label", "_Label")
local prefix = get_lang_with_fallback(label_key, "_prefix", "_Prefix")
local postfix = get_lang_with_fallback(label_key, "_postfix", "_Postfix")
 
if prefix == "" and postfix == "" and label == "" then
return nil
end
 
--------------------------------------------------
-- VALUE (Enhanced) with PREFIX and POSTFIX/SUFFIX
--------------------------------------------------
local val_str
local suffix = postfix
if css == "move_speed" or css == "sprint_speed" then
if not postfix:find("%%") then
suffix = postfix .. "/s"
end
end
 
if prefix ~= "" then
if prefix == "{s:sign}" then
local num = tonumber(tostring(value):match("^-?%d+%.?%d*"))
if num and num >= 0 then
prefix = "+"
else
prefix = ""
end
end
if suffix ~= "" and suffix ~= "%" then
val_str = string.format(
"{{#invoke:Enhanced|render|%s|%s|prefix=%s|suffix=%s|noicon=true}}",
item_name,
prop,
prefix,
suffix
)
else
val_str = string.format(
"{{#invoke:Enhanced|render|%s|%s|prefix=%s|noicon=true}}",
item_name,
prop,
prefix
)
end
else
if suffix ~= "" and suffix ~= "%" then
val_str = string.format(
"{{#invoke:Enhanced|render|%s|%s|suffix=%s|noicon=true}}",
item_name,
prop,
suffix
)
else
val_str = string.format(
"{{#invoke:Enhanced|render|%s|%s|noicon=true}}",
item_name,
prop
)
end
end
 
--------------------------------------------------
-- Postfix (UNSTYLED)
--------------------------------------------------
if postfix ~= "" and postfix ~= "%" and not val_str:find(postfix, 1, true) then
if postfix == "HP/s" then
val_str = val_str .. " " .. postfix
else
val_str = val_str .. postfix
end
end
 
--------------------------------------------------
-- Label (UNSTYLED - only if not skipped)
--------------------------------------------------
if label ~= "" then
val_str = val_str .. " " .. label
end
 
--------------------------------------------------
-- Conditional marker
--------------------------------------------------
local item = get_json_item(item_name)
local raw_prop = item[prop]
 
if type(raw_prop) == "table"
and (raw_prop.UsageFlags == "ConditionallyApplied"
or raw_prop.UsageFlags == "ConditionallyEnemyApplied")
then
local conditional_text = lang.get_string("Citadel_Shop_ConditionalAttribute")
val_str = val_str .. string.format(
' <span style="color:#C0C0C0">(%s)</span>',
conditional_text
)
end


    local clip_size = stats.ClipSize or 0
return val_str
    if clip_size <= 0 then
end
        return base_damage * bullets_per_shot * burst_count / total_cycle_time
    end


    local reload_time
-- Special formatting for StatusEffect properties (enhanced variant)
     if stats.ReloadSingle then
local function format_property_for_infobox_enhanced(item_name, prop)
         reload_time = (stats.ReloadTime or 0) * clip_size
     if prop:match("^StatusEffect") then
         return string.format(
            "[[{{#invoke:Lang|get_string|Citadel_%s}}]] [[{{#invoke:Lang|get_string|Citadel_StatusEffect}}]]",
            prop
        )
     else
     else
         reload_time = stats.ReloadTime or 0
         return format_stat_enhanced(item_name, prop)
     end
     end
    reload_time = reload_time + (stats.ReloadDelay or 0)
    local time_to_empty_clip = (clip_size / burst_count) * total_cycle_time
    local damage_from_clip = base_damage * bullets_per_shot * clip_size
    local total_time = time_to_empty_clip + reload_time
    if total_time == 0 then return 0 end
    return damage_from_clip / total_time
end
end


-- Compute how much DPS changes when all relevant component scalings are applied.
-- Returns items that build from this item, sorted by tier
-- Used for alt-fire rows where explicit DPS/SustainedDPS scaling keys are missing.
local function get_component_of_sorted(item_input)
local function compute_dps_scaling(hero_data, base_stats, dps_type, scaling_type)
     local target_key = nil
     local scaling_source = hero_data[scaling_type .. "Scaling"] or {}
    if data[item_input] then
 
        target_key = item_input
    local component_scalings = {}
     else
     local possible_keys = {
         for k, v in pairs(data) do
        "BulletDamage", "RoundsPerSecond", "ClipSize", "ReloadTime",
            if v.Name == item_input and (v.IsDisabled == false or v.IsDisabled == nil) then
         "ReloadDelay", "BulletsPerShot", "BulletsPerBurst"
                target_key = k; break
    }
             end
    for _, key in ipairs(possible_keys) do
        local alt_key = key .. "AltFire"
        if scaling_source[alt_key] then
             component_scalings[key] = scaling_source[alt_key]
         end
         end
     end
     end
    if not target_key then return {} end


     -- ClipSize is shared between fire modes; fall back to primary scaling
     local parents = {}
     if base_stats.ClipSize and not component_scalings.ClipSize then
     for key, item in pairs(data) do
        local primary_clip_scale = scaling_source["ClipSize"]
        if item.IsDisabled ~= true and item.Components and type(item.Components) == "table" then
        if primary_clip_scale then
            for _, comp in ipairs(item.Components) do
             component_scalings.ClipSize = primary_clip_scale
                if comp == target_key then
                    table.insert(parents, key)
                    break
                end
             end
         end
         end
     end
     end


     if next(component_scalings) == nil then
     table.sort(parents, function(a, b)
         return 0
        local tier_a = tonumber(data[a] and data[a]["Tier"]) or 0
     end
        local tier_b = tonumber(data[b] and data[b]["Tier"]) or 0
         return tier_a < tier_b
     end)


    local base_dps = calculate_dps(base_stats, dps_type)
     return parents
    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
end


-- =========================================================================
-- Format stat value for Infobox, handling prefix/postfix, CSS, scaling, and conditional
-- Main module function
-- allow_conditional should be true for ImportantProperties, false for regular Properties
-- =========================================================================
local function format_stat(item_name, prop, skip_label, debug, allow_conditional, enhanced)
if allow_conditional == nil then allow_conditional = true end
 
if prop == "AbilityCooldown" and not skip_label then
return nil
end
 
local value, css, loc_key = get_raw_value(item_name, prop)
if value == nil or value == 0 or value == "" then
return nil
end
 
local numeric_val = tonumber(tostring(value):match("^-?%d+%.?%d*")) or 0
if numeric_val == 0 then
return nil
end
 
local label_key = loc_key:gsub("^#", "")
 
if not (
lang.get_string(label_key.."_label") or
lang.get_string(label_key.."_Label") or
lang.get_string(label_key.."_prefix") or
lang.get_string(label_key.."_Prefix") or
lang.get_string(label_key.."_postfix") or
lang.get_string(label_key.."_Postfix") or
lang.get_string(label_key.."_postvalue_label") or
lang.get_string(label_key.."_Postvalue_Label")
) then
return nil
end
 
if prop == "BuildUpPerShot" then label_key = "BuildupPerShot"
elseif prop == "DotDuration" then label_key = "DOTDuration"
end
 
local label = skip_label and "" or get_lang_with_fallback(label_key, "_label", "_Label")
local prefix = get_lang_with_fallback(label_key, "_prefix", "_Prefix")
local postfix = get_lang_with_fallback(label_key, "_postfix", "_Postfix")
 
if prefix == "" and postfix == "" and label == "" then
return nil
end
 
--------------------------------------------------
-- VALUE (normal or enhanced)
--------------------------------------------------
local val_str
if enhanced then
val_str = string.format(
"{{#invoke:Enhanced|render|%s|%s}}",
item_name,
prop
)
else
val_str = tostring(value)
end
 
--------------------------------------------------
-- Prefix (UNSTYLED - skipped when enhanced, handled internally by Enhanced module)
--------------------------------------------------
if not enhanced then
if prefix ~= "" then
if prefix == "{s:sign}" then
local num = tonumber(val_str:match("^-?%d+%.?%d*"))
if num and num >= 0 and not val_str:find("^%+") then
val_str = "+" .. val_str
end
else
val_str = prefix .. val_str
end
end
end
 
--------------------------------------------------
-- Postfix (UNSTYLED - skipped when enhanced, handled internally by Enhanced module)
--------------------------------------------------
if not enhanced then
if postfix ~= "" and not val_str:find(postfix, 1, true) then
if postfix == "HP/s" then
val_str = val_str .. " " .. postfix
else
val_str = val_str .. postfix
end
end
end
 
--------------------------------------------------
-- Move speed/Sprint speed postfix (skipped when enhanced)
--------------------------------------------------
if not enhanced then
if css == "move_speed" or css == "sprint_speed" then
if postfix == "" or not postfix:find("%%") then
val_str = val_str:gsub("/s", "") .. "/s"
end
local num = tonumber(val_str:match("^-?%d+%.?%d*"))
if num and num > 0 and not val_str:find("^%+") then
val_str = "+" .. val_str
end
end
end
 
--------------------------------------------------
-- Append scaling template {{Boon}} or {{Ss}}
--------------------------------------------------
local item = get_json_item(item_name)
local raw_prop = item[prop]
if type(raw_prop) == "table" and raw_prop.Scale and raw_prop.Scale.Value ~= 0 then
local scale_val = util_module.round_to_sig_fig(raw_prop.Scale.Value, 2)
local template_name = (raw_prop.Scale.Type == "power_increase") and "Boon" or "Ss"
val_str = val_str .. " " .. mw.getCurrentFrame():expandTemplate{ title = template_name, args = { scale_val } }
end
 
--------------------------------------------------
-- Label (UNSTYLED)
--------------------------------------------------
if label ~= "" then
val_str = val_str .. " " .. label
end
 
--------------------------------------------------
-- Conditional marker
--------------------------------------------------
if allow_conditional
and type(raw_prop) == "table"
and (raw_prop.UsageFlags == "ConditionallyApplied"
or raw_prop.UsageFlags == "ConditionallyEnemyApplied")
then
local conditional_text = lang.get_string("Citadel_Shop_ConditionalAttribute")
val_str = val_str .. string.format(
' <span style="color:#C0C0C0">(%s)</span>',
conditional_text
)
end
 
return val_str
end


p.write_hero_comparison_table = function(frame)
-- Special formatting for StatusEffect properties
     local power_increases = tonumber(frame.args[1]) or 0
local function format_property_for_infobox(item_name, prop, skip_label, debug)
    local spirit_power    = tonumber(frame.args[2]) or 0
     if prop:match("^StatusEffect") then
     local max_power      = tonumber(frame.args[3]) or 25
        return string.format(
     local max_spirit      = tonumber(frame.args[4]) or 500
            "[[{{#invoke:Lang|get_string|Citadel_%s}}]] [[{{#invoke:Lang|get_string|Citadel_StatusEffect}}]]",
            prop
        )
     else
        return format_stat(item_name, prop, skip_label, debug, true)
     end
end


     local display_scaling_icons = (power_increases == 0 and spirit_power == 0)
-- Build raw wikitext for the default tooltip infobox (no preprocess)
local function build_tooltip(frame)
     local item_name = frame.args[1]
    if not item_name then return "Item name missing", 0 end


     local body_str = ""
     local debug = tonumber(frame.args.debug) or 0
    local item = get_json_item(item_name)
    if not item then return "Item not found", debug end


     local stats_to_include = {
     local ItemData = require("Module:ItemData")
        Weapon = {
    local lines = {
            "DPS", "SustainedDPS", "BulletDamage", "RoundsPerSecond", "FireRate",
        "{{Infobox item",
            "ClipSize", "ReloadTime", "ReloadDelay", "ReloadSingle", "BulletsPerShot", "BulletsPerBurst",
        "| item_name = " .. item_name
            "BurstInterShotInterval", "LightMeleeDamage", "HeavyMeleeDamage",
            "BulletSpeed", "FalloffStartRange", "FalloffEndRange",
            "CritDamageBonusPercent", "BulletRadius", "RoundsPerSecondAtMaxSpin", "SpinAcceleration", "SpinDeceleration"
        },
        Vitality = {
            "MaxHealth", "BaseHealthRegen", "BulletResist", "TechResist", "MeleeResist",
            "CritDamageReceivedPercent", "DebuffResist", "BulletLifesteal", "MaxMoveSpeed",
            "SprintSpeed", "StaminaCooldown", "Stamina", "GroundDashSpeed", "GravityChange"
        },
        Spirit = { "TechPower" }
     }
     }


     -- Collect and sort heroes alphabetically, excluding disabled/in-development
     if item.StreetBrawl == true then
     local sorted_heroes = {}
        table.insert(lines, "| street_brawl = true")
    for hero_key, hero_data in pairs(heroes_data) do
    end
        if not hero_data["InDevelopment"] and not hero_data["IsDisabled"] then
 
             table.insert(sorted_heroes, {key = hero_key, data = hero_data})
    ----------------------------------------------------------------------
    -- Check if AbilityCooldown or AbilityChargeUpTime exists in sections
    ----------------------------------------------------------------------
     local cooldown_found_in_sections = false
    local chargeup_found_in_sections = false
    for _, section in ipairs(item.TooltipSections or {}) do
        for _, entry in ipairs(section.Entries or {}) do
            for _, prop in ipairs(entry.Properties or {}) do
                if prop == "AbilityCooldown" then cooldown_found_in_sections = true end
                if prop == "AbilityChargeUpTime" then chargeup_found_in_sections = true end
             end
            for _, prop in ipairs(entry.ImportantProperties or {}) do
                if prop == "AbilityCooldown" then cooldown_found_in_sections = true end
                if prop == "AbilityChargeUpTime" then chargeup_found_in_sections = true end
            end
         end
         end
     end
     end
     table.sort(sorted_heroes, function(a, b)
 
        local function get_sort_name(name)
    ----------------------------------------------------------------------
            return (name:gsub("^The ", ""))
     -- Innate stats
    ----------------------------------------------------------------------
    local innate_index = 1
    for _, section in ipairs(item.TooltipSections or {}) do
        if section.Type == "EArea_Innate" then
            for _, entry in ipairs(section.Entries or {}) do
                for _, prop in ipairs(entry.Properties or {}) do
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime" then
                        local val = format_stat(item_name, prop, false, debug, false)
                        if val then
                            table.insert(lines, string.format("| item_stat%d = %s", innate_index, val))
                            innate_index = innate_index + 1
                        end
                    end
                end
            end
         end
         end
         return get_sort_name(a.data["Name"] or a.key) < get_sort_name(b.data["Name"] or b.key)
    end
    end)
 
    ----------------------------------------------------------------------
    -- Passive stats
    ----------------------------------------------------------------------
    local passive_counter = 1
    for _, section in ipairs(item.TooltipSections or {}) do
         if section.Type ~= "EArea_Innate" and section.Type ~= "EArea_Active" then
            for _, entry in ipairs(section.Entries or {}) do
                local loc = entry.LocString and entry.LocString:gsub("#","") or nil
                if loc then
                    table.insert(lines, string.format(
                        "| passive%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}",
                        passive_counter, loc, item_name
                    ))
                end
 
                local stat_index = 1
                local seen_props = {}
 
                for _, prop in ipairs(entry.ImportantProperties or {}) do
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
                      and is_property_in_lang_data(prop) and not seen_props[prop] then
                        local val = format_stat(item_name, prop, false, debug, true)
                        if val then
                            table.insert(lines, string.format("| passive%d_stat%d = %s", passive_counter, stat_index, val))
                            stat_index = stat_index + 1
                            seen_props[prop] = true
                        end
                    end
                end
 
                for _, prop in ipairs(entry.Properties or {}) do
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
                      and is_property_in_lang_data(prop) and not seen_props[prop] then
                        local val = format_stat(item_name, prop, false, debug, false)
                        if val then
                            table.insert(lines, string.format("| passive%d_stat%d = %s", passive_counter, stat_index, val))
                            stat_index = stat_index + 1
                            seen_props[prop] = true
                        end
                    end
                end


    -- Build a single stat cell (<td>) with data attributes for JS recalculation
                for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do
    local function buildStatCell(hero_data, hero_key, attr_key, is_alt_fire, category)
                    for _, prop in ipairs(prop_list) do
        -- Conversion factors for stats that need unit changes (e.g. metres → centimetres)
                        if prop == "AbilityCooldown" then
        local unit_conversion = {
                            local cd = format_stat(item_name, prop, true, debug)
            BulletRadius = 100  -- metres to centimetres
                            if cd then table.insert(lines, string.format("| passive%d_cooldown = %s", passive_counter, cd)) end
        }
                        end
        local conv_factor = unit_conversion[attr_key] or 1
                        if prop == "AbilityChargeUpTime" then
                            local cu = format_stat(item_name, prop, true, debug)
                            if cu then table.insert(lines, string.format("| passive%d_chargeup = %s", passive_counter, cu)) end
                        end
                    end
                end


        local base_value
                 passive_counter = passive_counter + 1
        if is_alt_fire then
            if attr_key == "ClipSize" or attr_key == "ReloadTime" then
                base_value = get_nested_stat_value(hero_data, attr_key)
            elseif hero_data.Weapon and hero_data.Weapon.AltFire
                and hero_data.Weapon.AltFire[attr_key] ~= nil then
                base_value = hero_data.Weapon.AltFire[attr_key]
            else
                 base_value = get_nested_stat_value(hero_data, attr_key)
             end
             end
        else
            base_value = get_nested_stat_value(hero_data, attr_key)
         end
         end
    end


        -- Apply unit conversion to the base value
    ----------------------------------------------------------------------
         if type(base_value) == "number" then
    -- Active stats
             base_value = base_value * conv_factor
    ----------------------------------------------------------------------
        end
    local active_index = 1
    for _, section in ipairs(item.TooltipSections or {}) do
         if section.Type == "EArea_Active" then
             for _, entry in ipairs(section.Entries or {}) do
                if entry.LocString then
                    table.insert(lines, string.format(
                        "| active%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}",
                        active_index, entry.LocString:gsub("#",""), item_name
                    ))
                end


        local stat_value = base_value
                local stat_index = 1
                local seen_props = {}


        -- Scaling lookup
                 for _, prop in ipairs(entry.ImportantProperties or {}) do
        local scaling_data
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
        if is_alt_fire then
                      and is_property_in_lang_data(prop) and not seen_props[prop] then
            if attr_key == "ClipSize" or attr_key == "ReloadTime" then
                        local val = format_stat(item_name, prop, false, debug, true)
                 scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key)
                        if val then
            elseif (attr_key == "DPS" or attr_key == "SustainedDPS")
                            table.insert(lines, string.format("| active%d_stat%d = %s", active_index, stat_index, val))
                and hero_data.Weapon and hero_data.Weapon.AltFire then
                            stat_index = stat_index + 1
                -- Alt-fire DPS/SustainedDPS: use explicit key if present, otherwise compute from components
                            seen_props[prop] = true
                local direct_scaling = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire")
                        end
                if direct_scaling and next(direct_scaling) ~= nil then
                     end
                    scaling_data = direct_scaling
                else
                    local alt_stats = hero_data.Weapon.AltFire
                    local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                    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")
                    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
                 end
                 end
            elseif hero_data.Weapon and hero_data.Weapon.AltFire
                and hero_data.Weapon.AltFire[attr_key] ~= nil then
                scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire")
            else
                scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key)
            end
        else
            scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key)
        end


        local scaling_strs = ""
                for _, prop in ipairs(entry.Properties or {}) do
        local spirit_scale = 0
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
        local level_scale  = 0
                      and is_property_in_lang_data(prop) and not seen_props[prop] then
        local spirit_val = 0
                        local val = format_stat(item_name, prop, false, debug, false)
        local level_val  = 0
                        if val then
        if scaling_data ~= nil then
                            table.insert(lines, string.format("| active%d_stat%d = %s", active_index, stat_index, val))
            for scaling_val, scaling_type in pairs(scaling_data) do
                            stat_index = stat_index + 1
                if scaling_type == "Spirit" then
                            seen_props[prop] = true
                    spirit_val = scaling_val * conv_factor  -- apply unit conversion
                        end
                elseif scaling_type == "Level" then
                     end
                     level_val = scaling_val * conv_factor  -- apply unit conversion
                 end
                 end
            end


            -- Output Spirit icon before Level icon
                for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do
            local function append_scaling(val, stype)
                    for _, prop in ipairs(prop_list) do
                if val ~= 0 then
                        if prop == "AbilityCooldown" then
                    local scaling_str = hero_data_module.write_scalar_str(val, stype, true)
                            local cd = format_stat(item_name, prop, true, debug)
                    if scaling_str ~= "" then
                            if cd then table.insert(lines, string.format("| active%d_cooldown = %s", active_index, cd)) end
                        scaling_strs = scaling_strs .. " " .. scaling_str
                        end
                        if prop == "AbilityChargeUpTime" then
                            local cu = format_stat(item_name, prop, true, debug)
                            if cu then table.insert(lines, string.format("| active%d_chargeup = %s", active_index, cu)) end
                        end
                     end
                     end
                 end
                 end
            end
            append_scaling(spirit_val, "Spirit")
            append_scaling(level_val, "Level")


            if type(stat_value) == "number" then
                 active_index = active_index + 1
                stat_value = stat_value + (spirit_power * spirit_val)
                 stat_value = stat_value + (power_increases * level_val)
             end
             end
            spirit_scale = spirit_val
            level_scale  = level_val
         end
         end
    end


         if attr_key == "TechPower" and spirit_scale == 0 then
    ----------------------------------------------------------------------
             spirit_scale = 1.0
    -- Global fallback for AbilityCooldown and AbilityChargeUpTime
             if type(stat_value) == "number" then
    -- Attaches to the active section if one exists, otherwise innate
                 stat_value = stat_value + (spirit_power * spirit_scale)
    ----------------------------------------------------------------------
    if not cooldown_found_in_sections and not chargeup_found_in_sections and item.AbilityCooldown then
         local cd = format_stat(item_name, "AbilityCooldown", true, debug)
        if cd then
             local has_active = false
             for _, section in ipairs(item.TooltipSections or {}) do
                if section.Type == "EArea_Active" then has_active = true; break end
            end
            if has_active then
                 table.insert(lines, "| active1_cooldown = " .. cd)
            else
                table.insert(lines, "| item_stat_cooldown = " .. cd)
             end
             end
         end
         end
    end


         local innate_spirit_scale =
    if not chargeup_found_in_sections and item.AbilityChargeUpTime then
            (hero_data["LevelScaling"] and hero_data["LevelScaling"]["TechPower"]) or 0
         local cu = format_stat(item_name, "AbilityChargeUpTime", true, debug)
        if cu then table.insert(lines, "| item_stat_chargeup = " .. cu) end
    end


         if not display_scaling_icons then scaling_strs = "" end
    ----------------------------------------------------------------------
    -- Components
    ----------------------------------------------------------------------
    for i = 1, 2 do
        local comp = ItemData.get_component_name { args = { item_name, i } }
         if comp and comp ~= "" then
            table.insert(lines, string.format("| component%d_name = %s", i, comp))
        end
    end


        if type(stat_value) == "boolean" then
    ----------------------------------------------------------------------
             stat_value = tostring(stat_value)
    -- Is Component Of (sorted by tier)
        else
    ----------------------------------------------------------------------
            stat_value = util_module.round_to_sig_fig(stat_value, 5)
    local parents = get_component_of_sorted(item_name)
    for i, key in ipairs(parents) do
        local parent_name = data[key] and data[key]["Name"] or ""
        if parent_name ~= "" then
             table.insert(lines, string.format("| iscomponentof%d_name = %s", i, parent_name))
         end
         end
    end
    ----------------------------------------------------------------------
    -- Sounds
    ----------------------------------------------------------------------
    for key, value in pairs(frame.args) do
        local index = tostring(key):match("^sound(%d+)$")
        if index and value ~= "" then
            table.insert(lines, string.format("| sound%s = %s", index, value))
        end
    end


        local cell_inner = string.format(
    ----------------------------------------------------------------------
            '<span class="stat-num">%s</span><span class="stat-scaling">%s</span>',
    table.insert(lines, "}}")
            stat_value,
    return table.concat(lines, "\n"), debug
            scaling_strs
end
        )
 
-- Build raw wikitext for the enhanced tooltip infobox (no preprocess)
local function build_tooltip_enhanced(frame)
    local item_name = frame.args[1]
    if not item_name then return "Item name missing" end


        local weapon_table = hero_data.Weapon
    local item = get_json_item(item_name)
        if is_alt_fire and weapon_table and weapon_table.AltFire then
    if not item then return "Item not found" end
            weapon_table = weapon_table.AltFire
        end
        local hit_once = "false"
        if (attr_key == "DPS" or attr_key == "SustainedDPS")
            and weapon_table
            and weapon_table.HitOnceAcrossAllBullets
        then
            hit_once = "true"
        end


        local data_attrs = string.format(
    local ItemData = require("Module:ItemData")
            '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"',
    local lines = {
            attr_key,
        "{{Infobox item",
            tostring(type(base_value) == "number" and util_module.round_to_sig_fig(base_value, 3) or base_value),
        "| item_name = " .. item_name
            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
        )


         return string.format('<td style="white-space: nowrap;" %s>%s</td>', data_attrs, cell_inner)
    if item.StreetBrawl == true then
         table.insert(lines, "| street_brawl = true")
     end
     end


     -- Generate a hero's primary stat row
     ----------------------------------------------------------------------
     local function generatePrimaryRow(hero_data, hero_key, has_alt)
     -- Check if AbilityCooldown or AbilityChargeUpTime exists in sections
        local row_str = ""
    ----------------------------------------------------------------------
        local hero_name_local = localize(hero_key, hero_key)
    local cooldown_found_in_sections = false
        local hero_name_en = hero_data["Name"]
    local chargeup_found_in_sections = false
        local template_args = {[1] = hero_name_en, l1 = hero_name_local}
    for _, section in ipairs(item.TooltipSections or {}) do
        local hero_icon = mw.getCurrentFrame():expandTemplate{ title = "Template:HeroIcon", args = template_args }
         for _, entry in ipairs(section.Entries or {}) do
        local hero_cell_content = hero_icon
             for _, prop in ipairs(entry.Properties or {}) do
        if has_alt then
                if prop == "AbilityCooldown" then cooldown_found_in_sections = true end
            hero_cell_content = '[[#alt-fire-' .. hero_key .. '|+]] ' .. hero_cell_content
                 if prop == "AbilityChargeUpTime" then chargeup_found_in_sections = true end
        end
            end
        local sort_name = (hero_data["Name"] or hero_key):gsub("^The ", "")
            for _, prop in ipairs(entry.ImportantProperties or {}) do
        row_str = row_str .. '<td style="position: sticky; left: 0; z-index: 10; background-color: var(--background-color-base-2); isolation: isolate; overflow: hidden; min-width: 150px;" data-sort-value="' .. sort_name .. '">' .. hero_cell_content .. '</td>'
                if prop == "AbilityCooldown" then cooldown_found_in_sections = true end
         for _, category in ipairs(attribute_orders["category_order"]) do
                 if prop == "AbilityChargeUpTime" then chargeup_found_in_sections = true end
             if stats_to_include[category] ~= nil then
                 for _, attr_key in ipairs(stats_to_include[category]) do
                    row_str = row_str .. buildStatCell(hero_data, hero_key, attr_key, false, category)
                 end
             end
             end
         end
         end
        return "<tr>" .. row_str .. "</tr>"
     end
     end


     -- Generate an expandable alt-fire sub-row (weapon stats only)
     ----------------------------------------------------------------------
     local function generateAltFireSubRow(hero_data, hero_key)
     -- Innate stats (Enhanced)
        local row_str = ""
    ----------------------------------------------------------------------
        local hero_name_local = localize(hero_key, hero_key)
    local innate_index = 1
        local hero_name_en = hero_data["Name"]
    for _, section in ipairs(item.TooltipSections or {}) do
        local template_args = {[1] = hero_name_en, l1 = hero_name_local}
         if section.Type == "EArea_Innate" then
        local hero_icon = mw.getCurrentFrame():expandTemplate{ title = "Template:HeroIcon", args = template_args }
            for _, entry in ipairs(section.Entries or {}) do
        local hero_cell_content = hero_icon .. ' <small style="color:#aaa;">(Alt‑fire)</small>'
                 for _, prop in ipairs(entry.Properties or {}) do
         local sort_name = (hero_data["Name"] or hero_key):gsub("^The ", "")
                     if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
        row_str = row_str .. '<td style="position: sticky; left: 0; z-index: 9; background-color: #202122; isolation: isolate; overflow: hidden; min-width: 150px;" data-sort-value="' .. sort_name .. '">' .. hero_cell_content .. '</td>'
                      and is_property_in_lang_data(prop) then
        for _, category in ipairs(attribute_orders["category_order"]) do
                         local val = format_stat_enhanced(item_name, prop)
            if stats_to_include[category] ~= nil then
                        if val then
                 for _, attr_key in ipairs(stats_to_include[category]) do
                            table.insert(
                     if category == "Weapon" then
                                lines,
                         row_str = row_str .. buildStatCell(hero_data, hero_key, attr_key, true, category)
                                string.format(
                    else
                                    "| item_stat%d = %s",
                         row_str = row_str .. '<td></td>'
                                    innate_index,
                                    val
                                )
                            )
                            innate_index = innate_index + 1
                         end
                     end
                     end
                 end
                 end
             end
             end
         end
         end
        return '<tr class="alt-fire-sub" data-parent="' .. hero_key .. '">' .. row_str .. '</tr>'
     end
     end


     -- Build all body rows
     ----------------------------------------------------------------------
     for _, hero_entry in ipairs(sorted_heroes) do
    -- Passive stats (Enhanced)
         local hero_key  = hero_entry.key
    ----------------------------------------------------------------------
        local hero_data = hero_entry.data
    local passive_counter = 1
        local has_alt = hero_data.Weapon and hero_data.Weapon.AltFire and true or false
     for _, section in ipairs(item.TooltipSections or {}) do
        body_str = body_str .. generatePrimaryRow(hero_data, hero_key, has_alt)
         if section.Type ~= "EArea_Innate" and section.Type ~= "EArea_Active" then
        if has_alt then
            for _, entry in ipairs(section.Entries or {}) do
            body_str = body_str .. generateAltFireSubRow(hero_data, hero_key)
                if entry.LocString then
        end
                    table.insert(lines, string.format(
    end
                        "| passive%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}",
                        passive_counter,
                        entry.LocString:gsub("#",""),
                        item_name
                    ))
                end
 
                local stat_index = 1
                local seen = {}
 
                for _, prop in ipairs(entry.ImportantProperties or {}) do
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
                      and is_property_in_lang_data(prop) and not seen[prop] then
                        local val = format_property_for_infobox_enhanced(item_name, prop)
                        if val then
                            table.insert(
                                lines,
                                string.format(
                                    "| passive%d_stat%d = %s",
                                    passive_counter,
                                    stat_index,
                                    val
                                )
                            )
                            stat_index = stat_index + 1
                            seen[prop] = true
                        end
                    end
                end


    -- Pre-pass: determine which stats have any scaling, and which have both types.
                for _, prop in ipairs(entry.Properties or {}) do
    -- Used to set column widths in the header.
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
    local stats_with_any_scaling  = {}
                      and is_property_in_lang_data(prop) and not seen[prop] then
    local stats_with_both_scaling = {}
                        local val = format_stat_enhanced(item_name, prop)
    for _, hero_entry in ipairs(sorted_heroes) do
                        if val then
        local hero_data = hero_entry.data
                            table.insert(
        for _, category in ipairs(attribute_orders["category_order"]) do
                                lines,
            if stats_to_include[category] ~= nil then
                                string.format(
                for _, attr_key in ipairs(stats_to_include[category]) do
                                    "| passive%d_stat%d = %s",
                    -- Primary scaling
                                    passive_counter,
                    local scaling_data = hero_data_module.get_hero_scaling_data(hero_data, attr_key)
                                    stat_index,
                    if scaling_data ~= nil and next(scaling_data) ~= nil then
                                    val
                        stats_with_any_scaling[attr_key] = true
                                )
                        local has_spirit, has_level = false, false
                            )
                        for _, scaling_type in pairs(scaling_data) do
                             stat_index = stat_index + 1
                             if scaling_type == "Spirit" then has_spirit = true
                             seen[prop] = true
                             elseif scaling_type == "Level" then has_level = true end
                         end
                         end
                        if has_spirit and has_level then stats_with_both_scaling[attr_key] = true end
                     end
                     end
                end


                    -- Alt-fire scaling (only for stats present in the AltFire table)
                for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do
                    if hero_data.Weapon
                     for _, prop in ipairs(prop_list) do
                        and hero_data.Weapon.AltFire
                         if prop == "AbilityCooldown" then
                        and hero_data.Weapon.AltFire[attr_key] ~= nil
                             local cd = format_stat_enhanced(item_name, prop, true)
                     then
                             if cd then
                         if attr_key ~= "ClipSize" then
                                 table.insert(lines, string.format("| passive%d_cooldown = %s", passive_counter, cd))
                             local alt_scaling = hero_data_module.get_hero_scaling_data(hero_data, attr_key .. "AltFire")
                            end
                             if alt_scaling and next(alt_scaling) ~= nil then
                        end
                                 stats_with_any_scaling[attr_key] = true
                        if prop == "AbilityChargeUpTime" then
                                local has_spirit_alt, has_level_alt = false, false
                            local cu = format_stat_enhanced(item_name, prop, true)
                                for _, stype in pairs(alt_scaling) do
                            if cu then
                                    if stype == "Spirit" then has_spirit_alt = true
                                table.insert(lines, string.format("| passive%d_chargeup = %s", passive_counter, cu))
                                    elseif stype == "Level" then has_level_alt = true end
                                end
                                if has_spirit_alt and has_level_alt then stats_with_both_scaling[attr_key] = true end
                            else
                                if attr_key == "DPS" or attr_key == "SustainedDPS" then
                                    local alt_stats = hero_data.Weapon.AltFire
                                    local dps_type = attr_key == "DPS" and 'burst' or 'sustained'
                                    local lv = compute_dps_scaling(hero_data, alt_stats, dps_type, "Level")
                                    local sp = compute_dps_scaling(hero_data, alt_stats, dps_type, "Spirit")
                                    lv = util_module.round_to_sig_fig(lv, 5)
                                    sp = util_module.round_to_sig_fig(sp, 5)
                                    if lv ~= 0 or sp ~= 0 then
                                        stats_with_any_scaling[attr_key] = true
                                        if lv ~= 0 and sp ~= 0 then
                                            stats_with_both_scaling[attr_key] = true
                                        end
                                    end
                                end
                             end
                             end
                         end
                         end
                     end
                     end
                 end
                 end
                passive_counter = passive_counter + 1
             end
             end
         end
         end
     end
     end


     -- Build table header row
     ----------------------------------------------------------------------
     local headers_str = '<th style="position: sticky; left: 0; top: -1px; z-index: 12; background-color: var(--background-color-base-5); isolation: isolate; min-width: 150px;">Hero</th>'
    -- Active stats (Enhanced)
     local category_data = attribute_module.get_category_data()
     ----------------------------------------------------------------------
     local postfix_key_map = {
     local active_index = 1
         ["ReloadDelay"] = "StatDesc_ReloadTime_postfix",
     for _, section in ipairs(item.TooltipSections or {}) do
        ["BulletsPerShot"] = "",
         if section.Type == "EArea_Active" then
        ["BulletsPerBurst"] = "",
            for _, entry in ipairs(section.Entries or {}) do
        ["BurstInterShotInterval"] = "StatDesc_ReloadTime_postfix",
                if entry.LocString then
        ["ReloadSingle"] = "",
                    table.insert(lines, string.format(
        ["BonusAttackRange"] = "StatDesc_WeaponRangeFalloffMax_postfix",
                        "| active%d_description = {{#invoke:Lang|get_string|%s|item_name=%s}}",
        ["SustainedDPS"] = "DPS_postfix",
                        active_index,
        ["RoundsPerSecondAtMaxSpin"] = "",
                        entry.LocString:gsub("#",""),
        ["CritDamageBonusPercent"]  = "StatDesc_CritDamageBonusScale_postfix",
                        item_name
["CritDamageReceivedPercent"] = "StatDesc_CritDamageReceivedScale_postfix",
                    ))
        ["BulletLifesteal"] = "BulletLifestealPercentHero_postfix",
                end
        ["GroundDashSpeed"] = "DashSpeed_postfix"
 
    }
                local stat_index = 1
    for _, category in ipairs(attribute_orders["category_order"]) do
                local seen = {}
        local category_attrs = attributes_data[category]
 
        local category_rgb  = category_data[category]["rgb"]
                for _, prop in ipairs(entry.ImportantProperties or {}) do
        if stats_to_include[category] ~= nil then
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
            for _, attr_key in ipairs(stats_to_include[category]) do
                      and is_property_in_lang_data(prop) and not seen[prop] then
                local attr_data = category_attrs[attr_key]
                        local val = format_property_for_infobox_enhanced(item_name, prop)
               
                        if val then
                -- Fallback: if the stat is missing from this category, check all other categories
                            table.insert(
                if attr_data == nil then
                                lines,
                    for _, fallback_cat_data in pairs(attributes_data) do
                                string.format(
                        if fallback_cat_data[attr_key] ~= nil then
                                    "| active%d_stat%d = %s",
                             attr_data = fallback_cat_data[attr_key]
                                    active_index,
                            break
                                    stat_index,
                                    val
                                )
                            )
                            stat_index = stat_index + 1
                             seen[prop] = true
                         end
                         end
                     end
                     end
                 end
                 end


                 local attr_localized, postfix
                 for _, prop in ipairs(entry.Properties or {}) do
                if attr_data ~= nil then
                    if prop ~= "AbilityCooldown" and prop ~= "AbilityChargeUpTime"
                    attr_localized = lang_module.get_string(attr_data["label"])
                      and is_property_in_lang_data(prop) and not seen[prop] then
                    if attr_localized == nil or attr_localized == "" then
                        local val = format_stat_enhanced(item_name, prop)
                         attr_localized = util_module.add_space_before_cap(attr_key)
                        if val then
                            table.insert(
                                lines,
                                string.format(
                                    "| active%d_stat%d = %s",
                                    active_index,
                                    stat_index,
                                    val
                                )
                            )
                            stat_index = stat_index + 1
                            seen[prop] = true
                         end
                     end
                     end
                    postfix = lang_module.get_string(attr_data["postfix"])
                    if postfix == nil or postfix == "" then postfix = ""
                    else postfix = " (" .. postfix .. ")" end
                    if attr_key == "BulletRadius" then postfix = " (cm)" end
                else
                    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
                 end
                 local th_style = 'position: sticky; top: -1px; z-index: 3; background-color: rgb(' .. category_rgb .. ');'
 
                if stats_with_both_scaling[attr_key] then
                 for _, prop_list in ipairs({entry.Properties or {}, entry.ImportantProperties or {}}) do
                    th_style = th_style .. ' min-width: 130px;'
                    for _, prop in ipairs(prop_list) do
                elseif stats_with_any_scaling[attr_key] then
                        if prop == "AbilityCooldown" then
                    th_style = th_style .. ' min-width: 75px;'
                            local cd = format_stat_enhanced(item_name, prop, true)
                            if cd then
                                table.insert(lines, string.format("| active%d_cooldown = %s", active_index, cd))
                            end
                        end
                        if prop == "AbilityChargeUpTime" then
                            local cu = format_stat_enhanced(item_name, prop, true)
                            if cu then
                                table.insert(lines, string.format("| active%d_chargeup = %s", active_index, cu))
                            end
                        end
                    end
                 end
                 end
                 headers_str = headers_str .. '<th style="' .. th_style .. '">' .. attr_localized .. postfix .. "</th>"
 
                 active_index = active_index + 1
             end
             end
         end
         end
     end
     end
    headers_str = "<tr>" .. headers_str .. "</tr>"


     return string.format(
     ----------------------------------------------------------------------
         '<div id="hero-comparison-container" data-max-power="%s" data-max-spirit="%s">' ..
    -- Global fallback for AbilityCooldown and AbilityChargeUpTime
         '<div style="overflow: auto; max-height: 70vh; width: 100%%;">' ..
    -- Attaches to the active section if one exists, otherwise innate
         '<table class="wikitable sortable" style="table-layout: auto; width: 100%%;" id="hero-comparison-table">%s%s</table>' ..
    ----------------------------------------------------------------------
         '</div></div>',
    if not cooldown_found_in_sections and not chargeup_found_in_sections and item.AbilityCooldown then
        max_power, max_spirit, headers_str, body_str
        local cd = format_stat_enhanced(item_name, "AbilityCooldown", true)
     )
        if cd then
            local has_active = false
            for _, section in ipairs(item.TooltipSections or {}) do
                if section.Type == "EArea_Active" then has_active = true; break end
            end
            if has_active then
                table.insert(lines, "| active1_cooldown = " .. cd)
            else
                table.insert(lines, "| item_stat_cooldown = " .. cd)
            end
        end
    end
 
    if not chargeup_found_in_sections and item.AbilityChargeUpTime then
        local cu = format_stat_enhanced(item_name, "AbilityChargeUpTime", true)
         if cu then table.insert(lines, "| item_stat_chargeup = " .. cu) end
    end
 
    ----------------------------------------------------------------------
    -- Components
    ----------------------------------------------------------------------
    for i = 1, 2 do
        local comp = ItemData.get_component_name { args = { item_name, i } }
        if comp and comp ~= "" then
            table.insert(lines, string.format("| component%d_name = %s", i, comp))
        end
    end
 
    ----------------------------------------------------------------------
    -- Is Component Of (sorted by tier)
    ----------------------------------------------------------------------
    local parents = get_component_of_sorted(item_name)
    for i, key in ipairs(parents) do
        local parent_name = data[key] and data[key]["Name"] or ""
         if parent_name ~= "" then
            table.insert(lines, string.format("| iscomponentof%d_name = %s", i, parent_name))
        end
    end
 
    ----------------------------------------------------------------------
    -- Sounds
    ----------------------------------------------------------------------
    for key, value in pairs(frame.args) do
         local index = tostring(key):match("^sound(%d+)$")
        if index and value ~= "" then
            table.insert(lines, string.format("| sound%s = %s", index, value))
        end
    end
 
    ----------------------------------------------------------------------
    table.insert(lines, "}}")
    return table.concat(lines, "\n")
end
 
function p.fill_tooltip(frame)
    local output, debug = build_tooltip(frame)
    if debug == 1 or debug == 2 then
         return output
    end
    return mw.getCurrentFrame():preprocess(output)
end
 
function p.fill_tooltip_enhanced(frame)
    return mw.getCurrentFrame():preprocess(build_tooltip_enhanced(frame))
end
 
function p.render(frame)
    local output_lines = { "<tabber>" }
    table.insert(output_lines, "Default=")
    local tooltip, _ = build_tooltip(frame)
    table.insert(output_lines, tooltip)
    table.insert(output_lines, "|-|")
    table.insert(output_lines, "Enhanced=")
    table.insert(output_lines, build_tooltip_enhanced(frame))
    table.insert(output_lines, "</tabber>")
     return mw.getCurrentFrame():preprocess(table.concat(output_lines, "\n"))
end
end


return p
return p
Please note that all contributions to The Deadlock Wiki are considered to be released under the Creative Commons Attribution-NonCommercial-ShareAlike (see Deadlock:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

Page included on this page: