Module:Sandbox

From The Deadlock Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Sandbox/doc

local p = {};
local data = mw.loadJsonData("Data:ItemData.json")

-- Utility function to safely convert to number
local function toNumber(value)
    if type(value) == "number" then
        return value
    elseif type(value) == "string" then
        -- Remove any formatting (commas, etc.) before conversion
        local cleaned = value:gsub("[^%d.-]", "")
        return tonumber(cleaned) or 0
    end
    return 0
end

-- Normalize strings for comparison
local function normalize(str)
    return mw.ustring.lower(mw.text.trim(str or ""))
end

-- Safe item access
local function get_json_item(name)
    if not name or type(name) ~= 'string' then return nil end
    local targetName = normalize(name)
    
    for _, item in pairs(data) do
        if item.Name and normalize(item.Name) == targetName then
            if item.IsDisabled ~= true then
                return item
            end
        end
    end
    return nil
end

-- Format numbers with localization
local lang = mw.language.getContentLanguage()
local function formatNum(amount)
    return lang:formatNum(toNumber(amount))
end

-- Find all recipes where this item is used as a component
local function find_recipes(item_name)
    local recipes = {}
    for _, item in pairs(data) do
        if item.Components and type(item.Components) == "table" then
            for _, comp_id in ipairs(item.Components) do
                local component = data[comp_id]
                if component and component.Name == item_name then
                    table.insert(recipes, item.Name)
                    break  -- Only need to find one match per item
                end
            end
        end
    end
    return recipes
end

