Template:HeroDef: 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
 
(One intermediate revision by the same user not shown)
Line 5: Line 5:
The bot emits these per-hero blocks at <code>Data:Hero/<id></code>:
The bot emits these per-hero blocks at <code>Data:Hero/<id></code>:


# <code><nowiki>{{HeroDef | id=… | name=… | motto=… | desc=… | start_skills=… | start_magics=… | …}}</nowiki></code> — main row. <code>start_skills</code> and <code>start_magics</code> are comma-joined SID lists inline on this row (no side tables for them).
# <code><nowiki>{{HeroDef | id=… | start_skills=… | start_magics=… | …}}</nowiki></code> — main row. <code>start_skills</code> and <code>start_magics</code> are comma-joined SID lists inline on this row (no side tables for them).
# <code><nowiki>{{TranslationDef | type=hero | target_id=<id> | …}}</nowiki></code> — name/desc translation (per D-026).
# <code><nowiki>{{TranslationDef | type=hero | target_id=<id> | language=… | name=… | description=… }}</nowiki></code> × N — name/description, one row per language.
# <code><nowiki>{{TranslationDef | type=hero_motto | target_id=<id> | …}}</nowiki></code> — motto translation (separate row, kept off the schema-specialization path; per D-027).
# <code><nowiki>{{TranslationDef | type=hero_motto | target_id=<id> | language=… | description=… }}</nowiki></code> × N — motto, a separate translation key, one row per language (motto text goes in the <code>description</code> column since it may carry markup).
# <code><nowiki>{{HeroStartSquadDef | …}}</nowiki></code> × N — one per starting squad slot (primary + alt collapsed via <code>variant</code> discriminator).
# <code><nowiki>{{HeroStartSquadDef | …}}</nowiki></code> × N — one per starting squad slot (primary + alt collapsed via <code>variant</code> discriminator).


