Module:Sandbox: Difference between revisions

From The Deadlock Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
local p = {};
local p = {}
local data = mw.loadJsonData("Data:ItemData.json")
local util_module = require("Module:Utilities")
local lang_codes_set = mw.loadJsonData("Data:LangCodes.json")
local dictionary_module = require("Module:Dictionary")


-- Utility function to safely convert to number
-- Overrides applied to searches by key. Designed to handle edge cases where
local function toNumber(value)
-- the expected key does not have a localization entry
     if type(value) == "number" then
local KEY_OVERRIDES = {
        return value
    MoveSlowPercent_label = 'MovementSlow_label',
     elseif type(value) == "string" then
    BonusHealthRegen_label = 'HealthRegen_label',
        local cleaned = value:gsub("[^%d.-]", "")
    BarbedWireRadius_label = 'Radius_label',
         return tonumber(cleaned) or 0
    BarbedWireDamagePerMeter_label = 'DamagePerMeter_label',
    BuildUpDuration_label = 'BuildupDuration_label',
    TechArmorDamageReduction_label = 'TechArmorDamageReduction_Label',
     DamageAbsorb_label = 'DamageAbsorb_Label',
    InvisRegen_label = 'InvisRegen_Label',
    EvasionChance_label = 'EvasionChance_Label',
     DelayBetweenShots_label = 'DelayBetweenShots_Label',
}
 
function get_lang_file(lang_code)
    local file_name = string.format("Data:Lang_%s.json", lang_code)
    local success, data = pcall(mw.loadJsonData, file_name)
    if success then
         return data
    else
        return nil
     end
     end
    return 0
end
end


-- Normalize strings for comparison
-- Helper function to replace \"n with newlines
local function normalize(str)
local function process_newlines(str)
     return mw.ustring.lower(mw.text.trim(str or ""))
     if str then
        return str:gsub('\\"n', '\n')
    end
    return str
end
end


-- Safe item access
-- Get a localized string by the raw key
local function get_json_item(name)
p.get_string = function(key, lang_code_override, fallback_str, remove_var_index)
     if not name or type(name) ~= 'string' then return nil end
     -- If called internally (direct Lua call), args will be passed directly.
     local targetName = normalize(name)
     -- If called from wikitext, `key` will be the `frame` object, and we get args from `frame.args`.
   
 
     for _, item in pairs(data) do
     -- Handle the case where it's called via #invoke (i.e., from wikitext)
        if item.Name and normalize(item.Name) == targetName then
    if type(key) == "table" and key.args then
            if item.IsDisabled ~= true then
        local frame = key
                return item
        key = frame.args[1]
            end
        lang_code_override = frame.args["lang_code_override"]
         end
        fallback_str = frame.args["fallback_str"]
         remove_var_index = frame.args["remove_var_index"]
     end
     end
    return nil
end


-- Find items that use this item as a component
    -- Determine lang_code if not overridden
local function get_parent_items(item_name)
     local lang_code = lang_code_override
     local parents = {}
     if (lang_code == '' or lang_code == nil) then
     for _, item in pairs(data) do
        lang_code = get_lang_code()
        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(parents, item.Name)
                end
            end
        end
     end
     end
    return parents
end
-- Format numbers with localization
local lang = mw.language.getContentLanguage()
local function formatNum(amount)
    return lang:formatNum(toNumber(amount))
end


-- Smart stat display formatter
    -- Retrieve lang data
local function formatStat(stat, value)
    local data = get_lang_file(lang_code)
    local icon_map = {
     if (data == nil) then
        TechPower = "Spirit icon.png",
         return string.format("Lang code '%s' does not have a json file", lang_code)  
        WeaponPower = "Weapon Icon.png",
        TechResist = "Spirit Resist Icon.png",
        BulletResist = "Bullet Resist Icon.png",
        AbilityCooldown = "Cooldown Icon.png"
    }
   
    local suffix_map = {
        Percent = "%",
        Speed = "m/s",
        Range = "m",
        Duration = "s",
        Cooldown = "s",
        Health = " HP",
        Stacks = " stacks",
        Damage = " Damage",
        FireRate = " Fire Rate",
        Lifesteal = "% Lifesteal"
    }
   
    -- Special cases with custom formatting
     if stat == "AmmoPerSoul" then
        return string.format("• %d ammo per Soul", value)
    elseif stat == "SpiritPowerPerSoul" then
        return string.format("• +%d Spirit Power per Soul", value)
    elseif stat == "MaxStacks" then
         return string.format("• Max %d stacks", value)
     end
     end
      
      
     -- Automatic formatting based on stat name
     -- Localize
     local display = "• "
     local label = data[KEY_OVERRIDES[key] or key]
     local icon = icon_map[stat]
     if (label == nil) then
    local base_stat = stat:gsub("Bonus", ""):gsub("Percent", "")
        -- Apply fallback
   
        local fallback_tooltip = mw.getCurrentFrame():expandTemplate{title = "MissingValveTranslationTooltip"}
    -- Add value
        local fallback
    display = display .. tostring(value)
        if (fallback_str == 'en') then
   
            fallback = p.get_string(key, 'en', key .. fallback_tooltip, remove_var_index, upper_lower)
    -- Add icon if available
        elseif fallback_str == 'dictionary' then
    if icon then
            return dictionary_module.translate(key, lang_code_override)
         display = display .. " [[File:" .. icon .. "|20px]]"
        elseif fallback_str ~= nil then
            fallback = fallback_str
        else
            return ''
        end
         return process_newlines(fallback) .. fallback_tooltip
     end
     end
      
      
     -- Add appropriate suffix
     -- Apply remove_var
     for pattern, suffix in pairs(suffix_map) do
     if (remove_var_index ~= nil) then
         if stat:find(pattern) then
         label = util_module.remove_var(label, remove_var_index)
            return display .. suffix
        end
     end
     end
      
      
    -- Default case - show stat name if no special formatting
     return process_newlines(label)
     return display .. " " .. base_stat
