Module:Icon: Difference between revisions

LVL (talk | contribs)
Small fix
LVL (talk | contribs)
Reverted attempted fix
Tag: Manual revert
Line 4: Line 4:
local lang_codes = mw.loadJsonData("Data:LangCodes.json")
local lang_codes = mw.loadJsonData("Data:LangCodes.json")


-- Cache for compiled icon data, split by type (persists during page render)
-- Cache for compiled icon data (persists during page render)
local heroCacheBuilt = false
local iconDataCache = nil
local abilityCacheBuilt = false
local itemCacheBuilt = false
local iconDataCache = {}
 
local cachedLangCode, cachedLangData
local cachedLangCode, cachedLangData


Line 29: Line 25:
}
}


-- Build lookup table from bot-uploaded JSONs, only loading the relevant JSON per type
-- Build unified lookup table from bot-uploaded JSONs
-- to reduce CPU cost on pages with many icon calls
local function buildIconData()
local function buildIconData(iconType)
    if iconDataCache then
     -- Only load hero data if not already built
        return iconDataCache
     if iconType == "hero" and not heroCacheBuilt then
    end
        local heroData = mw.loadJsonData("Data:HeroData.json")
   
 
    local lookup = {}
        -- Process Heroes
   
        for heroKey, hero in pairs(heroData) do
     -- Load bot data
            if type(hero) == "table" and hero.Name then
     local heroData = mw.loadJsonData("Data:HeroData.json")
                local name = hero.Name
    local abilityData = mw.loadJsonData("Data:AbilityData.json")
                local lowerName = name:lower()
    local itemData = mw.loadJsonData("Data:ItemData.json")
 
   
                -- Check for missing image override
    -- Track which hero owns which ability (for linking)
                local imageName = missingImageOverrides[lowerName] or (name .. ".png")
    local abilityToHero = {}
 
   
                iconDataCache[lowerName] = {
    -- Process Heroes
                    name = name,
    for heroKey, hero in pairs(heroData) do
                    key = heroKey,
        if type(hero) == "table" and hero.Name then
                    link = name,
            local name = hero.Name
                    image = imageName,
            local lowerName = name:lower()
                    type = "hero"
           
                }
            -- Check for missing image override
             end
            local imageName = missingImageOverrides[lowerName] or (name .. ".png")
        end
           
 
            lookup[lowerName] = {
        heroCacheBuilt = true
                name = name,
 
                key = heroKey,
    -- Only load ability data (and hero data for parent linking) if not already built
                link = name,
    elseif iconType == "ability" and not abilityCacheBuilt then
                image = imageName,
        local heroData = mw.loadJsonData("Data:HeroData.json")
                type = "hero"
        local abilityData = mw.loadJsonData("Data:AbilityData.json")
            }
 
              
        -- Track which hero owns which ability (for linking)
            -- Map abilities to parent hero
        local abilityToHero = {}
             if hero.BoundAbilities then
 
        for heroKey, hero in pairs(heroData) do
             if type(hero) == "table" and hero.BoundAbilities then
                 for slot, ability in pairs(hero.BoundAbilities) do
                 for slot, ability in pairs(hero.BoundAbilities) do
                     if ability.Key then
                     if ability.Key then
                         abilityToHero[ability.Key] = {
                         abilityToHero[ability.Key] = {
                             heroName = hero.Name,
                             heroName = name,
                             abilityName = ability.Name
                             abilityName = ability.Name
                         }
                         }
Line 77: Line 70:
             end
             end
         end
         end
 
    end
        -- Process Abilities
   
        for abilityKey, ability in pairs(abilityData) do
