Module:HeroClass
From Heroes of Might and Magic: Olden Era Official Wiki
Description
This module is a comprehensive utility for generating hero class profiles. It integrates statistical data, secondary skill probabilities, and subclass information into a unified, styled infobox layout. It handles multi-language support and automatic data linking via Cargo.
Usage
You can invoke specific sections or the entire main box:
{{#invoke:HeroList|getMainBox|class=...|language=...}}
{{#invoke:HeroList|getStatsTable|class=...}}
{{#invoke:HeroList|getSkillsTable|class=...}}
{{#invoke:HeroList|getSubclassesBlock|class=...}}
Parameters
| Parameter | Description | Example |
|---|---|---|
class |
The identifier or common name of the hero class (e.g., knight, herald, warlock). | class=Knight
|
language |
Forces the Wiki language (if omitted, it detects the page suffix). | language=fr
|
Key Features
- Main Infobox (
getMainBox): Combines class crests, base stats, skill probabilities, and subclasses into a cohesive, responsive container. - Dynamic Statistics (
getStatsTable): Displays base hero attributes along with growth rates at levels 1-12 and 13+. Features automatic icon rendering. - Skill Probabilities (
getSkillsTable): Retrieves "Skill Roll Weights" from Cargo to generate a comparison table showing the likelihood of learning specific secondary skills. - Subclass Integration (
getSubclassesBlock): Dynamically fetches subclass data, including localization for names and descriptions, and lists activation skills with icons. - Smart Localization: Automatically detects page language (e.g.,
/fr) and falls back to English if translations are missing in theTranslationtable.
Implementation Example
To display the full infobox for the "Knight" class:
{{#invoke:HeroList|getMainBox|class=Knight}}
Technical Notes
- The module uses several Cargo tables:
HeroClass,SkillRollWeight,HeroSubClass, andTranslation. - The
class_mappingtable within the script translates user-friendly names (e.g., "knight") into technical Cargo IDs (e.g., "might_human"). - CSS is embedded inline to ensure the infobox maintains a consistent look across different Wiki skins, using a dark-theme style (
#1a1f26background).
local p = {}
-- =========================================================================
-- CONFIGURATION & TABLES DE CORRESPONDANCE
-- =========================================================================
-- Associe le nom d'usage de la classe à son identifiant Cargo (class_id)
local class_mapping = {
["herald"] = "magic_demon",
["warlock"] = "magic_dungeon",
["cleric"] = "magic_human",
["druid"] = "magic_nature",
["necromancer"] = "magic_undead",
["riftspeaker"] = "magic_unfrozen",
["enforcer"] = "might_demon",
["overlord"] = "dungeon_might", -- Note : si le standard est "might_dungeon", le script s'adaptera aussi
["knight"] = "might_human",
["warden"] = "might_nature",
["death knight"] = "might_undead",
["oathkeeper"] = "might_unfrozen"
}
-- Ta structure de compétences (Matrice 12x2 complète)
local skills_structure = {
{ {"Offence", "", "skill_assault"}, {"Defence", "", "skill_protection"} },
{ {"Resistance", "", "skill_resistance"}, {"Battlecraft", "", "skill_formation"} },
{ {"Sorcery", "", "skill_sorcery"}, {"Intelligence", "", "skill_mastery"} },
{ {"Summon Avatar", "", "skill_summoner"}, {"Battle Magic", "", "skill_battlemage"} },
{ {"Daylight Magic", "", "skill_magic_day"}, {"Nightshade Magic", "", "skill_magic_night"} },
{ {"Arcane Magic", "", "skill_magic_space"}, {"Primal Magic", "", "skill_magic_primal"} },
{ {"Leadership", "", "skill_leadership"}, {"Luck", "", "skill_luck"} },
{ {"Insight", "", "skill_enlightenment"}, {"Diplomacy", "", "skill_diplomacy"} },
{ {"Logistics", "", "skill_logistic"}, {"Scouting", "", "skill_scouting"} },
{ {"Economy", "", "skill_economy"}, {"Tactics", "", "skill_tactics"} },
{ {"Siegecraft", "", "skill_siege"}, {"Recruitment", "", "skill_trainer"} },
{ {"Thaumaturgy", "", "skill_wisdom"}, {"Combat", "", "skill_battle_artistry"} }
}
-- =========================================================================
-- FONCTIONS UTILITAIRES INTERNES
-- =========================================================================
-- Détecte automatiquement la langue via le titre (ex: "Chevalier/fr" -> "fr")
local function getLanguageInfo()
local currentTitle = mw.title.getCurrentTitle().text
local lang_suffix = string.match(currentTitle, "/([a-z][a-z])$")
if lang_suffix then
return lang_suffix, "/" .. lang_suffix
else
return "en", ""
end
end
-- Génère automatiquement le nom de la table SQL à partir du class_id
-- Exemple : "might_human" -> "humans_might_skills_table"
-- Exemple : "magic_demon" -> "demons_magic_skills_table"
local function generateTableId(classId)
local type_part, faction_part = string.match(classId, "([a-z]+)_([a-z]+)")
-- Inversion pour gérer le cas particulier de "dungeon_might" si nécessaire
if not type_part or not faction_part then
faction_part, type_part = string.match(classId, "([a-z]+)_([a-z]+)")
end
if faction_part and type_part then
-- Gestion du pluriel automatique pour les noms de factions Anglaises courantes dans tes tables
if faction_part == "human" then faction_part = "humans"
elseif faction_part == "demon" then faction_part = "demons"
elseif faction_part == "dungeon" then faction_part = "dungeons"
elseif faction_part == "nature" then faction_part = "natures"
elseif faction_part == "undead" then faction_part = "undeads"
elseif faction_part == "unfrozen" then faction_part = "unfrozens"
end
return string.format("%s_%s_skills_table", faction_part, type_part)
end
return nil
end
-- 1. RÉCUPÉRATION DES POIDS (Depuis ta table Cargo SkillRollWeight)
local function fetchWeightsFromCargo(tableId)
local weights = {}
if not tableId then return weights end
local res = mw.ext.cargo.query("SkillRollWeight", "skill_id, weight", {
where = string.format("table_id = '%s' AND band_kind = 'default'", tableId),
limit = 100
})
if res then
for _, row in ipairs(res) do
if row.skill_id and row.weight then
weights[string.lower(mw.text.trim(row.skill_id))] = row.weight
end
end
end
return weights
end
-- 2. RÉCUPÉRATION DES TRADUCTIONS (Depuis ta table Cargo Translation)
local function fetchTranslationsFromCargo(lang)
local translations = {}
local tables = "Skill, Translation"
local fields = "Skill.id=id, Translation.name=name"
local query_opts = {
join = "Skill.id = Translation.target_id",
where = string.format("Translation.type = 'skill' AND Translation.language = '%s'", lang),
limit = 300
}
local res = mw.ext.cargo.query(tables, fields, query_opts)
if res then
for _, row in ipairs(res) do
if row.id and row.name then
translations[string.lower(mw.text.trim(row.id))] = mw.text.trim(row.name)
end
end
end
-- Secours anglais si la traduction locale a des trous
if lang ~= "en" then
query_opts.where = "Translation.type = 'skill' AND Translation.language = 'en'"
local backup_res = mw.ext.cargo.query(tables, fields, query_opts)
if backup_res then
for _, row in ipairs(backup_res) do
local id_clean = string.lower(mw.text.trim(row.id or ""))
if id_clean ~= "" and not translations[id_clean] then
translations[id_clean] = mw.text.trim(row.name or "")
end
end
end
end
return translations
end
-- =========================================================================
-- FONCTION PRINCIPALE
-- =========================================================================
function p.getSkillsTable(frame)
local args = frame.args[1] and frame.args or frame:getParent().args
local lang, suffix = getLanguageInfo()
local raw_class_name = string.lower(mw.text.trim(args.class or args[1] or ""))
local class_id = class_mapping[raw_class_name]
if not class_id then
return ""
end
-- Génération dynamique de l'ID de la table de tirage (ex: "humans_might_skills_table")
local table_id = generateTableId(class_id)
-- Appel Cargo 1 : Récupération des poids
local weight_dictionary = fetchWeightsFromCargo(table_id)
-- Appel Cargo 2 : Récupération des traductions
local translation_dictionary = fetchTranslationsFromCargo(lang)
local html = {}
table.insert(html, '{| class="wikitable" style="width:100%; border-collapse:collapse;"')
for _, double_row in ipairs(skills_structure) do
local s1, s2 = double_row[1], double_row[2]
local label1 = translation_dictionary[s1[3]] or s1[1]
local label2 = translation_dictionary[s2[3]] or s2[1]
local val1 = weight_dictionary[s1[3]] or "0"
local val2 = weight_dictionary[s2[3]] or "0"
table.insert(html, '|-')
table.insert(html, string.format('| [[File:Skill_%s.png|32px|link=%s%s]] %s || %s || [[File:Skill_%s.png|32px|link=%s%s]] %s || %s',
s1[1], s1[1], suffix, label1, val1,
s2[1], s2[1], suffix, label2, val2
))
end
table.insert(html, '|}')
return table.concat(html, "\n")
end
function p.getSubclassesBlock(frame)
return ""
end
return p


