Module:Sandbox: Difference between revisions

No edit summary
No edit summary
Line 2: Line 2:
local data = mw.loadJsonData("Data:ItemData.json")
local data = mw.loadJsonData("Data:ItemData.json")


-- Utility function to get item by name (case-insensitive)
-- Normalize strings for comparison
local function normalize(str)
    return mw.ustring.lower(mw.text.trim(str or ""))
end
 
-- Clean and parse numbers
local function cleanNumber(num)
    if type(num) == "number" then return num end
    if not num then return 0 end
    return tonumber(tostring(num):gsub("[^%d.-]", "")) or 0
end
 
-- Safe item access
local function get_json_item(name)
local function get_json_item(name)
     if not name or type(name) ~= 'string' then return nil end
     if not name or type(name) ~= 'string' then return nil end
     local lowerName = mw.ustring.lower(name)
     local targetName = normalize(name)
      
      
     for _, v in pairs(data) do
     for _, item in pairs(data) do
         if v["Name"] and mw.ustring.lower(v["Name"]) == lowerName then
         if item.Name and normalize(item.Name) == targetName then
             if v["IsDisabled"] == false or v["IsDisabled"] == nil then
             if item.IsDisabled ~= true then
                 return v
                 return item
             end
             end
         end
         end
Line 17: Line 29:
end
end


-- Safe number formatting with error handling
-- Format numbers with localization
local lang = mw.language.getContentLanguage()
local lang = mw.language.getContentLanguage()
local function formatNum(amount)
local function formatNum(amount)
    if amount == nil then return "0" end
     return lang:formatNum(cleanNumber(amount))
    if type(amount) == 'string' then
        amount = tonumber(amount) or 0
    end
     return lang:formatNum(amount)
end
end


