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 the Translation table.

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, and Translation.
  • The class_mapping table 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 (#1a1f26 background).

local p = {}

-- =========================================================================
-- CONFIGURATION
-- =========================================================================

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",
    ["knight"]       = "might_human", 
    ["warden"]       = "might_nature",
    ["death knight"] = "might_undead",
    ["oathkeeper"]   = "might_unfrozen"
}

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"} }
}

local skill_ids_to_images = {}
for _, double_row in ipairs(skills_structure) do
    skill_ids_to_images[double_row[1][3]] = double_row[1][1]
    skill_ids_to_images[double_row[2][3]] = double_row[2][1]
end

-- =========================================================================
-- FONCTIONS UTILITAIRES
-- =========================================================================

local function getLanguageInfo()
    local currentTitle = mw.title.getCurrentTitle().text
    local lang_suffix = string.match(currentTitle, "/([a-z][a-z])$")
    return lang_suffix or "en", lang_suffix and ("/" .. lang_suffix) or ""
end

local function parseClassId(classId)
    local type_part, faction_part = string.match(classId, "([a-z]+)_([a-z]+)")
    if not type_part then faction_part, type_part = string.match(classId, "([a-z]+)_([a-z]+)") end
    return type_part, faction_part
end

local function generateTableId(classId)
    local type_part, faction_part = parseClassId(classId)
    local table_names = { ["human"]="humans", ["demon"]="demons", ["undead"]="necros", ["nature"]="nature", ["dungeon"]="dungeon", ["unfrozen"]="unfrozen" }
    local faction_key = table_names[faction_part] or faction_part
    return faction_key and type_part and string.format("%s_%s_skills_table", faction_key, type_part) or nil
end

-- =========================================================================
-- TABLEAU DES STATISTIQUES
-- =========================================================================

function p.getStatsTable(frame)
    local args = frame.args[1] and frame.args or frame:getParent().args
    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 "Classe non trouvée" end

    local res = mw.ext.cargo.query('HeroClass', 'offence, roll_lvl1_attack, roll_lvl24_attack, defence, roll_lvl1_defense, roll_lvl24_defense, spell_power, roll_lvl1_power, roll_lvl24_power, intelligence, roll_lvl1_knowledge, roll_lvl24_knowledge', { where = string.format('id="%s"', class_id), limit = 1 })
    if not res or #res == 0 then return "Données statistiques introuvables" end
    local data = res[1]

    local html = {'{| class="wikitable" style="width:100%; border-collapse:collapse;"'}
    table.insert(html, '! Carac. !! Base !! Niv. 1-12 !! Niv. 13+')
    table.insert(html, string.format('|- \n | [[File:Icon Stats Attack.png|24px]] Attack || %s || %s%% || %s%%', data.offence or 0, data.roll_lvl1_attack or 0, data.roll_lvl24_attack or 0))
    table.insert(html, string.format('|- \n | [[File:Icon Stats Defence.png|24px]] Defense || %s || %s%% || %s%%', data.defence or 0, data.roll_lvl1_defense or 0, data.roll_lvl24_defense or 0))
    table.insert(html, string.format('|- \n | [[File:Icon Stats Spellpower.png|24px]] Spellpower || %s || %s%% || %s%%', data.spell_power or 0, data.roll_lvl1_power or 0, data.roll_lvl24_power or 0))
    table.insert(html, string.format('|- \n | [[File:Icon Stats Inteligence.png|24px]] Knowledge || %s || %s%% || %s%%', data.intelligence or 0, data.roll_lvl1_knowledge or 0, data.roll_lvl24_knowledge or 0))
    table.insert(html, '|}')
    return table.concat(html, "\n")
end

-- =========================================================================
-- FONCTION GLOBALE
-- =========================================================================

function p.getMainBox(frame)
    local args = frame.args[1] and frame.args or frame:getParent().args
    local raw_class_name = mw.text.trim(args.class or args[1] or "")
    if raw_class_name == "" then return "Erreur : nom de classe manquant" end
    
    local image_class_name = string.upper(string.sub(raw_class_name, 1, 1)) .. string.sub(raw_class_name, 2)
    local html = {}
    
    table.insert(html, '<div style="max-width:420px; border: 1px solid #3a3a3a; border-radius: 8px; background-color: #1a1f26; padding: 16px;">')
    table.insert(html, string.format('<div style="text-align: center;">[[File:%s.png|120px]]</div>', image_class_name))
    table.insert(html, p.getStatsTable(frame))
    table.insert(html, p.getSkillsTable(frame)) -- Assure-toi que p.getSkillsTable est bien défini
    table.insert(html, '</div>')
    
    return table.concat(html, "\n")
end

return p