-- Process Abilities
            if type(ability) == "table" and ability.Name then
    for abilityKey, ability in pairs(abilityData) do
                local name = ability.Name
        if type(ability) == "table" and ability.Name then
                local lowerName = name:lower()
            local name = ability.Name
                local parentInfo = abilityToHero[abilityKey]
            local lowerName = name:lower()
 
            local parentInfo = abilityToHero[abilityKey]
                -- If we already have this ability name, prefer the one linked to a hero
           
                if iconDataCache[lowerName] and iconDataCache[lowerName].type == "ability" then
            -- If we already have this ability name, prefer the one linked to a hero
                    local existingHasParent = iconDataCache[lowerName].link ~= iconDataCache[lowerName].name
            if lookup[lowerName] and lookup[lowerName].type == "ability" then
                    local newHasParent = parentInfo ~= nil
                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 if existing is linked to hero but this one isn't (prevents orphans from overwriting real ones)
                        -- Skip this orphan ability
                if existingHasParent and not newHasParent then
                    else
                    -- Skip this orphan ability
                        -- Overwrite: new has parent and existing doesn't, or both/neither have parents
                        local link = parentInfo and parentInfo.heroName or name
                        iconDataCache[lowerName] = {
                            name = name,
                            key = abilityKey,
                            link = link,
                            image = name .. ".png",
                            type = "ability",
                            class = "theme"
                        }
                    end
                 else
                 else
                     -- No existing entry, add normally
                     -- Overwrite: new has parent and existing doesn't, or both/neither have parents
                     local link = parentInfo and parentInfo.heroName or name
                     local link = parentInfo and parentInfo.heroName or name
                     iconDataCache[lowerName] = {
                     lookup[lowerName] = {
                         name = name,
                         name = name,
                         key = abilityKey,
                         key = abilityKey,
Line 117: Line 99:
                     }
                     }
                 end
                 end
             end
             else
        end
                -- No existing entry, add normally
 
                 local link = parentInfo and parentInfo.heroName or name
        abilityCacheBuilt = true
                 lookup[lowerName] = {
 
    -- Only load item data if not already built
    elseif iconType == "item" and not itemCacheBuilt then
        local itemData = mw.loadJsonData("Data:ItemData.json")
 
        -- 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()
 
                 iconDataCache[lowerName] = {
                     name = name,
                     name = name,
                     key = itemKey,
                     key = abilityKey,
                     link = name,
                     link = link,
                     image = name .. ".png",
                     image = name .. ".png",
                     type = "item"
                     type = "ability",
                    class = "theme"
                 }
                 }
             end
             end
         end
         end
        itemCacheBuilt = true
     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


-- Get icon data by name and type (with alias support)
-- Get icon data by name (with alias support)
local function getIconData(iconName, iconType)
local function getIconData(iconName)
     buildIconData(iconType)
     local data = buildIconData()
     local lowerName = iconName:lower()
     local lowerName = iconName:lower()
 
   
     -- Check main lookup
     -- Check main lookup
     if iconDataCache[lowerName] then
     if data[lowerName] then
         return iconDataCache[lowerName], nil
         return data[lowerName], nil
     end
     end
 
   
     -- Check aliases (for edge cases like "doorman" -> "the doorman")
     -- Check aliases (for edge cases like "doorman" -> "the doorman")
     local aliases = {
     local aliases = {
Line 165: Line 153:
         ["debuff remover"] = "dispel magic"
         ["debuff remover"] = "dispel magic"
     }
     }
 
   
     if aliases[lowerName] then
     if aliases[lowerName] then
         local entry = iconDataCache[aliases[lowerName]]
         return data[aliases[lowerName]], nil
        if entry then
            return entry, nil
        end
     end
     end
 
   
     return nil, string.format("[[:Category:Module:Icon ERROR|Icon not found]] ('%s'). [[Category:Module:Icon ERROR]]", iconName)
     return nil, string.format("[[:Category:Module:Icon ERROR|Icon not found]] ('%s'). [[Category:Module:Icon ERROR]]", iconName)
end
end
Line 207: Line 192:
     local name = args[1] or ""
     local name = args[1] or ""
     local customText = args.l1 or ""
     local customText = args.l1 or ""
     -- type is passed via #invoke so must be read from frame.args, not parent args
      
    local iconType = frame.args.type or "item"
 
     -- Set Defaults
     -- Set Defaults
     local size = args.size or "20px"
     local size = args.size or "20px"
Line 219: Line 202:
         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
             if val == "icon-only" then
                 iconOnly = true
                 iconOnly = true
             elseif val == "no-link" then
             elseif val == "no-link" then
                 noLink = true
                 noLink = true
             elseif val:match("^%d+px$") then
             elseif val:match("^%d+px$") then  
                 size = val
                 size = val
             end
             end
Line 230: Line 213:
     end
     end


     -- Get icon data automatically from JSON, using type to limit which JSON is loaded
     -- Get icon data automatically from JSON
     local iconData, err = getIconData(name, iconType)
     local iconData, err = getIconData(name)
     if not iconData then
     if not iconData then
         return "Error: " .. err
         return "Error: " .. err
Line 237: Line 220:


     local langCode, langData = getLangData()
     local langCode, langData = getLangData()
 
   
     -- Determine display name
     -- Determine display name
     local displayName = ""
     local displayName = ""
Line 251: Line 234:
     local baseLink = iconData.link or ""
     local baseLink = 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