Module:HeroClassDataParser/fr
From Heroes of Might and Magic: Olden Era Official Wiki
Documentation for this module may be created at Module:HeroClassDataParser/fr/doc
local p = {}
-- =========================================================================
-- CONFIGURATION & TABLES DE CORRESPONDANCE
-- =========================================================================
-- Associe les noms de classes saisis par les utilisateurs aux clés réelles du fichier JSON
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"
}
-- Structure du tableau d'affichage des 22 compétences secondaires.
-- Format : { { Nom_Anglais_Image, Clé_JSON_Pourcentages, Id_Exact_Table_Cargo }, ... }
local skills_structure = {
{ {"Offence", "offenceSkill", "skill_assault"}, {"Defence", "defenceSkill", "skill_protection"} },
{ {"Resistance", "resistanceSkill", "skill_resistance"}, {"Battlecraft", "battlecraftSkill", "skill_formation"} },
{ {"Sorcery", "sorcerySkill", "skill_sorcery"}, {"Intelligence", "intelligenceSkill", "skill_mastery"} },
{ {"Summon Avatar", "summonAvatarSkill", "skill_summoner"}, {"Battle Magic", "battleMagicSkill", "skill_battlemage"} },
{ {"Daylight Magic", "daylightSkill", "skill_magic_day"}, {"Nightshade Magic", "nightshadeSkill", "skill_magic_night"} },
{ {"Arcane Magic", "arcaneSkill", "skill_magic_space"}, {"Primal Magic", "primalSkill", "skill_magic_primal"} },
{ {"Leadership", "leadershipSkill", "skill_leadership"}, {"Luck", "luckSkill", "skill_luck"} },
{ {"Insight", "insightSkill", "skill_enlightenment"}, {"Diplomacy", "diplomacySkill", "skill_diplomacy"} },
{ {"Logistics", "logisticsSkill", "skill_logistic"}, {"Scouting", "scoutingSkill", "skill_scouting"} },
{ {"Economy", "economySkill", "skill_economy"}, {"Tactics", "tacticsSkill", "skill_tactics"} },
{ {"Siegecraft", "siegecraftSkill", "skill_siege"}, {"Recruitment", "recruitmentSkill", "skill_trainer"} },
{ {"Thaumaturgy", "thaumaturgySkill", "skill_wisdom"}, {"Combat", "battleartistrySkill", "skill_battle_artistry"} }
}
-- =========================================================================
-- FONCTIONS UTILITAIRES INTERNES
-- =========================================================================
-- Détecte la langue courante via le titre de la page (ex: "Knight/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", "" -- Pas de suffixe = Anglais par défaut
end
end
-- Charge et met en cache les données du fichier JSON de la langue ciblée
local json_cache = {}
local function getHeroClassJson(lang)
if not json_cache[lang] then
local page_name = string.format('Data:HeroClassData/%s.json', lang)
local content = mw.title.new(page_name):getContent() or '{}'
json_cache[lang] = mw.text.jsonDecode(content)
end
return json_cache[lang]
end
-- Trouve l'objet de la classe de héros demandée dans le JSON
local function findHeroClassData(className, lang)
if not className then return nil end
local json = getHeroClassJson(lang)
if not json or not json.heroClasses then return nil end
local clean_name = mw.text.trim(string.lower(className))
local target_key = class_mapping[clean_name] or clean_name
for _, class_obj in ipairs(json.heroClasses) do
local current_class_name = string.lower(class_obj.name or "")
if current_class_name == target_key or current_class_name == clean_name then
return class_obj
end
end
return nil
end
-- Interroge Cargo pour récupérer un dictionnaire global [id_competence] = "Nom Traduit"
local function fetchTranslationsFromCargo(lang)
local translations = {}
local tables = "Skill, Translation"
local fields = "Skill.id=id, Translation.name=name"
-- Requête principale dans la langue demandée (ex: 'fr')
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
-- Si on n'est pas en anglais, on complète les manques éventuels avec les noms anglais ('en')
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
-- Formate proprement les valeurs numériques en pourcentages (ex: 15 -> 15%, 0 -> 0%)
local function formatPercentage(val)
if not val or val == "" or val == 0 or val == "0" then return "0%" end
if type(val) == "string" and string.match(val, "%%$") then return val end
return tostring(val) .. "%"
end
-- =========================================================================
-- FONCTIONS PUBLIQUES (APPELÉES PAR LES MODÈLES WIKI)
-- =========================================================================
-- 1. Génère le grand tableau des compétences secondaires avec icônes et traductions
function p.getSkillsTable(frame)
local args = frame.args[1] and frame.args or frame:getParent().args
local lang, suffix = getLanguageInfo()
local class_data = findHeroClassData(args[1], lang)
if not class_data then
return ""
end
-- On charge toutes les traductions disponibles d'un seul coup
local dictionary = fetchTranslationsFromCargo(lang)
local html = {}
table.insert(html, '{| class="wikitable" style="width:100%; border-collapse:collapse;"')
-- Parcours de la matrice 11x2 définie dans la configuration
for _, double_row in ipairs(skills_structure) do
local s1, s2 = double_row[1], double_row[2]
-- Recherche des noms traduits dans notre dictionnaire Cargo
local label1 = dictionary[s1[3]] or s1[1]
local label2 = dictionary[s2[3]] or s2[1]
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, formatPercentage(class_data[s1[2]]),
s2[1], s2[1], suffix, label2, formatPercentage(class_data[s2[2]])
))
end
-- 12ème ligne : Gestion asymétrique de la Thaumaturgie (seule sur sa ligne)
local label_thaum = dictionary["skill_thaumaturgy"] or "Thaumaturgy"
table.insert(html, '|-')
table.insert(html, string.format('| [[File:Skill_Thaumaturgy.png|32px|link=Thaumaturgy%s]] %s || %s || || ',
suffix, label_thaum, formatPercentage(class_data["thaumaturgySkill"])))
table.insert(html, '|}')
return table.concat(html, "\n")
end
-- 2. Génère le bloc visuel des sous-classes et de leurs compétences associées
function p.getSubclassesBlock(frame)
local args = frame.args[1] and frame.args or frame:getParent().args
local lang, suffix = getLanguageInfo()
local class_data = findHeroClassData(args[1], lang)
if not class_data or not class_data.subclasses then
return ""
end
-- Titres internationaux automatisés
local titles = { fr = "Sous-classes", de = "Unterklassen", es = "Subclases", it = "Sottoclassi", pl = "Podklasy", en = "Subclasses" }
local title_text = titles[lang] or titles["en"]
-- Tri alphabétique des clés de sous-classes
local keys = {}
for k in pairs(class_data.subclasses) do table.insert(keys, k) end
table.sort(keys, function(a, b) return a > b end)
local html = {}
table.insert(html, string.format('<div style="font-weight:bold; text-align:center; margin-top:8px; margin-bottom:6px;">%s</div>', title_text))
for _, sub_key in ipairs(keys) do
local sub = class_data.subclasses[sub_key]
if sub then
table.insert(html, '<div style="margin:4px 0; border:1px solid rgba(255,255,255,0.04); border-radius:4px; padding:6px;">')
-- En-tête de la sous-classe (Icône + Nom)
local display_name = sub.name or sub_key
table.insert(html, string.format(' <div style="text-align:center; font-weight:bold;">[[File:%s.png|60px]] %s</div>', sub_key, display_name))
-- Description de l'effet
local effect_desc = sub.effect or sub.description or ""
table.insert(html, string.format(' <div style="text-align:center; margin-top:4px; font-size:0.95em;">%s</div>', effect_desc))
-- Mini-grille horizontale contenant les 5 icônes de compétences requises
table.insert(html, ' <table style="margin:auto; margin-top:6px;">')
table.insert(html, ' <tr>')
if sub.skills then
for i = 1, 5 do
local name_skill = sub.skills[i]
if name_skill and name_skill ~= "" then
table.insert(html, string.format(' <td style="padding:0 3px;">[[File:Skill_%s.png|60px|link=%s%s]]</td>', name_skill, name_skill, suffix))
else
table.insert(html, ' <td></td>')
end
end
end
table.insert(html, ' </tr>')
table.insert(html, ' </table>')
table.insert(html, '</div>')
end
end
return table.concat(html, "\n")
end
return p


