Module:Icon: Difference between revisions

Jump to navigation Jump to search
updated aliases
LVL (talk | contribs)
Switch to pre-built lookup table for faster page rendering
Line 1: Line 1:
local p = {}
local p = {}


-- Load language codes
local iconData = mw.loadJsonData("Data:ResourceLookup.json")
local lang_codes = mw.loadJsonData("Data:LangCodes.json")
local lang_codes = mw.loadJsonData("Data:LangCodes.json")


-- Cache for compiled icon data (persists during page render)
local iconDataCache = nil
local cachedLangCode, cachedLangData
local cachedLangCode, cachedLangData


-- Cache for expensive existence checks (persists during page render)
local linkOverrides = {
local existenceCache = {}
    ["Bullet Lifesteal"] = "Bullet Lifesteal (item)",
    ["Spirit Lifesteal"] = "Spirit Lifesteal (item)"
}


-- Heroes/Items that don't have images uploaded yet
local missingImageOverrides = {
local missingImageOverrides = {
    -- Unreleased Heroes
     ["boho"] = "Module-Icon missing.png",
     ["boho"] = "Module-Icon missing.png",
     ["druid"] = "Module-Icon missing.png",  
     ["druid"] = "Module-Icon missing.png",
     ["fortuna"] = "Module-Icon missing.png",
     ["fortuna"] = "Module-Icon missing.png",
     ["graf"] = "Module-Icon missing.png",
     ["graf"] = "Module-Icon missing.png",
Line 22: Line 20:
     ["swan"] = "Module-Icon missing.png",
     ["swan"] = "Module-Icon missing.png",
     ["thumper"] = "Module-Icon missing.png",
     ["thumper"] = "Module-Icon missing.png",
    -- Add any others that show red links
}
}


-- Items that link to disambiguation pages
local aliases = {
local linkOverrides = {
     ["doorman"] = "the doorman",
     ["Bullet Lifesteal"] = "Bullet Lifesteal (item)",
    ["mo and krill"] = "mo & krill",
     ["Spirit Lifesteal"] = "Spirit Lifesteal (item)"
    ["mo"] = "mo & krill",
    ["krill"] = "mo & krill",
    ["curse"] = "cursed relic",
    ["debuff remover"] = "dispel magic",
    ["defend and fight!"] = "plot armor",
    ["conjure dragon"] = "bookwyrm",
     ["backstabber"] = "stalker",
    ["flying strike"] = "flying slash",
    ["tornado"] = "dust devil",
    ["kudzu bomb"] = "entangling thorns",
}
}


-- Build unified lookup table from bot-uploaded JSONs
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()
           
            -- Check for missing image override
            local imageName = missingImageOverrides[lowerName] or (name .. ".png")
           
            lookup[lowerName] = {
                name = name,
                key = heroKey,
                link = name,
                image = imageName,
                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
                        }
                    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]
           
            -- If we already have this ability name, prefer the one linked to a hero
            if lookup[lowerName] and lookup[lowerName].type == "ability" then
                local existingHasParent = lookup[lowerName].link ~= lookup[lowerName].name
                local newHasParent = parentInfo ~= nil
               
                -- Skip if existing is linked to hero but this one isn't (prevents orphans from overwriting real ones)
                if existingHasParent and not newHasParent then
                    -- Skip this orphan ability
                else
                    -- Overwrite: new has parent and existing doesn't, or both/neither have parents
                    local link = parentInfo and parentInfo.heroName or name
                    lookup[lowerName] = {
                        name = name,
                        key = abilityKey,
                        link = link,
                        image = name .. ".png",
                        type = "ability",
                        class = "theme"
                    }
                end
            else
                -- No existing entry, add normally
                local link = parentInfo and parentInfo.heroName or name
                lookup[lowerName] = {
                    name = name,
                    key = abilityKey,
                    link = link,
                    image = name .. ".png",
                    type = "ability",
                    class = "theme"
                }
            end
        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
-- Get icon data by name (with alias support)
local function getIconData(iconName)
    local data = buildIconData()
    local lowerName = iconName:lower()
   
    -- Check main lookup
    if data[lowerName] then
        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",
        ["defend and fight!"] = "plot armor",
        ["conjure dragon"] = "bookwyrm",
        ["backstabber"] = "stalker",
        ["flying strike"] = "flying slash",
        ["tornado"] = "dust devil",
        ["kudzu bomb"] = "entangling thorns",
        ["kudzu connection"] = "watcher's covenant"
    }
   
    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
