Template:HeroClassDef: Difference between revisions

From Heroes of Might and Magic: Olden Era Official Wiki
obelisk-bot: uploading initial Cargo templates
 
obelisk-bot: Pushing translation table rework
 
(2 intermediate revisions by the same user not shown)
Line 20: Line 20:
Each row holds the '''default values''' that every hero of that class inherits. Individual <code>HeroDef</code> rows carry only the ''deltas'' — fields the hero diverges from the class on (always <code>id</code>, <code>icon</code>, <code>specialization</code>,
Each row holds the '''default values''' that every hero of that class inherits. Individual <code>HeroDef</code> rows carry only the ''deltas'' — fields the hero diverges from the class on (always <code>id</code>, <code>icon</code>, <code>specialization</code>,
<code>startSquad</code>, <code>startSkills</code>, etc.; sometimes <code>stats</code>, <code>startLevel</code>,
<code>startSquad</code>, <code>startSkills</code>, etc.; sometimes <code>stats</code>, <code>startLevel</code>,
etc. for campaign and tutorial heroes who deviate from the canonical template). When a <code>HeroDef</code> row omits a class-level field, the wiki display layer joins to <code>HeroClassDef</code> to fill it in. See D-026.
etc. for campaign and tutorial heroes who deviate from the canonical template). When a <code>HeroDef</code> row omits a class-level field, the wiki display layer joins to <code>HeroClassDef</code> to fill it in.


