Module:Icon: Difference between revisionsGive feedback
Made icons have a link. |
Refactor to use HeroData.json, AbilityData.json, and ItemData.jsonm; no more need for hardcoded data table; auto-detect icon types |
||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
-- Load | -- Load language codes | ||
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 | ||
-- Get icon data by name | -- 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() | |||
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 | |||
-- Get icon data by name (with alias support) | |||
local function getIconData(iconName) | local function getIconData(iconName) | ||
local | local data = buildIconData() | ||
if | local lowerName = iconName:lower() | ||
return | |||
-- 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" | |||
} | |||
if aliases[lowerName] then | |||
return data[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 | ||
-- | -- Language handling | ||
local function getLangData() | local function getLangData() | ||
if cachedLangData then | if cachedLangData then | ||
| Line 49: | Line 152: | ||
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 | if val == "icon-only" then | ||
| Line 65: | Line 167: | ||
noLink = true | noLink = true | ||
elseif val:match("^%d+px$") then | elseif val:match("^%d+px$") then | ||
size = val | size = val | ||
end | end | ||
| Line 71: | Line 172: | ||
end | end | ||
-- | -- Get icon data automatically from JSON | ||
local iconData, err = getIconData(name) | local iconData, err = getIconData(name) | ||
if not iconData then | if not iconData then | ||
| Line 79: | Line 180: | ||
local langCode, langData = getLangData() | local langCode, langData = getLangData() | ||
-- Determine display name | -- Determine display name | ||
local displayName = "" | local displayName = "" | ||
if customText ~= "" then | if customText ~= "" then | ||
| Line 85: | Line 186: | ||
elseif iconData.key and langData[iconData.key] then | elseif iconData.key and langData[iconData.key] then | ||
displayName = langData[iconData.key] | displayName = langData[iconData.key] | ||
else | |||
displayName = iconData.name | displayName = iconData.name | ||
end | end | ||
-- Localize link if on | -- Localize link if on translated subpage | ||
local baseLink = iconData.link or "" | local baseLink = iconData.link or "" | ||
local link = (langCode ~= "en" and baseLink ~= "") and (baseLink .. "/" .. langCode) or baseLink | local link = (langCode ~= "en" and baseLink ~= "") and (baseLink .. "/" .. langCode) or baseLink | ||
-- Style logic | -- Style logic (only abilities get theme class by default) | ||
local class = iconData.class or "" | local class = iconData.class or "" | ||
local extra = (class:find("invert", 1, true) and " filter:invert(1);" or "") | local extra = (class:find("invert", 1, true) and " filter:invert(1);" or "") | ||
| Line 107: | Line 204: | ||
end | end | ||
-- | -- Generate icon HTML | ||
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, iconData.image, size, imageLink) | ||
Revision as of 15:45, 9 February 2026
Main module for rendering game icons.
Note: This module is not intended for direct use. Use the templates below.
Data sources
The module reads from Data:ResourceLookup.json, a pre-indexed lookup table generated by Deadbot at deploy time. When the bot uploads new game files, hero, ability, and item icons update automatically.
An additional resource for other icons can be found at Data:ResourceLookup2.json, a manually maintained table; some entries may rely on a dictionary key.
See Module:Icon/data for manual overrides, legacy items, aliases, and renames used by this module.
Usage
Any of the following templates can be used. The module automatically detects the icon type:
{{HeroIcon}}{{AbilityIcon}}{{ItemIcon}}{{EntityIcon}}
Note: While all four templates work identically, it is recommended to use the appropriate one for clarity (e.g., use {{AbilityIcon|Siphon Life}} even though {{HeroIcon|Siphon Life}} will also work).
Error tracking
Icons that fail to render are added to Category:Module:Icon ERROR.
local p = {}
-- Load language codes
local lang_codes = mw.loadJsonData("Data:LangCodes.json")
-- Cache for compiled icon data (persists during page render)
local iconDataCache = nil
local cachedLangCode, cachedLangData
-- 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()
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
-- 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"
}
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()
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
cachedLangCode = langCode
cachedLangData = langData
return langCode, langData
end
-- Main render function
function p.render(frame)
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
-- Get icon data automatically from JSON
local iconData, err = getIconData(name)
if not iconData then
return "Error: " .. err
end
local langCode, langData = getLangData()
-- Determine display name
local displayName = ""
if customText ~= "" then
displayName = customText
elseif iconData.key and langData[iconData.key] then
displayName = langData[iconData.key]
else
displayName = iconData.name
end
-- Localize link if on translated subpage
local baseLink = iconData.link or ""
local link = (langCode ~= "en" and baseLink ~= "") and (baseLink .. "/" .. langCode) or baseLink
-- Style logic (only abilities get theme class by default)
local class = iconData.class or ""
local extra = (class:find("invert", 1, true) and " filter:invert(1);" or "")
local style
if class:find("theme", 1, true) then
style = 'class="module-icon-ability" style="position:relative; bottom:2px;' .. extra .. '"'
else
style = 'style="position:relative; bottom:2px;' .. extra .. '"'
end
-- Generate icon HTML
local imageLink = noLink and "" or link
local iconHtml = string.format('<span %s>[[File:%s|%s|link=%s]]</span>', style, iconData.image, size, imageLink)
if iconOnly or displayName == "" then
return iconHtml
end
local textHtml = (noLink or link == "") and displayName or string.format('[[%s|%s]]', link, displayName)
return '<span style="white-space:nowrap;">' .. iconHtml .. " " .. textHtml .. '</span>'
end
return p