Template:HeroClassDef: Difference between revisions
obelisk-bot: uploading initial Cargo templates |
obelisk-bot: Pushing translation table rework |
||
| 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 | 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_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 --> | ||
<!-- 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> | * '''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> | * '''<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. | ||
* [[ | * [[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_sid = {{{name_sid|}}} | | name_sid = {{{name_sid|}}} | ||
| desc_sid = {{{desc_sid|}}} | | desc_sid = {{{desc_sid|}}} | ||
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
idis<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_sidis<class_type>_<faction>_name(e.g.magic_demon_name→ "Herald"); full 16-language coverage on all 12. desc_sidis one of two shared SIDs:might_descormagic_desc. The same description applies to all 6 factions of a given type — faction doesn't matter for this text. All 6 might classes sharedesc_sid='might_desc'; all 6 magic classes sharedesc_sid='magic_desc'— so the resolved description text repeats across the six corresponding Translation rows. Intentional.mountis singular here even though the source'smountsis 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.statsis flattened from the source's nestedstatsdict. All twelve fields present on every hero.statsRollsis uniformly 2 bands × 4 chances across all 108 faction heroes. The second band always starts at level 24, and the chance-value tuple is alwaysv=(0, 1, 2, 3)in that exact order. Thevindexes 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 columnsroll_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
HeroDefrow — they're all defaulted fromHeroClassDef. Display layer reads the field fromHeroDef; if absent, joinsHeroClassDefonHero.class_id = HeroClass.idand 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, andstart_level; sometimesmesh,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.


