Module:Sandbox/LVL: Difference between revisions

Jump to navigation Jump to search
LVL (talk | contribs)
No edit summary
Tag: Manual revert
LVL (talk | contribs)
No edit summary
Line 1: Line 1:
local p = {}
local p = {}


-- Load data and utility modules
-- Load language codes
local npcs_data = mw.loadJsonData("Data:Sandbox.json")
local lang_codes = mw.loadJsonData("Data:LangCodes.json")
local util_module = require('Module:Utilities')


-- returns the entire data table for a specific NPC, used by other Lua modules
-- Cache for compiled icon data (persists during page render)
function p.get_npc_data(name)
local iconDataCache = nil
if not name or name == '' then
local cachedLangCode, cachedLangData
return nil
 
end
-- Build unified lookup table from bot-uploaded JSONs
return npcs_data[name]
local function buildIconData()
    if iconDataCache then
        return iconDataCache
    end
   
    local lookup = {}
   
    -- Load bot data
    local heroData = mw.loadJsonData("Data:HeroData.json")
    local abilityData = mw.loadJsonData("Data:AbilityData.json")
    local itemData = mw.loadJsonData("Data:ItemData.json")
   
    -- Track which hero owns which ability (for linking)
    local abilityToHero = {}
   
    -- Process Heroes
    for heroKey, hero in pairs(heroData) do
        if type(hero) == "table" and hero.Name then
            local name = hero.Name
            local lowerName = name:lower()
           
            lookup[lowerName] = {
                name = name,
                key = heroKey,
                link = name,
                image = name .. ".png",
                type = "hero"
            }
           
            -- Map abilities to parent hero
            if hero.BoundAbilities then
                for slot, ability in pairs(hero.BoundAbilities) do
                    if ability.Key then
                        abilityToHero[ability.Key] = {
                            heroName = name,
                            abilityName = ability.Name -- Use name from hero data if available
                        }
                    end
                end
            end
        end
    end
   
    -- Process Abilities
    for abilityKey, ability in pairs(abilityData) do
        if type(ability) == "table" and ability.Name then
            local name = ability.Name
            local lowerName = name:lower()
            local parentInfo = abilityToHero[abilityKey]
           
            -- Determine link (fallback to ability name if no parent found)
            local link = parentInfo and parentInfo.heroName or name
           
            lookup[lowerName] = {
                name = name,
                key = abilityKey,
                link = link,
                image = name .. ".png",
                type = "ability",
                class = "theme"  -- Abilities get theme class by default
            }
        end
    end
   
    -- Process Items
    for itemKey, item in pairs(itemData) do
        if type(item) == "table" and item.Name then
            local name = item.Name
            local lowerName = name:lower()
           
            lookup[lowerName] = {
                name = name,
                key = itemKey,
                link = name,
                image = name .. ".png",
                type = "item"
            }
        end
    end
   
    iconDataCache = lookup
    return lookup
end
end


