Files
lords-of-ash/docs/milestones/M1-foundation.md
T
jpmschweitzerandClaude Opus 4.6 a2edd9025e docs(milestones): add detailed milestone implementation documents
Break the MASTERPLAN into 8 milestone documents (M1-M8) with a
standard template. Each milestone includes prerequisites, current
state assessment, numbered implementation tasks with file references,
testing checkpoints, acceptance criteria, and risk analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 16:29:56 +01:00

23 KiB

M1: Foundation

The mod loads and plays cleanly on the vanilla map with zero errors, no vanilla bleed-through, and a working game rule toggle.

Prerequisites

  • None. This is the first milestone.
  • CK3 version 1.18.* installed and launchable.
  • The lords_of_ash/ mod directory is loadable via lords-of-ash.mod.

Current State

The mod has a strong skeleton but several issues prevent a clean boot and playthrough. All critical issues from the original review have been resolved (trait name mismatches, missing event namespaces, @# comment syntax, missing modifiers, province history, bookmark). However, warning-level issues remain that must be fixed before M2 work can begin safely.

What exists and works:

  • common/modifiers/00_mistborn_modifiers.txt -- all 7 modifier definitions present (compounder, lord_ruler_attention tiers 1-3, ruin_influence, steel_inquisitor_power, great_house_status, atium_supply, ash_blight, koloss_rampage, opinion modifiers). Lines 1-154.
  • history/provinces/00_mistborn_provinces.txt -- 105 provinces with culture, religion, and holding assignments. Lines 1-770.
  • common/bookmarks/00_mistborn_bookmarks.txt -- "End of an Empire" bookmark.
  • common/landed_titles/00_landed_titles.txt -- full title hierarchy mapping 105 baronies to vanilla Swiss/Swabian/Tyrolean/Burgundian/Lombard provinces. Lines 1-731.
  • common/scripted_triggers/mistborn_triggers.txt -- 15 triggers. Lines 1-134.
  • common/scripted_effects/mistborn_effects.txt -- 10 effects. Lines 1-190.
  • common/culture/ -- 3 cultures, 3 pillars, 4 traditions.
  • common/religion/ -- 4 religions with 4 faiths and 5 holy sites.
  • history/characters/00_mistborn_characters.txt -- 30 characters.
  • history/titles/00_mistborn_titles.txt -- title assignments for all 50+ titles.

What needs fixing (this milestone):

Issue File Severity
Empty culture pillar blocks (WARNING-01) common/culture/pillars/00_scadrial_pillars.txt Warning
Dual Lord Ruler attention systems (WARNING-05) common/scripted_effects/, common/on_action/, events Warning
is_feruchemist_trigger matches dormant potential (WARNING-06) common/scripted_triggers/mistborn_triggers.txt:71-76 Warning
has_allomantic_potential_trigger is over-inclusive (WARNING-07) common/scripted_triggers/mistborn_triggers.txt:28-36 Warning
No vanilla decision suppression common/decisions/ Missing
No game rule definitions common/game_rules/ Missing
Province history uses sequential IDs 1-105 instead of vanilla IDs history/provinces/00_mistborn_provinces.txt Critical
trait_knows_hemalurgy referenced but was previously missing common/traits/05_hemalurgy_traits.txt:94-108 Resolved

Critical discovery: The province history file uses sequential IDs 1-105, but 00_landed_titles.txt maps baronies to vanilla province IDs (2046, 2047, 2048, etc.). Province history must use the actual vanilla province IDs from the landed titles file, not sequential numbers. This mismatch means no province currently has valid culture/religion/holding data at game start.

Implementation Tasks

1.1 -- Fix Province History to Use Vanilla Province IDs

Context: The province history file (history/provinces/00_mistborn_provinces.txt) uses sequential IDs 1-105, but the landed titles file maps baronies to specific vanilla province IDs (e.g., b_kredik_shaw -> province 2046, b_luthadel_city -> province 2047). CK3 loads province history by matching numeric IDs to the actual province definitions in map_data/. Since we use the vanilla map, our province history must reference the vanilla province IDs from the landed titles file. This is the single most critical fix -- without it, every province loads with default (wrong) culture, religion, and holdings.

Steps:

  1. Extract all province = XXXX values from common/landed_titles/00_landed_titles.txt. There are 105 baronies with vanilla province IDs ranging from 2030 to 3107.
  2. Rewrite history/provinces/00_mistborn_provinces.txt to use these vanilla IDs instead of 1-105. Each province entry should keep the same culture, religion, and holding type, but the numeric key must match the vanilla province ID.
  3. Group provinces by dominance (Central, Northern, Eastern, Western, Southern, Remote) with clear comments, matching the structure in the landed titles file.
  4. For each barony, determine the correct data from the existing file by matching position -- e.g., province 1 in the old file corresponds to b_kredik_shaw (province 2046), province 2 to b_luthadel_city (province 2047), etc.
  5. Verify every vanilla province ID from the landed titles file has a corresponding entry in the province history file. Missing entries will cause errors.

Province ID mapping (first 21 as example -- Central Dominance):

Old ID Vanilla ID Barony Notes
1 2046 b_kredik_shaw castle_holding, noble_scadrian
2 2047 b_luthadel_city city_holding, noble_scadrian
3 2048 b_luthadel_slums city_holding, skaa
4 2049 b_fellise castle_holding, noble_scadrian
5 2037 b_fellise_estates castle_holding, noble_scadrian
6 2051 b_holstep castle_holding, noble_scadrian
7 2461 b_holstep_mines city_holding, skaa
... ... ... ...

The full mapping must be extracted from common/landed_titles/00_landed_titles.txt lines 33-730 by reading each province = XXXX value in order.

Files:

  • history/provinces/00_mistborn_provinces.txt -- rewrite

References:

  • Province ID source: common/landed_titles/00_landed_titles.txt:33-730
  • CK3 province history format: {province_id} = { culture = X religion = Y holding = Z }

1.2 -- Fix Culture Pillar Definitions

Context: The culture pillars in common/culture/pillars/00_scadrial_pillars.txt were originally empty {} blocks (WARNING-01). They have since been partially filled but need validation against CK3 1.18 requirements. Pillars must have type, is_shown, and typically audio_parameter (for heritage) or ai_will_do (for language).

Steps:

  1. Validate heritage_scadrial (lines 4-12): Confirm it has type = heritage, is_shown block, and audio_parameter. Currently present -- verify the heritage_is_shown_trigger is a valid vanilla scripted trigger (it should reference the vanilla culture system).
  2. Validate language_scadrial (lines 14-28): Confirm type = language, is_shown, and ai_will_do. Currently present.
  3. Validate language_terris (lines 30-44): Same checks as above.
  4. Add martial_custom_male_only reference check -- this is used by all 3 cultures in 00_scadrial_cultures.txt. Confirm it is a vanilla pillar that will be loaded (it is a standard CK3 martial custom, so this should work if vanilla pillars remain loaded).
  5. If any pillar is missing a required field, add it following the vanilla pattern in common/culture/pillars/00_heritage.txt from the CK3 game files.

Files:

  • common/culture/pillars/00_scadrial_pillars.txt -- modify (validate/fix)

References:

  • Current pillar code: common/culture/pillars/00_scadrial_pillars.txt:1-44
  • Cultures that reference these pillars: common/culture/cultures/00_scadrial_cultures.txt:8,34,58

1.3 -- Fix Scripted Trigger Issues

Context: Two triggers have inclusiveness problems identified in WARNING-06 and WARNING-07 of the review. These will cause incorrect behavior in M2 when compounding checks and snapping logic rely on distinguishing active vs dormant Allomancers/Feruchemists.

Steps:

  1. Rename is_feruchemist_trigger (line 71) to has_any_feruchemy_trigger to reflect that it matches both active and dormant. Create a new is_feruchemist_trigger that only matches trait_feruchemist (active). This preserves backward compatibility for any code already using the old name by making the old name now correctly mean "active Feruchemist."

    Actually, the cleaner fix: is_feruchemist_trigger currently matches both trait_feruchemist and trait_feruchemy_potential. Change it to match only trait_feruchemist. Add a separate has_feruchemy_potential_trigger that matches only trait_feruchemy_potential. Add has_any_feruchemy_trigger that matches either.

    is_feruchemist_trigger = {
        has_trait = trait_feruchemist
    }
    
    has_feruchemy_potential_trigger = {
        has_trait = trait_feruchemy_potential
    }
    
    has_any_feruchemy_trigger = {
        OR = {
            has_trait = trait_feruchemist
            has_trait = trait_feruchemy_potential
        }
    }
    
  2. Fix has_allomantic_potential_trigger (line 28). Currently matches active Allomancers via is_allomancer_trigger and is_mistborn_trigger. Rename to has_any_allomantic_ability_trigger. Create a clean has_allomantic_potential_trigger that only matches dormant potential:

    has_allomantic_potential_trigger = {
        OR = {
            has_trait = trait_allomantic_potential
            has_trait = trait_mistborn_potential
        }
        NOT = { is_allomancer_trigger = yes }
    }
    

    And add a broader trigger:

    has_any_allomantic_ability_trigger = {
        OR = {
            has_trait = trait_allomantic_potential
            has_trait = trait_mistborn_potential
            is_allomancer_trigger = yes
        }
    }
    
  3. Verify is_full_feruchemist_trigger (line 78) is correct -- it matches only trait_feruchemist, which is fine.

  4. Search all files that use is_feruchemist_trigger or has_allomantic_potential_trigger and update callsites to use the correct new trigger name. Key locations:

    • common/on_action/mistborn_on_actions.txt (compounding check, line 69)
    • events/mistborn_misc_events.txt (mist spirit encounter, line 381)
    • Any future M2 code

Files:

  • common/scripted_triggers/mistborn_triggers.txt -- modify

References:

  • Current trigger code: common/scripted_triggers/mistborn_triggers.txt:28-36,71-80
  • Compounding check using triggers: common/on_action/mistborn_on_actions.txt:66-91

1.4 -- Unify the Dual Lord Ruler Attention System

Context: WARNING-05 identifies two parallel Lord Ruler attention systems: (1) a variable-based system (lord_ruler_attention variable 0-100) managed by mistborn_effects.txt and mistborn_on_actions.txt, and (2) a modifier-based system (lord_ruler_attention_modifier) applied by some events/decisions. The variable system is more robust and already has threshold-based event firing in on_actions. The modifier system is used in improve_skaa_conditions_decision (line 524-527 of decisions) and the smuggle_atium_decision (line 370 check). These should be unified on the variable system.

Steps:

  1. Remove the generic lord_ruler_attention_modifier from common/modifiers/00_mistborn_modifiers.txt (lines 23-25). Keep the tiered modifiers (lord_ruler_attention_1/2/3) as they are applied by the attention threshold events.

  2. In common/decisions/00_mistborn_decisions.txt:

    • improve_skaa_conditions_decision (line 524): Replace add_character_modifier = { modifier = lord_ruler_attention_modifier ... } with increase_lord_ruler_attention_effect = yes.
    • smuggle_atium_decision (line 370): Replace the check has_character_modifier = lord_ruler_attention_modifier with has_variable = lord_ruler_attention and var:lord_ruler_attention >= 25 (checking if already under scrutiny).
  3. Update the on_action yearly pulse attention threshold checks (common/on_action/mistborn_on_actions.txt lines 27-63) to apply the tiered modifiers automatically:

    • When attention >= 25: apply lord_ruler_attention_1 (remove 2 and 3)
    • When attention >= 60: apply lord_ruler_attention_2 (remove 1 and 3)
    • When attention >= 90: apply lord_ruler_attention_3 (remove 1 and 2)
    • When attention < 25: remove all three modifiers

    This connects the variable thresholds to the modifier effects, giving players visible feedback.

  4. Verify no other files reference lord_ruler_attention_modifier. Search all .txt files in the mod for this string and update any remaining references.

Files:

  • common/modifiers/00_mistborn_modifiers.txt -- modify (remove generic modifier)
  • common/decisions/00_mistborn_decisions.txt -- modify
  • common/on_action/mistborn_on_actions.txt -- modify

References:

  • Variable system: common/scripted_effects/mistborn_effects.txt:44-99
  • Threshold events: common/on_action/mistborn_on_actions.txt:27-63
  • Modifier definitions: common/modifiers/00_mistborn_modifiers.txt:59-90

1.5 -- Suppress Vanilla Decisions

Context: As a total conversion mod, vanilla CK3 decisions (feasts, hunts, pilgrimages, grand weddings, etc.) should not appear in the decision list. CK3 mods suppress vanilla content by creating replacement files with the same path that contain empty or no-op definitions. All files in common/decisions/ from the vanilla game that contain non-Mistborn decisions must be overridden.

Steps:

  1. Identify the vanilla CK3 decision files. In version 1.18, the key vanilla decision files in common/decisions/ are:

    • 00_decisions.txt -- major decisions (found a kingdom, etc.)
    • 00_activity_decisions.txt -- feast, hunt, pilgrimage activities
    • 00_dlc_decisions.txt -- DLC-specific decisions
    • 00_major_decisions.txt -- form empire, etc.
    • Various numbered decision files
  2. Create empty replacement files in lords_of_ash/common/decisions/ for each vanilla decision file that contains immersion-breaking content. The file must have the exact same filename as the vanilla file. The file content should be a comment explaining it is intentionally empty:

    # Vanilla decisions suppressed for Lords of Ash total conversion
    
  3. Key vanilla files to suppress (at minimum):

    • 00_major_decisions.txt -- found kingdom, found holy order, etc.
    • 00_decisions.txt -- generic decisions
    • Activity decisions that reference feasts, hunts, pilgrimages
  4. Do NOT suppress 00_coa_decisions.txt or decisions that are purely mechanical (succession, realm management) unless they reference anachronistic content.

  5. Test by loading the mod and checking the decision list -- only Mistborn decisions should appear.

Files:

  • common/decisions/00_major_decisions.txt -- create (empty suppression)
  • common/decisions/00_decisions.txt -- create (empty suppression)
  • common/decisions/00_activity_decisions.txt -- create (empty suppression)
  • Additional vanilla files as identified during testing

References:

  • Existing mod decisions: common/decisions/00_mistborn_decisions.txt:1-627
  • Vanilla CK3 decision file names (check game installation directory)

1.6 -- Create Game Rule Infrastructure

Context: The MASTERPLAN specifies a Narrative Mode vs Sandbox Mode toggle that gates story events and certain decisions. M1 only needs the infrastructure -- the actual gating happens in M2 (Sandbox decision) and M5-M6 (narrative events). The game rule must appear on the game setup screen.

Steps:

  1. Create directory lords_of_ash/common/game_rules/ if it does not exist.
  2. Create lords_of_ash/common/game_rules/00_mistborn_game_rules.txt with:
    mistborn_game_mode = {
        default = narrative_mode
    
        narrative_mode = {
            # Curated story arcs following the book's plot
            # Narrative events fire, some decisions restricted
        }
        sandbox_mode = {
            # Play any character, full freedom
            # "Discover Your Metallic Arts" decision available
            # Narrative events suppressed
        }
    }
    
  3. Add localization keys to a new file localization/english/mistborn_game_rules_l_english.yml:
    l_english:
     mistborn_game_mode:0 "Game Mode"
     mistborn_game_mode_desc:0 "Choose between a curated Mistborn story experience or an open sandbox."
     mistborn_game_mode_narrative_mode:0 "Narrative Mode"
     mistborn_game_mode_narrative_mode_desc:0 "Experience the story of Mistborn: The Final Empire. Curated events will fire based on story progression. Recommended for first playthrough."
     mistborn_game_mode_sandbox_mode:0 "Sandbox Mode"
     mistborn_game_mode_sandbox_mode_desc:0 "Play as any character with full freedom. The 'Discover Your Metallic Arts' decision is available. Narrative story events are suppressed."
    
  4. Create a scripted trigger is_narrative_mode_trigger in common/scripted_triggers/mistborn_triggers.txt that checks the game rule:
    is_narrative_mode_trigger = {
        has_game_rule = mistborn_game_mode_narrative_mode
    }
    is_sandbox_mode_trigger = {
        has_game_rule = mistborn_game_mode_sandbox_mode
    }
    
  5. Verify the game rule appears on the setup screen when starting a new game.

Files:

  • common/game_rules/00_mistborn_game_rules.txt -- create
  • localization/english/mistborn_game_rules_l_english.yml -- create
  • common/scripted_triggers/mistborn_triggers.txt -- modify (add mode triggers)

References:

  • CK3 game rule syntax: game_rule_name = { default = X option_1 = { } option_2 = { } }
  • Localization pattern: localization/english/mistborn_*_l_english.yml

1.7 -- Validate Localization Coverage for New Content

Context: Any new modifiers, game rules, triggers, or suppressed content must have matching localization entries. Missing localization causes [KEY] display errors in-game.

Steps:

  1. Verify all modifier names in 00_mistborn_modifiers.txt have localization keys in mistborn_modifiers_l_english.yml. Check for:
    • lord_ruler_attention_1, lord_ruler_attention_2, lord_ruler_attention_3
    • ruin_influence, steel_inquisitor_power
    • great_house_status, atium_supply
    • ash_blight, koloss_rampage
    • compounder_modifier, steel_ministry_favor, steel_ministry_displeasure
    • breeding_for_allomancy_modifier, disapproves_skaa_mercy
  2. If the removed lord_ruler_attention_modifier has a localization key, remove it to prevent orphaned entries.
  3. Add any missing localization keys for new content created in this milestone (game rules, new triggers do not need localization, but game rule options do).
  4. Ensure all .yml files have UTF-8 BOM encoding (the first bytes must be EF BB BF).

Files:

  • localization/english/mistborn_modifiers_l_english.yml -- modify
  • localization/english/mistborn_game_rules_l_english.yml -- validate (created in 1.6)

References:

  • Existing localization: localization/english/mistborn_modifiers_l_english.yml
  • Modifier definitions: common/modifiers/00_mistborn_modifiers.txt

Testing Checkpoints

  • After 1.1 (Province History):

    • Test: Launch the mod, start a new game at 1020.1.1, inspect any county.
    • Expected: Province shows correct culture (e.g., Luthadel = noble_scadrian), correct religion (steel_ministry), and correct holding type. No "missing province history" errors in error.log.
  • After 1.3 (Triggers):

    • Test: Open console, check a character with trait_feruchemy_potential. Verify is_feruchemist_trigger returns NO for that character (should only match active trait_feruchemist).
    • Expected: Dormant potential characters are not flagged as active Feruchemists.
  • After 1.4 (Unified Attention):

    • Test: Use console to set lord_ruler_attention variable to 30 on a character. Advance 1 year. Check if lord_ruler_attention_1 modifier appears.
    • Expected: Tier 1 modifier is visible on the character. Variable and modifier are in sync.
  • After 1.5 (Vanilla Suppression):

    • Test: Start a new game, open the decision menu.
    • Expected: Only Mistborn-themed decisions appear. No "Host a Feast", "Go on a Pilgrimage", or "Found a Holy Order" options.
  • After 1.6 (Game Rules):

    • Test: Start a new game from the main menu.
    • Expected: "Game Mode" option appears on the game setup screen with "Narrative Mode" and "Sandbox Mode" choices.
  • After all tasks (Full Boot Test):

    • Test: Load the mod, start as Devinshae Venture (900001), play 5 years at speed 5.
    • Expected: No crashes. error.log is clean (no errors, warnings acceptable only for missing vanilla content we intentionally suppressed). Lord Ruler attention events fire if attention rises. Snapping events fire for children with potential. Province data is correct on the map.

Acceptance Criteria

  • The mod loads with no errors in error.log (warnings for vanilla content suppression are acceptable).
  • All 105 provinces display correct culture, religion, and holding type at game start.
  • Province history IDs match the vanilla province IDs referenced in common/landed_titles/00_landed_titles.txt.
  • No vanilla decisions appear in the decision menu.
  • The "Game Mode" game rule appears on the setup screen with Narrative and Sandbox options.
  • is_feruchemist_trigger returns false for characters with only trait_feruchemy_potential.
  • has_allomantic_potential_trigger returns false for characters who have already snapped into active Allomancers.
  • Lord Ruler attention modifiers (tiers 1-3) are applied automatically based on the attention variable thresholds.
  • No references to the old lord_ruler_attention_modifier remain in any file.
  • Culture pillars load without errors.
  • Playing as any landed character for 5 years produces no crashes.
  • All localization keys for modifiers, game rules, and decisions resolve correctly (no [KEY] display).

Risks & Mitigations

  1. Province ID mapping errors. If any vanilla province ID is wrong, the barony will have no culture/religion. Mitigation: Programmatically extract all province IDs from 00_landed_titles.txt and cross-reference.

  2. Vanilla decision suppression scope. We may miss vanilla decision files, causing some to leak through. Mitigation: Check the CK3 game directory for all files in common/decisions/ and create suppressions iteratively after testing.

  3. Game rule syntax changes. CK3 1.18 may have changed game rule syntax from earlier versions. Mitigation: Cross-reference with CK3 1.18 documentation or inspect vanilla game rule files.

  4. Culture pillar validation. The vanilla heritage_is_shown_trigger and language_is_shown_trigger may not exist in CK3 1.18 or may have different names. Mitigation: Check vanilla culture pillar files for current syntax.

Open Questions

  1. Should we suppress ALL vanilla decisions or only clearly anachronistic ones (feasts, hunts, pilgrimages)? Some mechanical decisions (e.g., disinherit heir) may still be useful. Decision needed on scope of suppression.

  2. The province history currently assigns culture = noble_scadrian to most provinces and culture = skaa to some labor/slum provinces. Should Terris provinces (24-25, 27-29, 31, 33 in the old system, now d_tathingdwen/ d_urteau area) have religion = terris_worldbringers instead of steel_ministry? Lore says the Lord Ruler publicly suppressed Terris faith, so steel_ministry may be correct with characters secretly following Terris Worldbringers.

  3. Should we create a common/on_action/ file that runs a game-start initialization effect to set up variables, flags, and initial conditions beyond what currently exists in on_game_start? This would be useful for M2 and beyond.