Empirical: across all 108 faction heroes, the 11 class-level fields listed below are '''invariant''' within each class — there are zero deviations from the template. Campaign (46) and tutorial (21) heroes universally override <code>stats</code> plus a small subset of other fields, treating the <code>HeroClassDef</code> row as a starting point.
Empirical: across all 108 faction heroes, the 11 class-level fields listed below are '''invariant''' within each class — there are zero deviations from the template. Campaign (46) and tutorial (21) heroes universally override <code>stats</code> plus a small subset of other fields, treating the <code>HeroClassDef</code> row as a starting point.
Line 27: Line 27:
{{#cargo_declare:_table=HeroClass
{{#cargo_declare:_table=HeroClass
| id = String
| id = String
| name = String
| desc = Wikitext
| name_sid = String
| name_sid = String
| desc_sid = String
| desc_sid = String
Line 65: Line 63:
{{HeroClassDef
{{HeroClassDef
| id = String                  <!-- e.g. magic_demon, might_human -->
| id = String                  <!-- e.g. magic_demon, might_human -->
| name = String                <!-- e.g. Herald, Knight -->
 
| desc = Wikitext              <!-- shared per classType (might_desc / magic_desc) -->
<!-- Localization references — display text lives in the Translation table, type='hero_class' -->
| name_sid = String
| name_sid = String
| desc_sid = String
| desc_sid = String
Line 113: Line 111:
== Field notes ==
== Field notes ==
* '''<code>id</code>''' is <code><class_type>_<faction></code> (matches the L10n SID prefix). 12 fixed values: <code>might_human</code>, <code>magic_human</code>, … <code>magic_unfrozen</code>.
* '''<code>id</code>''' is <code><class_type>_<faction></code> (matches the L10n SID prefix). 12 fixed values: <code>might_human</code>, <code>magic_human</code>, … <code>magic_unfrozen</code>.
* '''<code>name</code>''' comes from <code><class_type>_<faction>_name</code> SIDs (e.g. <code>magic_demon_name</code> → "Herald"). Full 16-language coverage on all 12 SIDs.
* '''Display name / description''' for every language (English included) live in the [[Template:TranslationDef|Translation]] table, <code>type='hero_class'</code>. <code>name_sid</code> is <code><class_type>_<faction>_name</code> (e.g. <code>magic_demon_name</code> → "Herald"); full 16-language coverage on all 12.
* '''<code>desc</code>''' comes from one of two shared SIDs: <code>might_desc</code> or <code>magic_desc</code>. The same description appears on all 6 factions of a given type — Unfrozen reused it, faction doesn't matter for this text. Both SIDs have full 16-language coverage. '''All 6 might classes share the same <code>desc_sid='might_desc'</code>; all 6 magic classes share <code>desc_sid='magic_desc'</code>''' — translation duplication is intentional (one SID, six joined rows).
* '''<code>desc_sid</code>''' is one of two shared SIDs: <code>might_desc</code> or <code>magic_desc</code>. The same description applies to all 6 factions of a given type — faction doesn't matter for this text. '''All 6 might classes share <code>desc_sid='might_desc'</code>; all 6 magic classes share <code>desc_sid='magic_desc'</code>''' — so the resolved description text repeats across the six corresponding Translation rows. Intentional.
* '''<code>mount</code>''' is singular here even though the source's <code>mounts</code> is a list, because every faction hero in the 2026-05-03 corpus has exactly one mount per class. If a future patch ships heroes with multiple mounts, promote this to a list-typed column or split it to a side table.
* '''<code>mount</code>''' is singular here even though the source's <code>mounts</code> is a list, because every faction hero in the 2026-05-03 corpus has exactly one mount per class. If a future patch ships heroes with multiple mounts, promote this to a list-typed column or split it to a side table.
* '''<code>stats</code>''' is flattened from the source's nested <code>stats</code> dict. All twelve fields present on every hero.
* '''<code>stats</code>''' is flattened from the source's nested <code>stats</code> dict. All twelve fields present on every hero.
Line 131: Line 129:
== Related tables ==
== Related tables ==
* [[Template:HeroDef|Hero]] — joined on <code>Hero.class_id = HeroClass.id</code> (1:N). Hero rows hold only the per-hero data and any fields that override the class default.
* [[Template:HeroDef|Hero]] — joined on <code>Hero.class_id = HeroClass.id</code> (1:N). Hero rows hold only the per-hero data and any fields that override the class default.
* [[HeroClassTranslation]] — joined on <code>id = class_id</code> (1:1) for the 15 non-English locales of <code>name</code> + <code>desc</code>.
* [[Template:TranslationDef|Translation]] — display text in every language (English included), joined on <code>id = Translation.target_id WHERE Translation.type='hero_class' AND Translation.language='{{{lang|en}}}'</code> (1:N, one row per language).
* [[Template:HeroSubClassDef|HeroSubClass]] — the named prestige classes (Swashbuckler, Paragon, etc.) that branch off each HeroClass at certain skill thresholds. Joined via <code>HeroSubClass.faction + HeroSubClass.class_type</code>.
* [[Template:HeroSubClassDef|HeroSubClass]] — the named prestige classes (Swashbuckler, Paragon, etc.) that branch off each HeroClass at certain skill thresholds. Joined via <code>HeroSubClass.faction + HeroSubClass.class_type</code>.


Line 139: Line 137:
</noinclude><includeonly>{{#cargo_store:_table=HeroClass
</noinclude><includeonly>{{#cargo_store:_table=HeroClass
| id = {{{id|}}}
| id = {{{id|}}}
| name = {{{name|}}}
| desc = {{{desc|}}}
| name_sid = {{{name_sid|}}}
| name_sid = {{{name_sid|}}}
| desc_sid = {{{desc_sid|}}}
| desc_sid = {{{desc_sid|}}}
Line 172: Line 168:
| roll_lvl24_power = {{{roll_lvl24_power|}}}
| roll_lvl24_power = {{{roll_lvl24_power|}}}
| roll_lvl24_knowledge = {{{roll_lvl24_knowledge|}}}
| roll_lvl24_knowledge = {{{roll_lvl24_knowledge|}}}
}}</includeonly>
}}
Created <code>HeroClass</code> entry: <code>{{{id|}}}</code></includeonly>

Latest revision as of 20:48, 14 May 2026

One row per hero class — exactly 12 rows, the cross-product of the 6 factions and the 2 class types (might, magic):

Faction Might class Magic class
human Knight Cleric
undead Death Knight Necromancer
dungeon Overlord Warlock
nature Warden Druid
demon Enforcer Herald
unfrozen Oathkeeper Riftspeaker

Each row holds the default values that every hero of that class inherits. Individual HeroDef rows carry only the deltas — fields the hero diverges from the class on (always id, icon, specialization, startSquad, startSkills, etc.; sometimes stats, startLevel, etc. for campaign and tutorial heroes who deviate from the canonical template). When a HeroDef row omits a class-level field, the wiki display layer joins to HeroClassDef to fill it in.

Empirical: across all 108 faction heroes, the 11 class-level fields listed below are invariant within each class — there are zero deviations from the template. Campaign (46) and tutorial (21) heroes universally override stats plus a small subset of other fields, treating the HeroClassDef row as a starting point.

Schema

This template defines the table "HeroClass". View table.

{{HeroClassDef
| id = String                  <!-- e.g. magic_demon, might_human -->

<!-- Localization references — display text lives in the Translation table, type='hero_class' -->
| name_sid = String
| desc_sid = String

| faction = String (allowed values=human,undead,dungeon,nature,demon,unfrozen)
| class_type = String (allowed values=might,magic)

<!-- Class defaults: every faction hero inherits these unchanged -->
| mesh = String
| mount = String                 <!-- single mount; source is a list, but always length 1 -->
| native_biome = String
| skills_roll_variant = String
| cost_gold = Integer
| start_level = Integer
| attacks_times_before = String  <!-- comma-joined floats, e.g. "0.5" -->

<!-- Stat block (full UnitStats-style flatten) -->
| view_radius = Integer
| stats_num = Integer
| magic_casts_per_round = Integer
| enable_tactics = Boolean
| tactics_placement_size = Integer
| enable_hero_native_biome = Boolean
| offence = Integer
| defence = Integer
| spell_power = Integer
| intelligence = Integer
| luck = Integer
| morale = Integer

<!-- statsRolls flattened: always 2 bands (level 1 / level 24),
     each with chance weights for which stat the level-up bonus
     lands on. Source `v` indexes 0/1/2/3 are interpreted as
     attack / defense / power / knowledge in that order. -->
| roll_lvl1_attack = Integer
| roll_lvl1_defense = Integer
| roll_lvl1_power = Integer
| roll_lvl1_knowledge = Integer
| roll_lvl24_attack = Integer
| roll_lvl24_defense = Integer
| roll_lvl24_power = Integer
| roll_lvl24_knowledge = Integer
}}

Field notes

  • id is <class_type>_<faction> (matches the L10n SID prefix). 12 fixed values: might_human, magic_human, … magic_unfrozen.
  • Display name / description for every language (English included) live in the Translation table, type='hero_class'. name_sid is <class_type>_<faction>_name (e.g. magic_demon_name → "Herald"); full 16-language coverage on all 12.
  • desc_sid is one of two shared SIDs: might_desc or magic_desc. The same description applies to all 6 factions of a given type — faction doesn't matter for this text. All 6 might classes share desc_sid='might_desc'; all 6 magic classes share desc_sid='magic_desc' — so the resolved description text repeats across the six corresponding Translation rows. Intentional.
  • mount is singular here even though the source's mounts is a list, because every faction hero in the 2026-05-03 corpus has exactly one mount per class. If a future patch ships heroes with multiple mounts, promote this to a list-typed column or split it to a side table.
  • stats is flattened from the source's nested stats dict. All twelve fields present on every hero.
  • statsRolls is uniformly 2 bands × 4 chances across all 108 faction heroes. The second band always starts at level 24, and the chance-value tuple is always v=(0, 1, 2, 3) in that exact order. The v indexes don't appear in source data with any player-facing meaning, but their order matches the four primary stats — so the schema flattens them to named columns roll_lvl<N>_<stat> (attack, defense, power, knowledge) holding the chance weight for that level-band / stat-bonus pair. Weights sum to 100 within each band. If a future patch reveals this stat-order assumption is wrong, the column names need to change accordingly — the JSON itself doesn't label them.

Override semantics

The HeroDef table mirrors most of the columns above. When the bot emits a hero row:

  • Faction heroes (108): the bot omits all 11 class-level columns from the HeroDef row — they're all defaulted from HeroClassDef. Display layer reads the field from HeroDef; if absent, joins HeroClassDef on Hero.class_id = HeroClass.id and reads from there.
  • Campaign / tutorial heroes: the bot includes only the columns where the hero deviates from the class template. Empirically these are almost always stats, attacks_times_before, and start_level; sometimes mesh, mounts, skills_roll_variant. All other class-level columns stay defaulted.

The override rule is a single Cargo query pattern: COALESCE(Hero.<col>, HeroClass.<col>) — Hero wins when present, HeroClass fills in otherwise.

Related tables

  • Hero — joined on Hero.class_id = HeroClass.id (1:N). Hero rows hold only the per-hero data and any fields that override the class default.
  • Translation — display text in every language (English included), joined on id = Translation.target_id WHERE Translation.type='hero_class' AND Translation.language='en' (1:N, one row per language).
  • HeroSubClass — the named prestige classes (Swashbuckler, Paragon, etc.) that branch off each HeroClass at certain skill thresholds. Joined via HeroSubClass.faction + HeroSubClass.class_type.

Notes