-- Retrieves a specific stat value for a given NPC. Handles both simple and nested calls.
-- Get icon data by name (with alias support)
-- {{#invoke:NpcData|get_npc_var|NPC_KEY|STAT_KEY_1|STAT_KEY_2|...|SIG_FIGS}}
local function getIconData(iconName)
p.get_npc_var = function(frame)
    local data = buildIconData()
     -- Safer argument handling for both live and debug environments
    local lowerName = iconName:lower()
     local args = {}
   
     if frame.getParent then
    -- Check main lookup
         local parent_args = frame:getParent().args
    if data[lowerName] then
         for k, v in pairs(parent_args) do args[k] = v end
        return data[lowerName], nil
    end
   
    -- Check aliases (for edge cases like "doorman" -> "the doorman")
    local aliases = {
        ["doorman"] = "the doorman",
        ["mo and krill"] = "mo & krill",
        ["mo"] = "mo & krill",
        ["krill"] = "mo & krill",
        ["curse"] = "cursed relic",
        ["debuff remover"] = "dispel magic"
    }
   
    if aliases[lowerName] then
        return data[aliases[lowerName]], nil
    end
   
    return nil, string.format("[[:Category:Module:Icon ERROR|Icon not found]] ('%s'). [[Category:Module:Icon ERROR]]", iconName)
end
 
-- Language handling (unchanged from your original)
local function getLangData()
    if cachedLangData then
        return cachedLangCode, cachedLangData
    end
 
    local title = mw.title.getCurrentTitle().fullText
    local subpageLang = title:match("/([^/]+)$")
     local langCode = (subpageLang and lang_codes[subpageLang]) and subpageLang or "en"
 
     local langData = {}
     if langCode ~= "en" then
         local success, data = pcall(function()
            return mw.loadJsonData("Data:Lang " .. langCode .. ".json")
         end)
        if success and type(data) == "table" then
            langData = data
        end
     end
     end
    -- Direct args from #invoke or the debug table overwrite parent args
    for k, v in pairs(frame.args) do args[k] = v end


local npc_name = args[1] and mw.text.trim(args[1]) or nil
    cachedLangCode = langCode
if not npc_name or npc_name == '' then
    cachedLangData = langData
return '<span class="error">Error: NPC name not provided.</span>'
end


local npc = npcs_data[npc_name]
    return langCode, langData
if not npc then
end
return '<span class="error">Error: NPC "' .. npc_name .. '" not found.</span>'
end


local current_value = npc
-- Main render function (updated to handle missing keys gracefully)
local last_key_index = 1
function p.render(frame)
local path = {npc_name} -- Track the path for better error messages
    local args = frame:getParent().args
    local name = args[1] or ""
    local customText = args.l1 or ""
   
    -- Set Defaults
    local size = args.size or "20px"
    local noLink = args["no-link"] == "true"
    local iconOnly = args["icon-only"] == "true"
 
    -- Scan unnamed parameters
    for k, v in pairs(args) do
        if type(k) == "number" and k > 1 then
            local val = mw.text.trim(v)
           
            if val == "icon-only" then
                iconOnly = true
            elseif val == "no-link" then
                noLink = true
            elseif val:match("^%d+px$") then
                size = val
            end
        end
    end


-- Traverse through the arguments to find the nested value
    -- Get icon data automatically from JSON
for i = 2, #args do
    local iconData, err = getIconData(name)
local key = args[i] and mw.text.trim(args[i]) or nil
    if not iconData then
if not key or key == '' then break end
        return "Error: " .. err
    end


-- Check if this might be the sig_figs argument (numeric on a non-table)
    local langCode, langData = getLangData()
if tonumber(key) and type(current_value) ~= "table" then
   
break
    -- Determine display name
end
    local displayName = ""
    if customText ~= "" then
        displayName = customText
    elseif iconData.key and langData[iconData.key] then
        displayName = langData[iconData.key]
    else
        displayName = iconData.name
    end


if type(current_value) ~= "table" then
    -- Localize link if on translated subpage
return '<span class="error">Error: Invalid path at "' .. table.concat(path, '.') .. '". Tried to index a ' .. type(current_value) .. ' value.</span>'
    local baseLink = iconData.link or ""
end
    local link = (langCode ~= "en" and baseLink ~= "") and (baseLink .. "/" .. langCode) or baseLink
-- Try string key first
local next_value = current_value[key]
-- If nil, try numeric key
if next_value == nil then
local num_key = tonumber(key)
if num_key then
next_value = current_value[num_key]
end
end
if next_value == nil then
return '<span class="error">Error: Key "' .. key .. '" not found in path "' .. table.concat(path, '.') .. '".</span>'
end
current_value = next_value
table.insert(path, key)
last_key_index = i
end


-- Handle sig_figs if provided
    -- Style logic (only abilities get theme class by default)
local sig_figs_arg = args[last_key_index + 1]
    local class = iconData.class or ""
local sig_figs = sig_figs_arg and tonumber(mw.text.trim(sig_figs_arg)) or nil
    local extra = (class:find("invert", 1, true) and " filter:invert(1);" or "")
    local style
if sig_figs then
    if class:find("theme", 1, true) then
if type(current_value) ~= "number" then
        style = 'class="module-icon-ability" style="position:relative; bottom:2px;' .. extra .. '"'
return '<span class="error">Error: Cannot apply sig_figs to non-numeric value at "' .. table.concat(path, '.') .. '" (type: ' .. type(current_value) .. ').</span>'
    else
end
        style = 'style="position:relative; bottom:2px;' .. extra .. '"'
current_value = util_module.round_to_sig_fig(current_value, sig_figs)
    end
if current_value == nil then
return '<span class="error">Error: Rounding failed for value at "' .. table.concat(path, '.') .. '".</span>'
end
end


-- Convert tables/booleans to strings for display
    -- Generate icon HTML
if type(current_value) == "table" then
    local imageLink = noLink and "" or link
return '<span class="error">Error: Path "' .. table.concat(path, '.') .. '" points to a table/object. Specify a deeper path to get a value.</span>'
    local iconHtml = string.format('<span %s>[[File:%s|%s|link=%s]]</span>', style, iconData.image, size, imageLink)
elseif type(current_value) == "boolean" then
return tostring(current_value)
end


return current_value
    if iconOnly or displayName == "" then
end
        return iconHtml
    end


-- Legacy function for backwards compatibility
    local textHtml = (noLink or link == "") and displayName or string.format('[[%s|%s]]', link, displayName)
-- Now just redirects to get_npc_var since it handles numeric indices
    return '<span style="white-space:nowrap;">' .. iconHtml .. " " .. textHtml .. '</span>'
p.get_list_elem = function(frame)
return p.get_npc_var(frame)
end
end


return p
return p