-- Main function with complete item relationship handling
p.generate_infobox = function(frame)
    local item_name = frame.args[1]
    if not item_name or item_name == "" then
        return frame:preprocess("{{Error|No item name specified}}")
    end
    
    local item = get_json_item(item_name)
    if not item then
        return frame:preprocess("{{Error|Item not found: " .. item_name .. "}}")
    end
    
    -- Determine item type
    local item_type = "Weapon" -- default
    if item.Slot then
        local slot = normalize(item.Slot)
        if slot == "armor" then
            item_type = "Vitality"
        elseif slot == "tech" then
            item_type = "Spirit"
        end
    end
    
    -- Color definitions
    local colors = {
        Weapon = {
            bg = "#80550F", header = "#C97A03", border = "#C97A03",
            components = {bg = "#9E630C", text = "#DCCFC6"},
            recipes = {bg = "#704A0C", text = "#D1CBC6"}
        },
        Vitality = {
            bg = "#4D7214", header = "#659818", border = "#659818",
            components = {bg = "#203500", text = "#C7C9C6"},
            recipes = {bg = "#436310", text = "#CACFC7"}
        },
        Spirit = {
            bg = "#623585", header = "#8B56B4", border = "#8B56B4",
            components = {bg = "#372248", text = "#C9C7CB"},
            recipes = {bg = "#552D74", text = "#CCC8D2"}
        }
    }
    local color = colors[item_type] or colors.Weapon

    -- Process components (what this item is made of)
    local component_text = ""
    if item.Components and type(item.Components) == "table" and #item.Components > 0 then
        local valid_components = {}
        for _, comp_id in ipairs(item.Components) do
            local component = data[comp_id]
            if component and component.Name then
                table.insert(valid_components, "{{ItemIcon|" .. component.Name .. "}}")
            end
        end
        
        if #valid_components > 0 then
            component_text = string.format(
                '|-\n! colspan="4" style="text-align:left; padding:0 12px; font-weight:bold; font-size:16px; font-family:\'Retail Demo Bold\',serif; background-color: %s; color: %s" | COMPONENTS:<br/>%s',
                color.components.bg, color.components.text,
                table.concat(valid_components, "<br/>")
            )
        end
    end

    -- Process recipes (what items this is used in)
    local recipe_text = ""
    local recipes = find_recipes(item.Name)
    if #recipes > 0 then
        local recipe_links = {}
        for _, recipe_name in ipairs(recipes) do
            table.insert(recipe_links, "{{ItemIcon|" .. recipe_name .. "}}")
        end
        
        recipe_text = string.format(
            '|-\n! colspan="4" style="text-align:left; padding:0 12px; font-weight:bold; font-size:16px; font-family:\'Retail Demo Bold\',serif; background-color: %s; color: %s" | USED IN:<br/>%s',
            color.recipes.bg, color.recipes.text,
            table.concat(recipe_links, "<br/>")
        )
    end

    -- Process stats - comprehensive stat handling
    local stats = {}
    local stat_config = {
        TechPower = {icon = "Spirit icon.png", suffix = "Spirit Power"},
        WeaponPower = {icon = "Weapon Icon.png", suffix = "Weapon Damage"},
        TechResist = {icon = "Spirit Resist Icon.png", suffix = "Tech Resist"},
        BulletResist = {icon = "Bullet Resist Icon.png", suffix = "Bullet Resist"},
        AbilityCooldown = {icon = "Cooldown Icon.png", suffix = "s Cooldown"},
        AbilityCastRange = {suffix = "m Cast Range"},
        AbilityUnitTargetLimit = {prefix = "Targets: "},
        -- Rapid Recharge specific stats
        CooldownBetweenChargeReduction = {suffix = "% Charge Cooldown Reduction"},
        BonusAbilityCharges = {prefix = "+", suffix = " Bonus Charges"},
        CooldownReductionOnChargedAbilities = {suffix = "% Charged Ability Cooldown Reduction"},
        BaseAttackDamagePercent = {suffix = "% Base Attack Damage"},
        -- Add more stats as needed
    }

    for stat, config in pairs(stat_config) do
        local value = item[stat]
        if value and value ~= "0" and value ~= 0 then
            local text = "• "
            if config.prefix then text = text .. config.prefix end
            text = text .. tostring(value)
            if config.icon then text = text .. " [[File:" .. config.icon .. "|20px]]" end
            if config.suffix then text = text .. config.suffix end
            table.insert(stats, text)
        end
    end

    -- Calculate shop bonus
    local tier = toNumber(item.Tier or 1)
    local shop_bonus = "+0"
    if item_type == "Weapon" then
        shop_bonus = string.format("+%d%% Weapon Damage", 6 + (tier-1)*4)
    elseif item_type == "Vitality" then
        shop_bonus = string.format("+%d%% Base Health", 11 + (tier-1)*3)
    else -- Spirit
        shop_bonus = string.format("+%d Spirit Power", 4 * tier)
    end

    -- Build infobox parts
    local parts = {
        -- Header
        string.format(
            '<div class="infobox_item" style="float:right; display:inline-block; vertical-align:top;">\n' ..
            '{| class="wikitable" style="text-align:left; border-collapse:collapse; width:312px; max-width:100%%; color: #FFEFD7; padding:12px; font-family:\'Retail Demo Regular\',serif; background-color: %s"\n' ..
            '! colspan="4" style="width:280px; padding:5px 12px; text-align:center; font-size:24px; font-family:\'Retail Demo Bold\',serif; background-color: %s" | %s',
            color.bg, color.header, item.Name
        ),
        
        -- Image
        string.format(
            '|-\n| colspan=4 class="infobox-image" style="padding:5px; text-align:center;" | [[File:%s.png|144px]]',
            item.Name
        ),
        
        -- Cost
        string.format(
            '|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Cost\n' ..
            '| colspan=2 style="padding-left:0.5em;" | {{Souls|%s}}',
            color.border, formatNum(item.Cost)
        ),
        
        -- Tier
        string.format(
            '|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Tier\n' ..
            '| colspan=2 style="padding-left:0.5em;" | %s',
            color.border, tier
        ),
        
        -- Shop Bonus
        string.format(
            '|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Shop Bonus\n' ..
            '| colspan=2 style="padding-left:0.5em;" | %s',
            color.border, shop_bonus
        )
    }

    -- Add components section if exists
    if component_text ~= "" then
        table.insert(parts, component_text)
    end

    -- Add recipes section if exists
    if recipe_text ~= "" then
        table.insert(parts, recipe_text)
    end

    -- Add description if it exists
    if item.Description and item.Description ~= "" then
        table.insert(parts,
            string.format(
                '|-\n| colspan="4" style="text-align:left; padding:0 12px; color:#FFEFD7; background-color: %s" | %s',
                color.bg, item.Description
            )
        )
    end

    -- Add stats if they exist
    if #stats > 0 then
        table.insert(parts,
            '|-\n| ' .. table.concat(stats, "\n|-\n| ")
        )
    end

    -- Close table
    table.insert(parts, "|}\n</div>")

    -- Process and return
    return frame:preprocess(table.concat(parts, "\n"))
end

return p