local function getLangData()
local function getLangData()
     if cachedLangData then
     if cachedLangData then
Line 196: Line 58:
     cachedLangCode = langCode
     cachedLangCode = langCode
     cachedLangData = langData
     cachedLangData = langData
    return langCode, langData
end
-- Infer image path from name, with override support for missing images
local function getImage(iconInfo)
    local lowerName = iconInfo.name:lower()
    return missingImageOverrides[lowerName] or (iconInfo.name .. ".png")
end


     return langCode, langData
-- Infer link from type and available fields
local function getLink(iconInfo)
     if iconInfo.type == "ability" then
        return iconInfo.hero_name or iconInfo.name
    end
    return linkOverrides[iconInfo.name] or iconInfo.name
end
end


-- Main render function
function p.render(frame)
function p.render(frame)
     local args = frame:getParent().args
     local args = frame:getParent().args
     local name = args[1] or ""
     local name = args[1] or ""
    local lowerName = name:lower()
    if aliases[lowerName] then
        lowerName = aliases[lowerName]
    end
    local iconInfo = iconData[lowerName]
    if not iconInfo then
        return string.format("[[:Category:Module:Icon ERROR|Icon not found]] ('%s'). [[Category:Module:Icon ERROR]]", name)
    end
     local customText = args.l1 or ""
     local customText = args.l1 or ""
   
    -- Set Defaults
     local size = args.size or "20px"
     local size = args.size or "20px"
     local noLink = args["no-link"] == "true"
     local noLink = args["no-link"] == "true"
     local iconOnly = args["icon-only"] == "true"
     local iconOnly = args["icon-only"] == "true"


    -- Scan unnamed parameters
     for k, v in pairs(args) do
     for k, v in pairs(args) do
         if type(k) == "number" and k > 1 then
         if type(k) == "number" and k > 1 then
             local val = mw.text.trim(v)
             local val = mw.text.trim(v)
           
             if val == "icon-only" then iconOnly = true
             if val == "icon-only" then
             elseif val == "no-link" then noLink = true
                iconOnly = true
             elseif val:match("^%d+px$") then size = val end
             elseif val == "no-link" then
                noLink = true
             elseif val:match("^%d+px$") then  
                size = val
            end
         end
         end
     end
     end


    -- Get icon data automatically from JSON
     local langCode, langData = getLangData()
     local iconData, err = getIconData(name)
    if not iconData then
        return "Error: " .. err
    end


     local langCode, langData = getLangData()
     local displayName = customText
      
     if displayName == "" then
    -- Determine display name
         if iconInfo.key and langData[iconInfo.key] then
    local displayName = ""
            displayName = langData[iconInfo.key]
    if customText ~= "" then
        else
         displayName = customText
            displayName = iconInfo.name
    elseif iconData.key and langData[iconData.key] then
        end
        displayName = langData[iconData.key]
    else
        displayName = iconData.name
     end
     end


    -- Localize link if on translated subpage
     local baseLink = getLink(iconInfo)
     local baseLink = linkOverrides[iconData.name] or iconData.link or ""
     local link = baseLink
     local link = baseLink
   
     if langCode ~= "en" and baseLink ~= "" then
     if langCode ~= "en" and baseLink ~= "" then
         link = baseLink .. "/" .. langCode
         link = baseLink .. "/" .. langCode
     end
     end


     -- Style logic (only abilities get theme class by default)
     -- Abilities get the module-icon-ability class for light/dark theme filter support
    local class = iconData.class or ""
     local style
     local style
     if class:find("theme", 1, true) then
     if iconInfo.type == "ability" then
         style = 'class="module-icon-ability" style="position:relative; bottom:2px;"'
         style = 'class="module-icon-ability" style="position:relative; bottom:2px;"'
     else
     else
Line 261: Line 129:
     end
     end


     -- Generate icon HTML
     local image = getImage(iconInfo)
     local imageLink = noLink and "" or link
     local imageLink = noLink and "" or link
     local iconHtml = string.format('<span %s>[[File:%s|%s|link=%s]]</span>', style, iconData.image, size, imageLink)
     local iconHtml = string.format('<span %s>[[File:%s|%s|link=%s]]</span>', style, image, size, imageLink)


     if iconOnly or displayName == "" then
     if iconOnly or displayName == "" then