Line 13: Line 13:
{{#cargo_declare:_table=Hero
{{#cargo_declare:_table=Hero
| id = String
| id = String
| name = String
| motto = Wikitext
| description = Wikitext
| name_sid = String
| name_sid = String
| motto_sid = String
| motto_sid = String
Line 60: Line 57:
{{HeroDef
{{HeroDef
| id = String
| id = String
| name = String
 
| motto = Wikitext
<!-- Localization references — display text lives in the Translation table:
| description = Wikitext
    type='hero' carries name+description, type='hero_motto' carries the motto -->
| name_sid = String
| name_sid = String
| motto_sid = String
| motto_sid = String
Line 85: Line 82:


<!-- Note: spell level / isLearned are dropped (constant in the
<!-- Note: spell level / isLearned are dropped (constant in the
     2026-05-03 corpus per D-027 revised). Skill level is preserved
     2026-05-03 corpus). Skill level is preserved because 8/208
    because 8/208 entries diverge from the default of 1. -->
    entries diverge from the default of 1. -->




Line 143: Line 140:
== Field notes ==
== Field notes ==
* '''<code>id</code>''' is the hero's source JSON id (e.g. <code>human_hero_1</code>, <code>campaign_M10_hero_demon_1</code>).
* '''<code>id</code>''' is the hero's source JSON id (e.g. <code>human_hero_1</code>, <code>campaign_M10_hero_demon_1</code>).
* '''<code>name_sid</code>/<code>motto_sid</code>/<code>desc_sid</code>''' carry the source SID for traceability and as the join key to the Translation rows. Faction heroes use implicit conventions (<code><id></code> / <code><id>_motto</code> / <code><id>_description</code>); campaign heroes set them explicitly in source JSON.
* '''<code>name_sid</code>/<code>motto_sid</code>/<code>desc_sid</code>''' carry the source SID for traceability only. Faction heroes use implicit conventions (<code><id></code> / <code><id>_motto</code> / <code><id>_description</code>); campaign heroes set them explicitly in source JSON.
* '''<code>name</code>/<code>motto</code>/<code>desc</code>''' carry the resolved English text inline. Translations live on the two <code>TranslationDef</code> rows (one for name+desc, one for motto).
* '''Display name / motto / description''' for every language (English included) live in the [[Template:TranslationDef|Translation]] table — <code>type='hero'</code> rows carry name + description, <code>type='hero_motto'</code> rows carry the motto. Both keyed on <code>target_id = Hero.id</code>.
* '''<code>class_id</code>''' is <code><class_type>_<faction></code> — matches <code>HeroClass.id</code>.
* '''<code>class_id</code>''' is <code><class_type>_<faction></code> — matches <code>HeroClass.id</code>.
* '''<code>faction</code>/<code>class_type</code>''' are denormalized from the class so that "all human heroes" / "all might heroes" filters don't need a join.
* '''<code>faction</code>/<code>class_type</code>''' are denormalized from the class so that "all human heroes" / "all might heroes" filters don't need a join.
Line 154: Line 151:
* <code>SkillDef</code> (future) — joined on <code>start_skills</code> LIKE <code>%<sid>%</code> or via Cargo's <code>HOLDS</code> operator on the List column.
* <code>SkillDef</code> (future) — joined on <code>start_skills</code> LIKE <code>%<sid>%</code> or via Cargo's <code>HOLDS</code> operator on the List column.
* <code>SpellDef</code> (future) — same join pattern via <code>start_magics</code>.
* <code>SpellDef</code> (future) — same join pattern via <code>start_magics</code>.
'' [[Template:HeroSpecializationDef|HeroSpecialization]] — joined on <code>specialization_id = id</code> (N:1). The hero's unique passive. ''(Schema lands in a follow-up.)*
* [[Template:HeroSpecializationDef|HeroSpecialization]] — joined on <code>specialization_id = id</code> (N:1). The hero's unique passive.
* [[Template:TranslationDef|<code>TranslationDef</code>]] — two rows per hero (<code>type=hero</code> and <code>type=hero_motto</code>).
* [[Template:TranslationDef|Translation]] — display text in every language (English included), joined on <code>id = Translation.target_id</code> with <code>Translation.type IN ('hero', 'hero_motto')</code> and the desired <code>language</code> filter (1:N, two keys × one row per language).


== Notes ==
== Notes ==
Line 162: Line 159:
</noinclude><includeonly>{{#cargo_store:_table=Hero
</noinclude><includeonly>{{#cargo_store:_table=Hero
| id = {{{id|}}}
| id = {{{id|}}}
| name = {{{name|}}}
| motto = {{{motto|}}}
| description = {{{description|}}}
| name_sid = {{{name_sid|}}}
| name_sid = {{{name_sid|}}}
| motto_sid = {{{motto_sid|}}}
| motto_sid = {{{motto_sid|}}}
Line 204: Line 198:
| 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>Hero</code> entry: <code>{{{id|}}}</code></includeonly>

Latest revision as of 20:48, 14 May 2026

One row per hero (~177 in the 2026-05-03 corpus: 108 faction + 46 campaign + 21 tutorial + 2 custom-map). Carries the per-hero identity (name/motto/desc), the link to the parent HeroClass, and any class-default fields the hero diverges on. Faction heroes match their class template exactly, so their override fields are sparse-emitted (absent); campaign and tutorial heroes routinely override stats, start_level, and attacks_times_before.

The bot emits these per-hero blocks at Data:Hero/<id>:

  1. {{HeroDef | id=… | start_skills=… | start_magics=… | …}} — main row. start_skills and start_magics are comma-joined SID lists inline on this row (no side tables for them).
  2. {{TranslationDef | type=hero | target_id=<id> | language=… | name=… | description=… }} × N — name/description, one row per language.
  3. {{TranslationDef | type=hero_motto | target_id=<id> | language=… | description=… }} × N — motto, a separate translation key, one row per language (motto text goes in the description column since it may carry markup).
  4. {{HeroStartSquadDef | …}} × N — one per starting squad slot (primary + alt collapsed via variant discriminator).

Schema

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

{{HeroDef
| id = String

<!-- Localization references — display text lives in the Translation table:
     type='hero' carries name+description, type='hero_motto' carries the motto -->
| name_sid = String
| motto_sid = String
| desc_sid = String

| class_id = String              <!-- joins HeroClass.id (e.g. magic_demon) -->
| faction = String               <!-- denormalized from class -->
| class_type = String (allowed values=might,magic)

| icon = String
| specialization_id = String     <!-- joins HeroSpecialization.id -->

| source_path = String

<!-- Inline list-of-SID columns (always present; empty when none).
     start_skills and start_skill_levels are positionally aligned —
     the Nth element of start_skill_levels is the level for the Nth
     skill SID in start_skills. -->
| start_skills = List (,) of String        <!-- joins future Skill.id -->
| start_skill_levels = List (,) of Integer <!-- positionally aligned with start_skills -->
| start_magics = List (,) of String        <!-- joins future Spell.id -->

<!-- Note: spell level / isLearned are dropped (constant in the
     2026-05-03 corpus). Skill level is preserved because 8/208
     entries diverge from the default of 1. -->


<!-- Class-default override fields. Sparse: absent means inherit from
     HeroClass. Faction heroes always inherit (no overrides).
     Campaign/tutorial heroes routinely override stats + a few others. -->
| cost_gold = Integer
| start_level = Integer
| attacks_times_before = String      <!-- comma-joined floats -->
| mesh = String
| mount = String
| native_biome = String
| skills_roll_variant = String

<!-- Stat block overrides -->
| 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 overrides (named after the four primary stats; see
     HeroClass.md for the v=0..3 → attack/defense/power/knowledge
     mapping). -->
| 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
}}

Override semantics

Every Cargo query that wants the effective value of a class-shared field on a hero should use:

<syntaxhighlight lang="sql"> COALESCE(Hero.<col>, HeroClass.<col>) AS effective_<col> </syntaxhighlight>

Joining on Hero.class_id = HeroClass.id. Hero wins if present; HeroClass fills in otherwise.

For faction heroes (108): all 11 class-shared columns are absent on the Hero row — every query falls through to HeroClass.

For campaign/tutorial heroes: ~3-5 columns are typically present (empirically: stats_*, start_level, attacks_times_before). Other class-shared columns still inherit from HeroClass.

Field notes

  • id is the hero's source JSON id (e.g. human_hero_1, campaign_M10_hero_demon_1).
  • name_sid/motto_sid/desc_sid carry the source SID for traceability only. Faction heroes use implicit conventions (<id> / <id>_motto / <id>_description); campaign heroes set them explicitly in source JSON.
  • Display name / motto / description for every language (English included) live in the Translation table — type='hero' rows carry name + description, type='hero_motto' rows carry the motto. Both keyed on target_id = Hero.id.
  • class_id is <class_type>_<faction> — matches HeroClass.id.
  • faction/class_type are denormalized from the class so that "all human heroes" / "all might heroes" filters don't need a join.
  • specialization_id points at a row in HeroSpecializationDef (one specialization per hero, 1:1 ratio). Schema for that table comes in a follow-up.

Related tables

  • HeroClass — joined on class_id = id (N:1). Carries the class defaults the Hero row may override.
  • HeroStartSquad — joined on hero_id = id (1:N). Each row is one slot in primary or alt starting army.
  • SkillDef (future) — joined on start_skills LIKE %<sid>% or via Cargo's HOLDS operator on the List column.
  • SpellDef (future) — same join pattern via start_magics.
  • HeroSpecialization — joined on specialization_id = id (N:1). The hero's unique passive.
  • Translation — display text in every language (English included), joined on id = Translation.target_id with Translation.type IN ('hero', 'hero_motto') and the desired language filter (1:N, two keys × one row per language).

Notes