529 lines
18 KiB
Markdown
529 lines
18 KiB
Markdown
# CK3 Modding Reference
|
|
|
|
Practical reference for developing Crusader Kings III mods, with emphasis on
|
|
total conversion considerations. Based on official wiki documentation,
|
|
community guides, and patterns from major TC mods (AGOT, Elder Kings 2,
|
|
Princes of Darkness, Atlantis template).
|
|
|
|
## Mod Structure
|
|
|
|
### Directory Layout
|
|
|
|
```
|
|
my_mod/ # Mod root folder
|
|
├── descriptor.mod # Inner descriptor (no path field)
|
|
├── thumbnail.png # Steam Workshop thumbnail
|
|
├── common/ # Game data definitions (80+ subdirs)
|
|
│ ├── bookmarks/ # Start screen scenarios
|
|
│ ├── buildings/ # Holdings and buildings
|
|
│ ├── casus_belli_types/ # War justifications
|
|
│ ├── character_interactions/ # Diplomatic actions
|
|
│ ├── council_positions/ # Council roles
|
|
│ ├── council_tasks/ # Council job actions
|
|
│ ├── culture/ # Culture definitions
|
|
│ │ ├── cultures/ # Culture entries
|
|
│ │ ├── pillars/ # Heritage, language, martial custom, etc.
|
|
│ │ └── traditions/ # Cultural traditions
|
|
│ ├── decisions/ # Player/AI decisions
|
|
│ ├── defines/ # Game constants and tunables
|
|
│ ├── dynasties/ # Dynasty definitions
|
|
│ ├── dynasty_houses/ # Cadet branches
|
|
│ ├── dynasty_legacies/ # Legacy tracks
|
|
│ ├── ethnicities/ # Portrait ethnicity definitions
|
|
│ ├── factions/ # Faction types
|
|
│ ├── governments/ # Government forms
|
|
│ ├── holdings/ # Holding types
|
|
│ ├── landed_titles/ # Title hierarchy (empire → barony)
|
|
│ ├── laws/ # Realm laws
|
|
│ ├── lifestyles/ # Lifestyle focuses and perks
|
|
│ ├── men_at_arms_types/ # Custom troop types
|
|
│ ├── modifiers/ # Static modifiers
|
|
│ ├── nicknames/ # Character nicknames
|
|
│ ├── on_action/ # Event hooks (game triggers)
|
|
│ ├── province_terrain/ # Terrain types per province
|
|
│ ├── religion/ # Religion definitions
|
|
│ │ ├── religions/ # Religion and faith entries
|
|
│ │ └── holy_sites/ # Holy site definitions
|
|
│ ├── schemes/ # Intrigue schemes
|
|
│ ├── script_values/ # Reusable numeric values
|
|
│ ├── scripted_effects/ # Reusable effect blocks
|
|
│ ├── scripted_modifiers/ # Reusable modifier blocks
|
|
│ ├── scripted_triggers/ # Reusable condition blocks
|
|
│ └── traits/ # Character traits
|
|
├── events/ # Event scripts
|
|
├── gfx/ # Graphics (icons, portraits, UI art)
|
|
│ └── interface/icons/traits/ # Trait icon DDS files
|
|
├── gui/ # UI layout overrides
|
|
├── history/ # Starting game state
|
|
│ ├── characters/ # Character definitions with dates
|
|
│ ├── provinces/ # Province culture/religion/holdings
|
|
│ └── titles/ # Title holder history
|
|
├── localization/ # Display strings
|
|
│ └── english/ # l_english.yml files
|
|
│ └── replace/ # Overrides for vanilla loc keys
|
|
└── map_data/ # Map files (TC only)
|
|
├── default.map # Province groupings
|
|
├── definitions.csv # Province ID → RGB mapping
|
|
├── geographical_regions/ # Regional groupings
|
|
├── provinces.png # Province map image
|
|
├── rivers.png # River overlay
|
|
└── heightmap files # Terrain elevation
|
|
my_mod.mod # Outer descriptor (has path field)
|
|
```
|
|
|
|
### The .mod Descriptor Files
|
|
|
|
Every mod needs two descriptor files with identical content except for the
|
|
`path` field:
|
|
|
|
**Outer file** (`my_mod.mod`) — sits alongside the mod folder in the mod
|
|
directory. Includes the `path` field:
|
|
|
|
```
|
|
version="1.0.0"
|
|
tags={
|
|
"Total Conversion"
|
|
}
|
|
name="My Mod"
|
|
supported_version="1.12.*"
|
|
path="mod/my_mod"
|
|
```
|
|
|
|
**Inner file** (`my_mod/descriptor.mod`) — sits inside the mod folder.
|
|
Same content but **without** the `path` field:
|
|
|
|
```
|
|
version="1.0.0"
|
|
tags={
|
|
"Total Conversion"
|
|
}
|
|
name="My Mod"
|
|
supported_version="1.12.*"
|
|
```
|
|
|
|
#### Descriptor Fields
|
|
|
|
| Field | Required | Description |
|
|
|---|---|---|
|
|
| `name` | Yes | Display name in the launcher |
|
|
| `version` | No | Mod version string (informational only) |
|
|
| `supported_version` | Yes | Game version compatibility (wildcards OK: `1.12.*`) |
|
|
| `path` | Outer only | Path to mod folder. **Use forward slashes only.** |
|
|
| `tags` | No | Category tags (`"Total Conversion"`, `"Gameplay"`, etc.) |
|
|
| `picture` | No | Thumbnail filename (e.g., `thumbnail.png`) |
|
|
| `remote_file_id` | No | Steam Workshop item ID (added automatically on upload) |
|
|
|
|
### Mod Installation Paths
|
|
|
|
| Platform | Path |
|
|
|---|---|
|
|
| Windows | `%USERPROFILE%\Documents\Paradox Interactive\Crusader Kings III\mod\` |
|
|
| Linux | `~/.local/share/Paradox Interactive/Crusader Kings III/mod/` |
|
|
| Mac | `~/Library/Application Support/Paradox Interactive/Crusader Kings III/mod/` |
|
|
|
|
Both the `.mod` file and the mod folder live in this directory.
|
|
|
|
## File Loading and Override Rules
|
|
|
|
### How CK3 Loads Mod Files
|
|
|
|
When a mod provides a file at the same path as a vanilla file, the mod's file
|
|
**replaces the entire vanilla file**. This is whole-file replacement — you
|
|
cannot partially override a file just by having the same filename.
|
|
|
|
### LIOS — Last In, Only Served (Most Files)
|
|
|
|
For most game data (traits, decisions, events, etc.), when multiple definitions
|
|
of the same object exist across different files, the **last loaded** definition
|
|
wins. Files load in ASCIIbetical order by filename:
|
|
|
|
- `01_my_traits.txt` loads after and overrides objects in `00_vanilla_traits.txt`
|
|
- Within the same file, later definitions override earlier ones
|
|
|
|
This means you can override a single vanilla object by creating a new file
|
|
that sorts after the vanilla file and redefining just that object.
|
|
|
|
### FIOS — First In, Only Served (GUI Files)
|
|
|
|
GUI types and templates use the opposite rule: the **first loaded** definition
|
|
wins. To override vanilla GUI, name your file to sort before the vanilla file.
|
|
|
|
### Localization Override
|
|
|
|
Place overriding localization files in `localization/<language>/replace/`.
|
|
Files in the `replace/` subfolder can override individual keys from vanilla
|
|
without errors. Overriding keys outside of `replace/` works but produces
|
|
log warnings.
|
|
|
|
### Multiple Mods
|
|
|
|
When two mods provide the same file, the mod **lower in the playset** (load
|
|
order) takes priority.
|
|
|
|
## Localization
|
|
|
|
### File Format
|
|
|
|
Localization files are YAML with strict requirements:
|
|
|
|
- **Encoding**: UTF-8 with BOM (byte order mark). Files without BOM will
|
|
silently fail to load or cause validation errors.
|
|
- **Filename**: Must end in `_l_<language>.yml` (e.g., `mistborn_traits_l_english.yml`)
|
|
- **First line**: Must declare the language: `l_english:`
|
|
|
|
### Entry Format
|
|
|
|
```yaml
|
|
l_english:
|
|
key_name:0 "Display text with $VARIABLE$ substitution"
|
|
another_key:1 "Text with [concept|E] references"
|
|
```
|
|
|
|
The number after the colon (`:0`, `:1`) is a version number used by Paradox
|
|
for tracking translation updates. For mods, use `0` or `1`.
|
|
|
|
### Tips
|
|
|
|
- Copy an existing vanilla `.yml` file to inherit the correct BOM encoding
|
|
- Every trait, event option, decision, title, modifier, and event description
|
|
needs a localization entry
|
|
- Missing keys display as raw key names in-game (e.g., `trait_misting_coinshot`)
|
|
|
|
## Events
|
|
|
|
### Namespace Declaration
|
|
|
|
Every event file must declare its namespace at the top:
|
|
|
|
```
|
|
namespace = mistborn_snapping
|
|
```
|
|
|
|
### Event Structure
|
|
|
|
```
|
|
mistborn_snapping.0001 = {
|
|
type = character_event
|
|
title = mistborn_snapping.0001.t
|
|
desc = mistborn_snapping.0001.desc
|
|
theme = intrigue
|
|
|
|
trigger = {
|
|
# Conditions that must be true
|
|
}
|
|
|
|
immediate = {
|
|
# Effects that run before the event displays
|
|
}
|
|
|
|
option = {
|
|
name = mistborn_snapping.0001.a
|
|
# Effects when this option is chosen
|
|
}
|
|
}
|
|
```
|
|
|
|
### Event Types
|
|
|
|
- `character_event` — Targets a character, shows a popup
|
|
- `letter_event` — Displayed as a letter/message
|
|
- `court_event` — Royal court events (DLC)
|
|
|
|
## Traits
|
|
|
|
### Structure
|
|
|
|
```
|
|
trait_name = {
|
|
index = 200 # Unique index, avoid vanilla range
|
|
|
|
category = fame # fame, lifestyle, personality, etc.
|
|
|
|
genetic = yes # Can be inherited
|
|
inherit_chance = 15 # Base inheritance chance (%)
|
|
both_parent_has_trait_inherit_chance = 30
|
|
|
|
# Stat modifiers
|
|
prowess = 3
|
|
martial = 1
|
|
|
|
# Display
|
|
icon = "gfx/interface/icons/traits/trait_name.dds"
|
|
ruler_designer_cost = 50
|
|
shown_in_ruler_designer = yes
|
|
}
|
|
```
|
|
|
|
- Use `index` values 200+ to avoid conflicts with vanilla traits
|
|
- `group` field makes traits mutually exclusive within that group
|
|
- `birth = <weight>` controls random assignment at birth (0 = never)
|
|
|
|
## Scripted Triggers and Effects
|
|
|
|
### Triggers
|
|
|
|
Reusable conditions that return true/false:
|
|
|
|
```
|
|
is_allomancer_trigger = {
|
|
OR = {
|
|
has_trait = trait_misting_coinshot
|
|
has_trait = trait_misting_lurcher
|
|
has_trait = trait_mistborn
|
|
}
|
|
}
|
|
```
|
|
|
|
### Effects
|
|
|
|
Reusable blocks of game actions:
|
|
|
|
```
|
|
activate_allomancy_effect = {
|
|
remove_trait = trait_allomantic_potential
|
|
random_list = {
|
|
30 = { add_trait = trait_misting_coinshot }
|
|
30 = { add_trait = trait_misting_lurcher }
|
|
5 = { add_trait = trait_mistborn }
|
|
}
|
|
}
|
|
```
|
|
|
|
## On-Actions
|
|
|
|
Game hooks that fire events/effects when specific things happen:
|
|
|
|
```
|
|
on_birthday = {
|
|
events = {
|
|
mistborn_snapping.0001 # Check for natural snapping
|
|
}
|
|
effect = {
|
|
# Inline effects
|
|
}
|
|
}
|
|
```
|
|
|
|
Common hooks: `on_birthday`, `on_yearly_pulse`, `on_game_start`,
|
|
`on_death`, `on_war_started`, `on_title_gain`, `on_marriage`.
|
|
|
|
## Decisions
|
|
|
|
```
|
|
my_decision = {
|
|
picture = "gfx/interface/illustrations/decisions/decision_something.dds"
|
|
|
|
is_shown = {
|
|
# Conditions for the decision to appear in the list
|
|
}
|
|
|
|
is_valid = {
|
|
# Conditions for the decision to be clickable
|
|
}
|
|
|
|
cost = {
|
|
gold = 100
|
|
prestige = 200
|
|
}
|
|
|
|
effect = {
|
|
# What happens when the player takes the decision
|
|
}
|
|
|
|
ai_will_do = {
|
|
base = 10 # AI weight for taking this decision
|
|
}
|
|
}
|
|
```
|
|
|
|
## History Files
|
|
|
|
### Characters
|
|
|
|
```
|
|
900001 = {
|
|
name = "Devinshae"
|
|
dynasty = dynasty_venture
|
|
religion = "steel_ministry"
|
|
culture = "noble_scadrian"
|
|
trait = trait_misting_seeker
|
|
trait = education_intrigue_4
|
|
father = 900000
|
|
900.1.1 = { birth = yes }
|
|
930.1.1 = { death = yes }
|
|
}
|
|
```
|
|
|
|
Dates use `yyyy.mm.dd` format. Use `birth = yes` and `death = yes` inside
|
|
date blocks. Character IDs should avoid vanilla ranges (use 900000+).
|
|
|
|
### Titles
|
|
|
|
```
|
|
e_final_empire = {
|
|
900.1.1 = {
|
|
holder = 900001
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Total Conversion Considerations
|
|
|
|
### The Core Challenge
|
|
|
|
A total conversion must clear vanilla CK3 content while building an entirely
|
|
new world. This means replacing or masking: the map, all provinces and titles,
|
|
all cultures and religions, all characters and dynasties, all bookmarks.
|
|
|
|
### Approaches to Vanilla Content
|
|
|
|
**Option A: Replace with empty files** — Create empty files matching every
|
|
vanilla filename you want to suppress. Simple but tedious and fragile across
|
|
game updates.
|
|
|
|
**Option B: Preserve but hide (Atlantis approach)** — Keep vanilla content as
|
|
invisible placeholders, redirect references to dummy entries, hide elements
|
|
through GUI overrides. More resilient to game patches.
|
|
|
|
**Option C: replace_path directive** — The `replace_path` field in
|
|
descriptor.mod *should* tell the engine to ignore entire vanilla directories.
|
|
In practice this has been unreliable in CK3 (reported broken since 1.1).
|
|
Don't depend on it; use Option A or B instead.
|
|
|
|
### Map
|
|
|
|
Total conversions that want a custom map must provide:
|
|
|
|
- `map_data/provinces.png` — Province boundaries (each province a unique RGB)
|
|
- `map_data/definitions.csv` — Maps province IDs to RGB colors
|
|
- `map_data/rivers.png` — River overlay
|
|
- `map_data/default.map` — Province groupings and sea zones
|
|
- `map_data/heightmap` files — Terrain elevation
|
|
- `map_data/geographical_regions/` — Regional groupings
|
|
|
|
Broken `rivers.png` files can crash the game. Test map changes incrementally.
|
|
|
|
If your TC doesn't need a custom map (e.g., uses abstract/minimal geography),
|
|
you can reuse the vanilla map and just reassign provinces.
|
|
|
|
### Portraits and DNA
|
|
|
|
- Custom portrait assets go in `gfx/portraits/`
|
|
- Ethnicities are defined in `common/ethnicities/`
|
|
- Characters without proper ethnicity/portrait setup will appear bald
|
|
- Add `has_scripted_appearance = yes` to character history to use scripted
|
|
(non-DNA) appearance
|
|
- DNA strings can be generated using the in-game portrait editor (debug mode)
|
|
|
|
### Culture and Religion Families
|
|
|
|
- Religion families (`family = rf_scadrial`) are auto-created on first use
|
|
but verify this works with your CK3 version
|
|
- Culture pillars (heritage, language, martial_custom, ethos) must be defined
|
|
before cultures that reference them
|
|
- File load order matters — name pillar files to sort before culture files
|
|
|
|
### Bookmarks
|
|
|
|
Bookmarks define the start screen scenarios. Each needs:
|
|
|
|
- A bookmark definition in `common/bookmarks/`
|
|
- Character entries with portraits and descriptions
|
|
- Localization for all display text
|
|
- At least one playable character with a valid title
|
|
|
|
### What Bleeds Through
|
|
|
|
Even in a TC, vanilla content can appear unexpectedly:
|
|
|
|
- **Decisions** — Vanilla decisions may show up if not replaced/emptied
|
|
- **Events** — Vanilla on_action hooks still fire unless overridden
|
|
- **GUI text** — Hardcoded strings and tooltips reference vanilla concepts
|
|
- **AI behavior** — AI weights reference vanilla traits, cultures, religions
|
|
- **Name lists** — Without custom name lists, characters get vanilla names
|
|
|
|
Test thoroughly and check `error.log` for references to content you didn't
|
|
create.
|
|
|
|
---
|
|
|
|
## Development Workflow
|
|
|
|
### Setup
|
|
|
|
1. Create your mod folder in the CK3 mod directory
|
|
2. Create both `.mod` files (outer with path, inner without)
|
|
3. Add Steam launch options: `-debug_mode -develop`
|
|
4. Create a playset in the launcher with your mod enabled
|
|
5. Reload mods in launcher: Mods → Manage All Mods → Reload Installed Mods
|
|
|
|
### Debug Mode
|
|
|
|
Launch with `-debug_mode` to enable:
|
|
|
|
- **Console** — Press tilde (`~`) to open. Tab for command list.
|
|
- **error.log** — Written to `Documents/Paradox Interactive/Crusader Kings III/logs/error.log`. Check this after every launch.
|
|
- **Event testing** — `test_event <id> <char_id>` checks if an event can fire; `debug_event <id> <char_id>` forces it to fire
|
|
|
|
### Useful Console Commands
|
|
|
|
| Command | Description |
|
|
|---|---|
|
|
| `event <id>` | Fire an event for the player character |
|
|
| `test_event <id> <char_id>` | Check if event can trigger (prints result) |
|
|
| `debug_event <id> <char_id>` | Force-fire event and print trigger info |
|
|
| `add_trait <trait>` | Add a trait to the player character |
|
|
| `remove_trait <trait>` | Remove a trait |
|
|
| `add_gold <amount>` | Add gold |
|
|
| `add_prestige <amount>` | Add prestige |
|
|
| `yesmen` | AI always accepts proposals |
|
|
| `observe` | Enter observer mode |
|
|
| `charinfo` | Toggle character debug info |
|
|
| `reload <file>` | Hot-reload a specific file |
|
|
|
|
### Iterative Testing
|
|
|
|
1. Make changes to mod files
|
|
2. Launch game (or use `reload` for supported file types)
|
|
3. Check `error.log` for new errors
|
|
4. Use console to test specific events/traits/decisions
|
|
5. Fix issues, repeat
|
|
|
|
### Publishing to Steam Workshop
|
|
|
|
1. In the CK3 launcher, go to Mods
|
|
2. Click "Upload Mod" → select your mod
|
|
3. Set the suggested game version, add thumbnail and description
|
|
4. The launcher handles packaging and upload
|
|
5. `remote_file_id` is added to your `.mod` file automatically
|
|
|
|
To update: make changes locally, then re-upload through the launcher.
|
|
|
|
---
|
|
|
|
## Common Pitfalls
|
|
|
|
| Issue | Cause | Fix |
|
|
|---|---|---|
|
|
| Localization not loading | Missing UTF-8 BOM encoding | Re-save files as UTF-8 with BOM |
|
|
| Traits not appearing | Wrong `index` collision or missing loc | Use unique indices 200+, add loc entries |
|
|
| Events silently failing | Namespace mismatch or missing event file | Check namespace declaration matches event prefix |
|
|
| Decision not showing | `is_shown` trigger failing | Test individual trigger conditions in console |
|
|
| Vanilla content showing | Not all vanilla files overridden | Empty/replace all relevant vanilla files |
|
|
| Characters appear bald | Missing ethnicity or portrait data | Add `has_scripted_appearance` or define ethnicities |
|
|
| Game crash on load | Broken map files or circular references | Test incrementally, check `error.log` |
|
|
| `replace_path` not working | Known CK3 bug/limitation | Use empty file replacement instead |
|
|
| Wrong trait activating | LIOS override — file sort order wrong | Name files to sort correctly (00_, 01_ prefix) |
|
|
| Mod not detected | `.mod` file path wrong or uses backslashes | Use forward slashes only in path field |
|
|
|
|
## Resources
|
|
|
|
- [CK3 Paradox Wiki — Modding](https://ck3.paradoxwikis.com/Modding)
|
|
- [CK3 Paradox Wiki — Mod Structure](https://ck3.paradoxwikis.com/Mod_structure)
|
|
- [CK3 Community Modding Documentation (GitHub)](https://github.com/CK3-Modding/Documentation)
|
|
- [Atlantis TC Template (GitHub)](https://github.com/bombusfrigidus/Atlantis)
|
|
- [CK3 Mod Co-op Discord](https://discord.com/invite/F7Ta5R2rFR)
|
|
- [Paradox Modding Forums](https://forum.paradoxplaza.com/forum/forums/crusader-kings-iii-modding.1080/)
|