-- Main function to generate infobox content
-- Main function with improved error handling
p.generate_infobox = function(frame)
p.generate_infobox = function(frame)
     local item_name = frame.args[1]
     local item_name = frame.args[1]
     if not item_name or item_name == '' then
     if not item_name or item_name == "" then
         return frame:preprocess("<span class='error'>No item name specified</span>")
         return frame:preprocess("{{Error|No item name specified}}")
     end
     end
      
      
     local item = get_json_item(item_name)
     local item = get_json_item(item_name)
     if not item then
     if not item then
         return frame:preprocess("<span class='error'>Item not found: " .. item_name .. "</span>")
         return frame:preprocess("{{Error|Item not found: " .. item_name .. "}}")
     end
     end
      
      
     -- Determine item type with fallback
     -- Determine item type with validation
     local item_type = "Weapon" -- default
     local item_type = "Weapon" -- default
    local valid_types = {
        Weapon = {bg = "#80550F", header = "#C97A03", border = "#C97A03"},
        Vitality = {bg = "#4D7214", header = "#659818", border = "#659818"},
        Spirit = {bg = "#623585", header = "#8B56B4", border = "#8B56B4"}
    }
   
     if item.Slot then
     if item.Slot then
         if item.Slot == "Armor" then
         local slot = normalize(item.Slot)
        if slot == "armor" then
             item_type = "Vitality"
             item_type = "Vitality"
         elseif item.Slot == "Tech" then
         elseif slot == "tech" then
             item_type = "Spirit"
             item_type = "Spirit"
         end
         end
     end
     end
   
    local colors = valid_types[item_type] or valid_types.Weapon


     -- Generate stats section with all possible stats
     -- Process components
     local stats = {}
     local components = {}
    local stat_order = {
     if item.Components and type(item.Components) == "table" then
        'TechPower', 'WeaponPower', 'TechResist', 'BulletResist',
        'AbilityCooldown', 'AbilityCastRange', 'AbilityUnitTargetLimit'
    }
      
    for _, stat in ipairs(stat_order) do
        if item[stat] and item[stat] ~= "0" and item[stat] ~= 0 then
            local stat_text = "• "
            if stat == 'TechPower' then
                stat_text = stat_text .. item[stat] .. " [[File:Spirit icon.png|20px]] Spirit Power"
            elseif stat == 'WeaponPower' then
                stat_text = stat_text .. item[stat] .. " [[File:Weapon Icon.png|20px]] Weapon Damage"
            elseif stat == 'TechResist' then
                stat_text = stat_text .. item[stat] .. " [[File:Spirit Resist Icon.png|20px]] Tech Resist"
            elseif stat == 'BulletResist' then
                stat_text = stat_text .. item[stat] .. " [[File:Bullet Resist Icon.png|20px]] Bullet Resist"
            elseif stat == 'AbilityCooldown' then
                stat_text = stat_text .. "[[File:Cooldown Icon.png|20px]] " .. item[stat] .. "s Cooldown"
            elseif stat == 'AbilityCastRange' then
                stat_text = stat_text .. item[stat] .. "m Cast Range"
            elseif stat == 'AbilityUnitTargetLimit' then
                stat_text = stat_text .. "Targets: " .. item[stat]
            end
           
            table.insert(stats, stat_text)
        end
    end
   
    local stats_text = table.concat(stats, "\\n|\\n| ")
   
    -- Generate components section if exists
    local components_text = ""
    if item.Components and #item.Components > 0 then
        local component_names = {}
         for _, comp_id in ipairs(item.Components) do
         for _, comp_id in ipairs(item.Components) do
             local component = data[comp_id]
             local component = data[comp_id]
             if component and component.Name then
             if component and component.Name then
                 table.insert(component_names, component.Name)
                 table.insert(components, component.Name)
             end
             end
         end
         end
          
    end
         if #component_names > 0 then
 
            components_text = string.format(
    -- Process stats dynamically
                "! colspan=\"4\" style=\"text-align:left; padding:0 12px; font-weight:bold; font-size:16px; font-family:'Retail Demo Bold',serif; %s\" | COMPONENTS:<br/>%s",
    local stats = {}
                (item_type == "Weapon" and "background-color: #9E630C; color: #DCCFC6;" or
    local stat_formats = {
                item_type == "Vitality" and "background-color: #203500; color: #C7C9C6;" or
         TechPower = {icon = "Spirit icon.png", suffix = "Spirit Power"},
                "background-color: #372248; color: #C9C7CB;"),
         WeaponPower = {icon = "Weapon Icon.png", suffix = "Weapon Damage"},
                table.concat(component_names, "<br/>{{ItemIcon|", "{{ItemIcon|", "}}")
        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: "}
    }
 
    for stat, format in pairs(stat_formats) do
        local value = item[stat]
        if value and value ~= "0" and value ~= 0 then
            local text = ""
            if format.prefix then text = text .. format.prefix end
            text = text .. tostring(value)
            if format.icon then text = text .. " [[File:" .. format.icon .. "|20px]]" end
            if format.suffix then text = text .. " " .. format.suffix end
             table.insert(stats, text)
         end
         end
     end
     end
   
 
     -- Calculate shop bonus dynamically
     -- Calculate shop bonus
     local shop_bonus = "+0"
     local shop_bonus = "+0"
     if item.Tier then
     local tier = cleanNumber(item.Tier or 1)
        local tier = tonumber(item.Tier) or 1
    if item_type == "Weapon" then
        if item_type == "Weapon" then
        shop_bonus = string.format("+%d%% Weapon Damage", 6 + (tier-1)*4)
            shop_bonus = "+" .. (6 + (tier-1)*4) .. "% Weapon Damage"
    elseif item_type == "Vitality" then
        elseif item_type == "Vitality" then
        shop_bonus = string.format("+%d%% Base Health", 11 + (tier-1)*3)
            shop_bonus = "+" .. (11 + (tier-1)*3) .. "% Base Health"
    else -- Spirit
        else -- Spirit
        shop_bonus = string.format("+%d Spirit Power", 4 * tier)
            shop_bonus = "+" .. (4 * tier) .. " Spirit Power"
        end
     end
     end
   
 
     -- Build the infobox as separate parsed chunks
     -- Build infobox parts
     local infobox_parts = {
     local parts = {
         frame:preprocess(string.format(
         -- Header
             [[<div class="infobox_item" style="float:right; display:inline-block; vertical-align:top;">
        string.format(
             {| class="wikitable" style="text-align:left; border-collapse:collapse; width:312px; max-width:100%%; color: #FFEFD7; padding:12px; font-family:'Retail Demo Regular',serif; %s"
             '<div class="infobox_item" style="float:right; display:inline-block; vertical-align:top;">\n' ..
             ! colspan="4" style="width:280px; padding:5px 12px; text-align:center; font-size:24px; font-family:'Retail Demo Bold',serif; %s" | %s]],
             '{| 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' ..
             (item_type == "Weapon" and "background-color: #80550F;" or
             '! colspan="4" style="width:280px; padding:5px 12px; text-align:center; font-size:24px; font-family:\'Retail Demo Bold\',serif; background-color: %s" | %s',
            item_type == "Vitality" and "background-color: #4D7214;" or
             colors.bg, colors.header, item.Name
            "background-color: #623585;"),
         ),
            (item_type == "Weapon" and "background-color: #C97A03;" or
            item_type == "Vitality" and "background-color: #659818;" or
            "background-color: #8B56B4;"),
            item.Name
         )),
          
          
         frame:preprocess(string.format(
         -- Image
             "|-\n| colspan=4 class=\"infobox-image\" style=\"padding:5px; text-align:center;\" | [[File:%s.png|144px]]",
        string.format(
             '|-\n| colspan=4 class="infobox-image" style="padding:5px; text-align:center;" | [[File:%s.png|144px]]',
             item.Name
             item.Name
         )),
         ),
          
          
         frame:preprocess(string.format(
         -- Cost
             "|-\n| colspan=2 style=\"text-align:right; width:50%%; %s padding-right:0.5em;\" | Cost\n| colspan=2 style=\"padding-left:0.5em;\" | {{Souls|%s}}",
        string.format(
             (item_type == "Weapon" and "border-right:1px solid #C97A03;" or
             '|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Cost\n' ..
            item_type == "Vitality" and "border-right:1px solid #659818;" or
            '| colspan=2 style="padding-left:0.5em;" | {{Souls|%s}}',
            "border-right:1px solid #8B56B4;"),
             colors.border, formatNum(item.Cost)
            formatNum(item.Cost or 0)
         ),
         )),
          
          
         frame:preprocess(string.format(
         -- Tier
             "|-\n| colspan=2 style=\"text-align:right; width:50%%; %s padding-right:0.5em;\" | Tier\n| colspan=2 style=\"padding-left:0.5em;\" | %s",
        string.format(
             (item_type == "Weapon" and "border-right:1px solid #C97A03;" or
             '|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Tier\n' ..
            item_type == "Vitality" and "border-right:1px solid #659818;" or
            '| colspan=2 style="padding-left:0.5em;" | %s',
            "border-right:1px solid #8B56B4;"),
             colors.border, tier
            item.Tier or "1"
         ),
         )),
          
          
         frame:preprocess(string.format(
         -- Shop Bonus
             "|-\n| colspan=2 style=\"text-align:right; width:50%%; %s padding-right:0.5em;\" | Shop Bonus\n| colspan=2 style=\"padding-left:0.5em;\" | %s",
        string.format(
             (item_type == "Weapon" and "border-right:1px solid #C97A03;" or
             '|-\n| colspan=2 style="text-align:right; width:50%%; border-right:1px solid %s; padding-right:0.5em;" | Shop Bonus\n' ..
            item_type == "Vitality" and "border-right:1px solid #659818;" or
            '| colspan=2 style="padding-left:0.5em;" | %s',
            "border-right:1px solid #8B56B4;"),
             colors.border, shop_bonus
            shop_bonus
         )
         )),
       
        components_text ~= "" and frame:preprocess("|-\n"..components_text) or "",
       
        frame:preprocess(string.format(
            "|-\n| colspan=\"4\" style=\"text-align:left; padding:0 12px; color:#FFEFD7; %s\" | %s",
            (item_type == "Weapon" and "background-color: #80550F;" or
            item_type == "Vitality" and "background-color: #4D7214;" or
            "background-color: #623585;"),
            item.Description or ""
        )),
       
        stats_text ~= "" and frame:preprocess("|-\n| "..stats_text) or "",
       
        frame:preprocess("|}\n</div>")
     }
     }
      
 
     -- Combine all parts and return
    -- Add components if they exist
     return table.concat(infobox_parts)
    if #components > 0 then
        table.insert(parts,
            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',
                (item_type == "Weapon" and "#9E630C" or item_type == "Vitality" and "#203500" or "#372248"),
                (item_type == "Weapon" and "#DCCFC6" or item_type == "Vitality" and "#C7C9C6" or "#C9C7CB"),
                table.concat(components, "<br/>{{ItemIcon|", "{{ItemIcon|", "}}")
            )
        )
     end
 
     -- Add description
    table.insert(parts,
        string.format(
            '|-\n| colspan="4" style="text-align:left; padding:0 12px; color:#FFEFD7; background-color: %s" | %s',
            colors.bg, item.Description or ""
        )
    )
 
    -- 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
end


return p
return p