When creating or editing files in common/factions/rules/, follow these conventions, derived from the engine documentation in common/factions/_documentation.md.
create_faction = NAME is deprecated in MD — use create_faction_from_template = TEMPLATE instead for DLC compatibility.
Every faction rule must include a type field. Valid built-in types and their trigger scopes:
| Type | trigger SCOPE |
trigger FROM |
|---|---|---|
joining_rules |
Joining country | Faction leader |
war_declaration_rules |
Country declaring war | Target country |
call_to_war_rules |
Country calling to war | Target country |
member_rules |
Faction leader | — |
change_leader_rules |
Country becoming leader | — |
peace_conference_rules |
(used for peace_action_modifiers) | — |
dismissal_rules |
(dismissal member trigger) | — |
contribution_rule |
(contribution effects) | — |
Important: The trigger scope varies by type (table above), but visible, available, can_remove, and ai_will_do always scope to the faction leader country.
Every player-selectable faction rule should include:
type— always requiredavailable— controls selection; includeis_locked_faction = nofor rules that shouldn't be changeable in locked factions (NATO, CSTO, Resistance Axis)can_remove— controls deselection of an active rule; must include the locked faction check for any rule withis_locked_faction = noinavailableortrigger:can_remove = { NOT = { ROOT = { is_locked_faction = yes } } }ai_will_do— AI weighting (usebase = 0if AI should not pick it by default)
Locked factions (NATO, CSTO, Resistance Axis) use the is_locked_faction scripted trigger. Rules must prevent players from changing settings on locked factions in two places:
available:is_locked_faction = no— prevents selecting the rulecan_remove:NOT = { ROOT = { is_locked_faction = yes } }— prevents deselecting an active rule
Both are needed because available only blocks selection, not removal. Without can_remove, a player could still deselect existing rules on NATO/CSTO.
For "impossible" rules (active only when the faction IS locked), invert the pattern:
available = {
is_locked_faction = yes
}
can_remove = {
NOT = {
ROOT = {
is_locked_faction = yes
}
}
}
Some rules have extra can_remove conditions (e.g., faction_manifest_fulfillment > 0.95). When adding the locked faction check, place the NOT = { ROOT = { is_locked_faction = yes } } block inside the existing can_remove, alongside the other conditions (they AND together implicitly):
can_remove = {
NOT = {
ROOT = {
is_locked_faction = yes
}
}
faction_manifest_fulfillment > 0.95
}
not_locked_faction is not a valid scripted trigger. The engine silently evaluates it as always-false (or always-true depending on context), breaking the rule's availability logic. Always use is_locked_faction = no:
available = {
is_locked_faction = no # correct
}
Not:
available = {
not_locked_faction = no # WRONG — does not exist
}
- Rules with
available = { always = no }— already permanently unavailable (e.g., scripted-only rules likecall_to_war_rule_chinese_united_front) - Rules with
can_remove = { always = no }— already permanently locked (e.g.,change_leader_rule_never) - Rules with
trigger = { always = no }— never trigger, no removal protection needed unless the rule should only exist on locked factions
Rules with type = peace_conference_rules must include a peace_action_modifiers block referencing modifiers from common/peace_conference/cost_modifiers. The enable trigger on the modifier does not run — the modifier is active as long as the rule is.
- Has
typeset to a valid rule type - Has
availablewithis_locked_faction = no(unless intentionally locked-only or scripted-only) - Has
can_removewith locked faction NOT check (unlessalways = noor scripted-only) - Has
ai_will_dowith appropriate weighting - Has
triggerwith correct scope awareness (see type table above) visibleis only included when the rule should be hidden from certain factions/governments- For
peace_conference_rules: haspeace_action_modifiersblock - For
contribution_rule: haseffectblock withset_faction_member_upgrade_min