end
end


-- Main function with improved error handling
-- Search for a localized string using its English label
p.generate_infobox = function(frame)
p.search_string = function(frame)
     local item_name = frame.args[1]
     local label = frame.args[1]
     local manual_component = frame.args.component
     local lang_code_override = frame.args[2]
      
 
     if not item_name or item_name == "" then
    return p._search_string(label, lang_code_override)
         return frame:preprocess("{{Error|No item name specified}}")
end
 
-- search_string, but for internal use by other modules
p._search_string = function(label, lang_code_override)
     lang_code = lang_code_override
     if (lang_code == '' or lang_code == nil) then
         lang_code = get_lang_code()
     end
     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"},
        Vitality = {bg = "#4D7214", header = "#659818", border = "#659818"},
        Spirit = {bg = "#623585", header = "#8B56B4", border = "#8B56B4"}
    }
    local color = colors[item_type] or colors.Weapon


     -- Process components
     -- Load the language files
     local components_text = ""
     local data_en = get_lang_file('en') -- English data
    if item.Components and type(item.Components) == "table" then
    local data_lang = get_lang_file(lang_code) -- Target language data
        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
            components_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',
                (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(valid_components, "<br/>")
            )
        end
    end


     -- Process parent items
     if (data_lang == nil) then
    local parent_items_text = ""
         error("Lang code '%s' does not have a json file", lang_code)  
    local parent_items = {}
   
    if manual_component and manual_component ~= "" then
         table.insert(parent_items, manual_component)
    else
        parent_items = get_parent_items(item.Name)
     end
     end
      
      
     if #parent_items > 0 then
     -- Search for the key in the English data
        local parent_icons = {}
    local key = nil
        for _, parent in ipairs(parent_items) do
    for k, v in pairs(data_en) do
             table.insert(parent_icons, "{{ItemIcon|" .. parent .. "}}")
        if v == label then
             key = k  -- Find the key corresponding to the label
            break
         end
         end
       
        parent_items_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" | IS COMPONENT OF:<br/>%s',
            (item_type == "Weapon" and "#704A0C" or item_type == "Vitality" and "#436310" or "#552D74"),
            (item_type == "Weapon" and "#D1CBC6" or item_type == "Vitality" and "#CACFC7" or "#CCC8D2"),
            table.concat(parent_icons, "<br/>")
        )
     end
     end


     -- Process ALL stats dynamically
     -- Default to input label if localized string is not found
     local stats = {}
     if (key == nil) then
    local skip_stats = {
        return process_newlines(label)
        Name = true, Description = true, Cost = true, Tier = true,
        Activation = true, Slot = true, Components = true,
        TargetTypes = true, ShopFilters = true, IsDisabled = true,
        AbilityCastDelay = true, AbilityChannelTime = true,
        AbilityPostCastDuration = true, AbilityCharges = true,
        AbilityCooldownBetweenCharge = true, ChannelMoveSpeed = true,
        AbilityResourceCost = true
    }
   
    for stat, value in pairs(item) do
        if not skip_stats[stat] and value ~= nil and value ~= "0" and value ~= 0 then
            local num_value = toNumber(value)
            local formatted = formatStat(stat, num_value)
            if formatted then
                table.insert(stats, formatted)
            end
        end
     end
     end
      
     if (data_lang[key] == nil) then
    -- Sort stats alphabetically for consistent display
         return process_newlines(label)
    table.sort(stats)
 
    -- 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
     end


     -- Build infobox parts
     return process_newlines(data_lang[key])
    local parts = {
end
        -- 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 if they exist
function get_lang_code()
     if components_text ~= "" then
     local title = mw.title.getCurrentTitle()
         table.insert(parts, components_text)
     local lang_code = title.fullText:match(".*/(.*)$")
   
    if lang_code == nil or lang_codes_set[lang_code] == nil then
         return 'en'   
     end
     end
 
          
    -- Add parent items if they exist
     return lang_code
    if parent_items_text ~= "" then
        table.insert(parts, parent_items_text)
    end
 
    -- Add description if it exists
    if 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
end


return p
return p

Revision as of 19:20, 1 April 2025

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

local p = {}
local util_module = require("Module:Utilities")
local lang_codes_set = mw.loadJsonData("Data:LangCodes.json")
local dictionary_module = require("Module:Dictionary")

-- Overrides applied to searches by key. Designed to handle edge cases where
-- the expected key does not have a localization entry
local KEY_OVERRIDES = {
    MoveSlowPercent_label = 'MovementSlow_label',
    BonusHealthRegen_label = 'HealthRegen_label',
    BarbedWireRadius_label = 'Radius_label',
    BarbedWireDamagePerMeter_label = 'DamagePerMeter_label',
    BuildUpDuration_label = 'BuildupDuration_label',
    TechArmorDamageReduction_label = 'TechArmorDamageReduction_Label',
    DamageAbsorb_label = 'DamageAbsorb_Label',
    InvisRegen_label = 'InvisRegen_Label',
    EvasionChance_label = 'EvasionChance_Label',
    DelayBetweenShots_label = 'DelayBetweenShots_Label',
}

function get_lang_file(lang_code)
    local file_name = string.format("Data:Lang_%s.json", lang_code)
    local success, data = pcall(mw.loadJsonData, file_name)
    if success then
        return data
    else
        return nil
    end
end

-- Helper function to replace \"n with newlines
local function process_newlines(str)
    if str then
        return str:gsub('\\"n', '\n')
    end
    return str
end

-- Get a localized string by the raw key
p.get_string = function(key, lang_code_override, fallback_str, remove_var_index)
    -- If called internally (direct Lua call), args will be passed directly.
    -- If called from wikitext, `key` will be the `frame` object, and we get args from `frame.args`.

    -- Handle the case where it's called via #invoke (i.e., from wikitext)
    if type(key) == "table" and key.args then
        local frame = key
        key = frame.args[1]
        lang_code_override = frame.args["lang_code_override"]
        fallback_str = frame.args["fallback_str"]
        remove_var_index = frame.args["remove_var_index"]
    end

    -- Determine lang_code if not overridden
    local lang_code = lang_code_override
    if (lang_code == '' or lang_code == nil) then
        lang_code = get_lang_code()
    end

    -- Retrieve lang data
    local data = get_lang_file(lang_code)
    if (data == nil) then
        return string.format("Lang code '%s' does not have a json file", lang_code)    
    end
    
    -- Localize
    local label = data[KEY_OVERRIDES[key] or key]
    if (label == nil) then
        -- Apply fallback
        local fallback_tooltip = mw.getCurrentFrame():expandTemplate{title = "MissingValveTranslationTooltip"}
        local fallback
        if (fallback_str == 'en') then
            fallback = p.get_string(key, 'en', key .. fallback_tooltip, remove_var_index, upper_lower)
        elseif fallback_str == 'dictionary' then
            return dictionary_module.translate(key, lang_code_override)
        elseif fallback_str ~= nil then
            fallback = fallback_str
        else
            return ''
        end
        return process_newlines(fallback) .. fallback_tooltip
    end
    
    -- Apply remove_var
    if (remove_var_index ~= nil) then 
        label = util_module.remove_var(label, remove_var_index)
    end
    
    return process_newlines(label)
end

-- Search for a localized string using its English label
p.search_string = function(frame)
    local label = frame.args[1]
    local lang_code_override = frame.args[2]

    return p._search_string(label, lang_code_override)
end

-- search_string, but for internal use by other modules
p._search_string = function(label, lang_code_override)
    lang_code = lang_code_override
    if (lang_code == '' or lang_code == nil) then
        lang_code = get_lang_code()
    end

    -- Load the language files
    local data_en = get_lang_file('en')  -- English data
    local data_lang = get_lang_file(lang_code)  -- Target language data

    if (data_lang == nil) then
        error("Lang code '%s' does not have a json file", lang_code)    
    end
    
    -- Search for the key in the English data
    local key = nil
    for k, v in pairs(data_en) do
        if v == label then
            key = k  -- Find the key corresponding to the label
            break
        end
    end

    -- Default to input label if localized string is not found
    if (key == nil) then
        return process_newlines(label)
    end
    if (data_lang[key] == nil) then
        return process_newlines(label)
    end

    return process_newlines(data_lang[key])
end

function get_lang_code()
    local title = mw.title.getCurrentTitle()
    local lang_code = title.fullText:match(".*/(.*)$")
    
    if lang_code == nil or lang_codes_set[lang_code] == nil then
        return 'en'    
    end
        
    return lang_code
end

return p