Module:Sandbox/Aurelain/SkillsOverview: Difference between revisions
From Heroes of Might and Magic: Olden Era Official Wiki
No edit summary |
No edit summary |
||
| Line 274: | Line 274: | ||
for _, group in pairs(skills) do | for _, group in pairs(skills) do | ||
local id = group.id | local id = group.id | ||
local category = CATEGORIES[id] | local category = CATEGORIES[id] | ||
if category and category ~= currentCategory then | if category and category ~= currentCategory then | ||
| Line 279: | Line 280: | ||
root:tag('h2'):wikitext(currentCategory) | root:tag('h2'):wikitext(currentCategory) | ||
end | end | ||
local ranks = group.ranks or {} | local ranks = group.ranks or {} | ||
root:tag('h3'):addClass(''):wikitext('[[' .. group.name .. ']] [[File:' .. ranks[1].icon .. '.png|16px]]') | |||
for n, rank in ipairs(ranks) do | for n, rank in ipairs(ranks) do | ||
Revision as of 06:54, 3 June 2026
Documentation for this module may be created at Module:Sandbox/Aurelain/SkillsOverview/doc
-- Usage: {{#invoke:SkillsOverview|display|lang=en}}
local p = {}
local CATEGORY_MIGHT = 1
local CATEGORY_MAGIC = 2
local CATEGORY_GENERAL = 3
local CATEGORY_FACTION = 4
local CATEGORIES = {
skill_assault = CATEGORY_MIGHT, -- Offense
skill_battle_artistry = CATEGORY_MIGHT, -- Combat
skill_battlemage = CATEGORY_MAGIC, -- Battle Magic
skill_diplomacy = CATEGORY_GENERAL, -- Diplomacy
skill_economy = CATEGORY_GENERAL, -- Economy
skill_enlightenment = CATEGORY_GENERAL, -- Insight
skill_faction_demons = CATEGORY_FACTION, -- Hive
skill_faction_dungeon = CATEGORY_FACTION, -- Dungeon
skill_faction_humans = CATEGORY_FACTION, -- Temple
skill_faction_nature = CATEGORY_FACTION, -- Grove
skill_faction_undead = CATEGORY_FACTION, -- Necropolis
skill_faction_unfrozen = CATEGORY_FACTION, -- Schism
skill_first_aid = CATEGORY_GENERAL, -- currently unused!
skill_formation = CATEGORY_MIGHT, -- Battlecraft
skill_leadership = CATEGORY_MIGHT, -- Leadership
skill_logistic = CATEGORY_GENERAL, -- Logistics
skill_luck = CATEGORY_MIGHT, -- Luck
skill_magic_day = CATEGORY_MAGIC, -- Daylight
skill_magic_night = CATEGORY_MAGIC, -- Nightshade
skill_magic_primal = CATEGORY_MAGIC, -- Primal
skill_magic_space = CATEGORY_MAGIC, -- Arcane
skill_mastery = CATEGORY_MAGIC, -- Wisdom
skill_protection = CATEGORY_MIGHT, -- Defense
skill_resistance = CATEGORY_MIGHT, -- Resistance
skill_scouting = CATEGORY_GENERAL, -- Scouting
skill_siege = CATEGORY_MIGHT, -- Siegecraft
skill_sorcery = CATEGORY_MAGIC, -- Sorcery
skill_summoner = CATEGORY_MAGIC, -- Summon Avatar
skill_tactics = CATEGORY_MIGHT, -- Tactics
skill_trainer = CATEGORY_MIGHT, -- Recruitment
skill_wisdom = CATEGORY_MAGIC, -- Thaumaturgy
}
local ORDER = {
'skill_assault', -- Offense
'skill_protection', -- Defense
'skill_leadership', -- Leadership
'skill_luck', -- Luck
'skill_resistance', -- Resistance
'skill_tactics', -- Tactics
'skill_formation', -- Battlecraft
'skill_siege', -- Siegecraft
'skill_trainer', -- Recruitment
'skill_battle_artistry', -- Combat
'skill_battlemage', -- Battle Magic
'skill_sorcery', -- Sorcery
'skill_summoner', -- Summon Avatar
'skill_mastery', -- Wisdom
'skill_magic_day', -- Daylight
'skill_magic_night', -- Nightshade
'skill_magic_space', -- Arcane
'skill_magic_primal', -- Primal
'skill_wisdom', -- Thaumaturgy
'skill_diplomacy', -- Diplomacy
'skill_logistic', -- Logistics
'skill_scouting', -- Scouting
'skill_enlightenment', -- Insight
'skill_economy', -- Economy
'skill_faction_humans', -- Temple
'skill_faction_undead', -- Necropolis
'skill_faction_nature', -- Grove
'skill_faction_demons', -- Hive
'skill_faction_unfrozen', -- Schism
'skill_faction_dungeon', -- Dungeon
'skill_first_aid', -- currently unused!
}
------------------------------------------------------------------------------------------------------------------------
-- Debugs a variable
------------------------------------------------------------------------------------------------------------------------
local function dump(target)
return '<pre>' .. mw.dumpObject(target) .. '</pre>'
end
------------------------------------------------------------------------------------------------------------------------
-- Retrieves the main skill translations from Cargo
------------------------------------------------------------------------------------------------------------------------
local function queryMain(lang, forcedSkill)
-- fields:
local fields = {}
table.insert(fields, 'Skill.id = id');
table.insert(fields, 'Translation.variant = variant');
table.insert(fields, 'Translation.name = name');
table.insert(fields, 'Translation.description = description');
-- where:
local where = {}
if forcedSkill then
table.insert(where, 'Skill.id="' .. forcedSkill .. '"')
end
table.insert(where, 'Skill.variant = "production"')
table.insert(where, 'Translation.language = "' .. lang .. '"')
-- query:
return mw.ext.cargo.query('Skill, Translation', table.concat(fields, ','), {
join = 'Skill.id = Translation.target_id',
where = table.concat(where, ' AND '),
limit = 200
})
end
------------------------------------------------------------------------------------------------------------------------
-- Retrieves the icons and the offered sub-skills from Cargo
------------------------------------------------------------------------------------------------------------------------
local function queryIconAndOffers(forcedSkill)
-- fields:
local fields = {}
table.insert(fields, 'SkillLevel.skill_id = id');
table.insert(fields, 'SkillLevel.level = level');
table.insert(fields, 'SkillLevel.icon = icon');
table.insert(fields, 'SkillLevel.offered_sub_skills = offered_sub_skills');
-- where:
local where = {}
if forcedSkill then
table.insert(where, 'SkillLevel.skill_id="' .. forcedSkill .. '"')
end
table.insert(where, 'SkillLevel.skill_id NOT LIKE "arena%"')
table.insert(where, 'SkillLevel.skill_id NOT LIKE "campaign%"')
-- query:
return mw.ext.cargo.query('SkillLevel', table.concat(fields, ','), {
where = table.concat(where, ' AND '),
limit = 200
})
end
------------------------------------------------------------------------------------------------------------------------
-- Retrieves the most important data from Cargo
------------------------------------------------------------------------------------------------------------------------
local function querySubs(lang, forcedSkill)
-- fields:
local fields = {}
table.insert(fields, 'SubSkill.id = id');
table.insert(fields, 'SubSkill.parent_skill_id = parent_skill_id');
table.insert(fields, 'SubSkill.icon = icon');
table.insert(fields, 'Translation.name = name');
table.insert(fields, 'Translation.description = description');
-- where:
local where = {}
if forcedSkill then
table.insert(where, 'SubSkill.parent_skill_id="' .. forcedSkill .. '"')
end
table.insert(where, 'SubSkill.variant = "production"')
table.insert(where, 'Translation.language = "' .. lang .. '"')
-- query:
return mw.ext.cargo.query('SubSkill, Translation', table.concat(fields, ','), {
join = 'SubSkill.id = Translation.target_id',
where = table.concat(where, ' AND '),
limit = 500
})
end
------------------------------------------------------------------------------------------------------------------------
-- Folds various skill results from Cargo into a single table
------------------------------------------------------------------------------------------------------------------------
local function consolidateSkills(main, iconAndOffers, subs)
local hub = {}
for _, row in ipairs(main) do
local id = row.id
local variant = row.variant
hub[id] = hub[id] or { id = id, ranks = {} }
local entry = hub[id]
if variant then
entry.ranks[tonumber(variant)] = {
name = row.name or '',
description = row.description or '',
}
else
entry.name = row.name
end
end
for _, row in ipairs(iconAndOffers) do
local id = row.id
local entry = hub[id] or { ranks = {} }
local rank = entry.ranks[tonumber(row.level)] or {}
rank.icon = row.icon or ''
local csv = row.offered_sub_skills
local ids = csv and mw.text.split(csv, ',') or {}
rank.subSkills = {}
for _, subId in ipairs(ids) do
rank.subSkills[subId] = {}
end
end
for _, row in ipairs(subs) do
local id = row.id
local parentId = row.parent_skill_id
local entry = hub[parentId] or { ranks = {} }
for _, rank in ipairs(entry.ranks) do
local subSkills = rank.subSkills or {}
if subSkills[id] then
subSkills[id].icon = row.icon
subSkills[id].name = row.name
subSkills[id].description = row.description
break
end
end
end
return hub
end
------------------------------------------------------------------------------------------------------------------------
-- Converts a dictionary into a linear array
------------------------------------------------------------------------------------------------------------------------
local function flatten(hub)
local list = {}
for _, entry in pairs(hub) do
list[#list + 1] = entry
end
return list
end
------------------------------------------------------------------------------------------------------------------------
-- Converts an array into a dictionary, where the array.value becomes dic.key and array.index becomes dic.value
------------------------------------------------------------------------------------------------------------------------
local function flipArray(array)
local dic = {}
for index, value in ipairs(array) do
dic[value] = index
end
return dic
end
local ID_TO_ORDER = flipArray(ORDER)
------------------------------------------------------------------------------------------------------------------------
-- Decides the order between two skills.
------------------------------------------------------------------------------------------------------------------------
local function compareSkills(a, b)
local rankA = ID_TO_ORDER[a.id] or 99
local rankB = ID_TO_ORDER[b.id] or 99
if rankA ~= rankB then
return rankA < rankB
end
return a.name < b.name
end
------------------------------------------------------------------------------------------------------------------------
--
------------------------------------------------------------------------------------------------------------------------
local function wrapQuotes(str)
if not str then return nil end
local result = string.gsub(str, "“_*(.-)_*”", "[[%1]]")
return result
end
------------------------------------------------------------------------------------------------------------------------
-- Displays a single skill (icon+name+description)
------------------------------------------------------------------------------------------------------------------------
local function createSkill(target, size)
local root = mw.html.create('div'):addClass('box')
root:tag('div'):addClass('icon-container'):wikitext('[[File:' .. target.icon .. '.png|' .. size .. 'px]]')
local textContainer = root:tag('div'):addClass('text-container')
textContainer:tag('div'):addClass('box-name'):wikitext('<b>' .. target.name .. '</b>')
textContainer:tag('div'):addClass('box-description'):wikitext(wrapQuotes(target.description))
return root
end
------------------------------------------------------------------------------------------------------------------------
-- Displays the main content
------------------------------------------------------------------------------------------------------------------------
local function renderSkills(skills)
local root = mw.html.create()
local currentCategory = nil
for _, group in pairs(skills) do
local id = group.id
local category = CATEGORIES[id]
if category and category ~= currentCategory then
currentCategory = category
root:tag('h2'):wikitext(currentCategory)
end
local ranks = group.ranks or {}
root:tag('h3'):addClass(''):wikitext('[[' .. group.name .. ']] [[File:' .. ranks[1].icon .. '.png|16px]]')
for n, rank in ipairs(ranks) do
root:node(createSkill(rank, 64))
local sub = root:tag('div'):addClass('sub')
local subSkills = rank.subSkills or {}
for _, subSkill in pairs(subSkills) do
sub:node(createSkill(subSkill, 32))
end
end
end
return tostring(root)
end
------------------------------------------------------------------------------------------------------------------------
-- Main public function
------------------------------------------------------------------------------------------------------------------------
function p.display(frame)
-- Args
local args = frame.args
local lang = args.lang or 'en'
local skill = args.skill
-- Cargo
local main = queryMain(lang, skill)
if #main == 0 then
return 'Cannot find skill "' .. skill .. '"'
end
local iconAndOffers = queryIconAndOffers(skill)
local subs = querySubs(lang, skill)
local hub = consolidateSkills(main, iconAndOffers, subs)
local skills = flatten(hub);
-- Various manipulations
table.sort(skills, compareSkills)
-- Content
local markup = renderSkills(skills)
-- Output
local styleTag = frame:extensionTag('templatestyles', '', { src = frame:getTitle() .. '/styles.css' })
--return dump(skills)
return styleTag .. markup
end
return p


