skills/ck3-modding/SKILL.md
Expert CK3 modding assistance - scripting, events, triggers, effects, scopes, localization
npx skillsauth add Sililex/ck3-claude-skill skills/ck3-moddingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are assisting with Crusader Kings III modding. CK3 uses a custom Paradox scripting language (called "Jomini script") — not JSON, not YAML — with its own rules.
Always consult the relevant .info reference file before writing or reviewing code for a system. They are in the reference/ directory alongside this SKILL.md.
Detailed reference files: scopes.md, effects.md, triggers.md, variables.md, ai.md, bookmarks.md, characters.md, cultures.md, decisions.md, dynasties.md, events.md, governments.md, history.md, holdings.md, lifestyles.md, regiments.md, religions.md, script_values.md, story_cycles.md, struggles.md, traits.md
Generate full lists with script_docs console command (outputs to logs folder).
add_gold). Used in effect blocks: immediate = {}, effect = {}, on_accept = {}, event option bodies. See effects.md for full reference (forms, scripted effects, text replacement pitfalls).is_ai = yes). Used in trigger blocks: limit = {}, trigger = {}, is_shown = {}, is_valid = {}. See triggers.md for full reference (early out, logic blocks, scope/value comparison, in-line complex triggers).primary_heir). We call objects "scopes" and switching between them "scoping".Never put effects where triggers are expected, or vice versa. The game will error or silently fail.
x = y
x = { y = z }
x = {
a = b
e = {
f = g
}
}
= yes).= no to check the opposite: is_ai = no is the same as NOT = { is_ai = yes }.add_gold = age adds gold equal to the character's age.add_gold = "opinion(liege)" (quotes required).#Scopes are database objects (characters, titles, provinces, cultures, faiths, etc.). Effects/triggers must be used on the correct scope type.
For the full scopes reference (context switching, list-builders, saved scopes, every_X/random_X/ordered_X/any_X, etc.), see scopes.md.
Key points:
root — default context of the current block (NOT "the player"). In events, it's the character receiving the event.prev — previous scope (one step back). Cannot be chained (prevprev does not exist).this — current scope. Useful for comparisons: this = root.title:k_france = { ... }primary_heir.faith.religious_head chains scope transitions with .title:k_france, culture:english, faith:orthodox, character:123456?= — checks existence before comparing: capital_county ?= title:c_byzantionrandom_X + save_scope_as to find and save them.Important: Do NOT use scope: before root, prev, or this. scope: is only for saved scopes.
AND, OR, NOT, NOR, NAND — convention is to capitalize for readability.
OR = {
is_ai = yes
gold > 100
}
AND — true if all conditions true (all trigger blocks are AND by default)OR — true if any condition trueNOT / NOR — true if all conditions false (they are equivalent)NAND — true if any condition falseCan be nested: OR = { AND = { NOT = { ... } } }
= — equality / scope comparison (primary_heir = primary_spouse)!= — not equal<, <=, >, >= — value comparisons?= — checks existence first, then compares. capital_county ?= title:c_byzantion is equivalent to exists = capital_county + capital_county = title:c_byzantionSave an object to reference later. Exists only in the current script execution chain (persists through called events/scripted effects, gone when the chain ends).
primary_heir = { save_scope_as = my_son }
scope:my_son = { death = natural }
save_scope_value_as saves a value or string flag:
save_scope_value_as = {
name = cost
value = primary_heir.age
}
add_gold = scope:cost
save_scope_value_as = {
name = kill_locale
value = flag:tower
}
if = {
limit = { scope:kill_locale = flag:tower }
# ...
}
In trigger blocks, use save_temporary_scope_as and save_temporary_scope_value_as. Normal versions will not work there.
Some saved scopes are premade by the game in on_actions and character interactions (e.g. scope:actor, scope:recipient). Check comments in on_action files.
See variables.md for full reference (all types, chaining, UI display, dead character variables).
Quick summary: set_variable, access with var:, change with change_variable, remove with remove_variable.
| Type | Access | Storage |
|------|--------|---------|
| Normal | var: | On scope object (lost on character death) |
| Global | global_var: | Gamestate (accessible anywhere) |
| Local | local_var: | Temporary (gone when block ends) |
| Dead | dead_var: | Dead character (requires duration) |
Variables can be chained: scope:someone.var:my_var.father.var:other_var
if = {
limit = { is_ai = no }
add_gold = 100
}
else_if = {
limit = { ... }
# effect
}
else = {
# effect
}
limit acts like AND — multiple conditions inside without needing AND = {}.
Common mistake: putting effects inside limit instead of the if block.
Replaces chains of else_ifs checking the same trigger:
switch = {
trigger = has_culture
culture:english = { add_gold = 10 }
culture:french = { add_gold = 20 }
}
while = {
count = 10
add_gold = 100
}
while = {
limit = { gold > 0 }
remove_short_term_gold = 50
}
Limited to 1000 iterations by default. No break statement.
Checks a trigger conditionally. If limit is false, the check is skipped entirely.
trigger_if = {
limit = { is_ai = no }
is_independent_ruler = yes
}
Must finish with trigger_else when using trigger_else_if (even if empty). Plain trigger_if alone doesn't require it.
Only use in trigger blocks, never in effect blocks.
Lists hold objects, variables, or string flags. Cannot hold other lists.
| Type | Add effect | Persistent? | Notes |
|------|-----------|-------------|-------|
| Simple | add_to_list | No (execution only) | |
| Local variable | add_to_local_variable_list | No | Supports duration |
| Temporary | add_to_temporary_list | No | Can be used in trigger blocks |
| Variable | add_to_variable_list | Yes (on scope) | Supports duration |
| Global variable | add_to_global_variable_list | Yes (global) | Supports duration |
Items are not duplicated if already in the list.
add_to_variable_list gotcha: run the effect on the scope that stores the list, not the target:
every_ruler = {
root = {
add_to_variable_list = {
name = rulers
target = prev
}
}
}
Always clear_variable_list before recreating a list to avoid stale items.
Check membership: is_in_list, is_target_in_variable_list, etc.
every_x — runs through all items in orderordered_x — orders by a value, can iterate all or pick onerandom_x — picks one random item, chance can be manipulatedAll support limit = {} (optional; if false, effect is skipped for that item).
any_x — returns true if condition is true for any/all itemsSupports count and percent with relational operators:
any_living_character = {
count > 10
has_culture = culture:english
is_adult = yes
}
Do NOT use limit inside any_ — it is already a trigger block. No effects either.
every_in_list, ordered_in_list, random_in_list, any_in_list (and _local_ / _global_ variants).
Specify list with list = name or variable = name.
Performance warning: Avoid nested iterators over large sets (e.g. every_living_character = { every_province = { — 20000 × 9000 iterations).
Defined in common/scripted_effects/ (global) or in event files with scripted_effect keyword:
my_effect = { add_gold = 100 }
Used: my_effect = yes
Use $PARAM$ in the definition:
gift = { add_gold = $VAL$ }
Call with: gift = { VAL = 100 }
Can replace any part of script, including effect names:
my_iterator = { every_$WHO$ = { add_gold = 10 } }
my_iterator = { WHO = child }
Can insert whole blocks:
do_anything = { $DO$ }
do_anything = { DO = "add_gold = 100" }
Convention: capitalize parameter names for readability.
Same as scripted effects but for trigger blocks. Defined in common/scripted_triggers/.
Can use = no when calling to check the inverse. Substitution works the same way.
Run a calculation and return a value. Defined in common/script_values/:
my_value = {
add = age
add = 10
divide = 5
}
Used: add_gold = my_value
In UI: [GetPlayer.MakeScope.ScriptValue('my_value')] (no GetValue).
Performance: Script values recalculate every time they're used. Complex ones cause lag, especially in UI (recalculates every frame).
There are two unrelated things both called "modifier" in CK3:
common/modifiers/. See reference/common/modifiers/_modifiers.info.ai_chance, weight_multiplier, etc.). These are unrelated to scope modifiers.my_new_modifier = {
icon = economy_positive
tax_mult = 0.25
county_opinion_add = -30
}
Icons are in gfx/interface/icons/modifiers/. Common naming: {category}_positive / {category}_negative (e.g. diplomacy_positive, health_negative, economy_positive, stress_negative, fertility_positive, prowess_negative).
Full list of modifier values (what stats they can affect): see reference/common/modifier_definition_formats/_definitions.info.
l_english: header:0 suffix (:1, :2 for revisions indicating changed text)[character.GetName], [character.GetSheHe], [title.GetName][character.Custom('custom_loc_key')]Detailed documentation for each moddable system lives in the reference/ directory, mirroring the game's .info files.
| System | Reference File |
|--------|---------------|
| Events | reference/events/_events.info |
| Decisions | reference/common/decisions/_decisions.info |
| Casus Belli | reference/common/casus_belli_types/_casus_belli.info |
| On Actions | reference/common/on_action/_on_actions.info |
| Story Cycles | reference/common/story_cycles/_story_cycles.info |
| Character Interactions | reference/common/character_interactions/_character_interactions.info |
| Traits | reference/common/traits/_traits.info |
| Buildings | reference/common/buildings/_buildings.info |
| Laws | reference/common/laws/_laws.info |
| Schemes | reference/common/schemes/scheme_types/_schemes.info |
| Factions | reference/common/factions/_factions.info |
| Script Values | reference/common/script_values/_script_values.info |
| Scripted Modifiers | reference/common/scripted_modifiers/_scripted_modifiers.info |
| Modifiers | reference/common/modifiers/_modifiers.info |
| Culture | reference/common/culture/cultures/_cultures.info |
| Religion | reference/common/religion/religions/_religions.info |
| Effect Localization | reference/common/effect_localization/_effect_localization.info |
| Trigger Localization | reference/common/trigger_localization/_trigger_localization.info |
| Custom Loc | reference/common/customizable_localization/_custom_loc.info |
| Succession | reference/common/succession_election/_succession_election.info |
| Men at Arms | reference/common/men_at_arms_types/_men_at_arms_types.info |
| Governments | reference/common/governments/_governments.info |
| History | reference/history/_history.info |
All 165 .info files are in reference/, preserving the game directory structure. Browse:
reference/common/ — gameplay systemsreference/events/ — event structurereference/history/ — history file formatreference/gfx/ — graphical definitionsreference/gui/ — UI widgetseffect add_gold = 100 or trigger is_ai = yesexplorer console commandrun/filename.txt, execute with run filename.txt-develop launch option: instant reload of events/decisions-debug_mode launch option: enables consolerelease_mode console command: shows error tracker in gameRun python sync_ck3_info.py from the repo root to re-sync after game updates. The script auto-detects common Steam paths, or use --game-dir to specify your CK3 install. See README.md in the repo root for details.
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.