Module:Icon: Difference between revisionsGive feedback
Fix expensive parser function limit errors by removing title.exists check, now uses direct linking for localized pages |
Testing fix for many icons on one page |
||
| 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 (persists during page render) | -- Cache for compiled icon data, split by type (persists during page render) | ||
local iconDataCache = | local heroCacheBuilt = false | ||
local abilityCacheBuilt = false | |||
local itemCacheBuilt = false | |||
local iconDataCache = {} | |||
local cachedLangCode, cachedLangData | local cachedLangCode, cachedLangData | ||
| Line 25: | Line 29: | ||
} | } | ||
-- Build | -- Build lookup table from bot-uploaded JSONs, only loading the relevant JSON per type | ||
local function buildIconData() | -- to reduce CPU cost on pages with many icon calls | ||
local function buildIconData(iconType) | |||
-- Only load hero data if not already built | |||
if iconType == "hero" and not heroCacheBuilt then | |||
local heroData = mw.loadJsonData("Data:HeroData.json") | |||
-- 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") | |||
iconDataCache[lowerName] = { | |||
name = name, | |||
key = heroKey, | |||
link = name, | |||
image = imageName, | |||
type = "hero" | |||
} | |||
end | |||
end | |||
heroCacheBuilt = true | |||
-- Only load ability data (and hero data for parent linking) if not already built | |||
elseif iconType == "ability" and not abilityCacheBuilt then | |||
local heroData = mw.loadJsonData("Data:HeroData.json") | |||
local abilityData = mw.loadJsonData("Data:AbilityData.json") | |||
-- Track which hero owns which ability (for linking) | |||
if hero.BoundAbilities then | local abilityToHero = {} | ||
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 = | heroName = hero.Name, | ||
abilityName = ability.Name | abilityName = ability.Name | ||
} | } | ||
| Line 70: | Line 77: | ||
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 iconDataCache[lowerName] and iconDataCache[lowerName].type == "ability" then | |||
local existingHasParent = iconDataCache[lowerName].link ~= iconDataCache[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 | |||
iconDataCache[lowerName] = { | |||
name = name, | |||
key = abilityKey, | |||
link = link, | |||
image = name .. ".png", | |||
type = "ability", | |||
class = "theme" | |||
} | |||
end | |||
else | else | ||
-- | -- No existing entry, add normally | ||
local link = parentInfo and parentInfo.heroName or name | local link = parentInfo and parentInfo.heroName or name | ||
iconDataCache[lowerName] = { | |||
name = name, | name = name, | ||
key = abilityKey, | key = abilityKey, | ||
| Line 99: | Line 117: | ||
} | } | ||
end | end | ||
end | |||
end | |||
local | |||
abilityCacheBuilt = true | |||
-- 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 = | key = itemKey, | ||
link = | link = name, | ||
image = name .. ".png", | image = name .. ".png", | ||
type = " | type = "item" | ||
} | } | ||
end | end | ||
end | end | ||
itemCacheBuilt = true | |||
end | end | ||
end | end | ||
-- Get icon data by name (with alias support) | -- Get icon data by name and type (with alias support) | ||
local function getIconData(iconName) | local function getIconData(iconName, iconType) | ||
buildIconData(iconType) | |||
local lowerName = iconName:lower() | local lowerName = iconName:lower() | ||
-- Check main lookup | -- Check main lookup | ||
if | if iconDataCache[lowerName] then | ||
return | return iconDataCache[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 153: | Line 165: | ||
["debuff remover"] = "dispel magic" | ["debuff remover"] = "dispel magic" | ||
} | } | ||
if aliases[lowerName] then | if aliases[lowerName] then | ||
return | return iconDataCache[aliases[lowerName]], nil | ||
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 192: | Line 204: | ||
local name = args[1] or "" | local name = args[1] or "" | ||
local customText = args.l1 or "" | local customText = args.l1 or "" | ||
local iconType = args.type or "item" | |||
-- Set Defaults | -- Set Defaults | ||
local size = args.size or "20px" | local size = args.size or "20px" | ||
| Line 202: | Line 215: | ||
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 213: | Line 226: | ||
end | end | ||
-- Get icon data automatically from JSON | -- Get icon data automatically from JSON, using type to limit which JSON is loaded | ||
local iconData, err = getIconData(name) | local iconData, err = getIconData(name, iconType) | ||
if not iconData then | if not iconData then | ||
return "Error: " .. err | return "Error: " .. err | ||
| Line 220: | Line 233: | ||
local langCode, langData = getLangData() | local langCode, langData = getLangData() | ||
-- Determine display name | -- Determine display name | ||
local displayName = "" | local displayName = "" | ||
| Line 234: | Line 247: | ||
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 | ||