脚本:修订间差异

本页面所适用的版本可能已经过时,最后更新于2.8
无编辑摘要
无编辑摘要
第20行: 第20行:
# Definition of entity_1
# Definition of entity_1
entity_1 = {
entity_1 = {
  property_1 = value1
   property_1 = value1
  property_2 = value2
   property_2 = value2


  block_1 = { # A condition block
   block_1 = { # A condition block
   scope = {
scope = {
    condition = yes
   condition = yes
}
    }
    }
  }
   block_2 = { # A command block
  block_2 = { # A command block
scope = {
   scope = {
   command = yes
    command = yes
}
    }
    }
  }
}
}


第95行: 第95行:
|-
|-
! AND
! AND
| Returns true if all enclosed conditions return true. This is the default operator.
| Returns true if all enclosed conditions return true. This is the default operator after a scope change.
|<pre>
|<pre>
AND = {
AND = {
  age = 15
   age >= 15
  NOT = {
    age < 35
    age = 35
  }
}
}
</pre>
</pre>
第109行: 第107行:
|<pre>
|<pre>
OR = {
OR = {
  culture = norman 
   culture = norman 
  culture = saxon
   culture = saxon
}
}
</pre>
</pre>
第116行: 第114行:
! NOT 
! NOT 
|Returns true if the enclosed condition is false. 
|Returns true if the enclosed condition is false. 
It behaves the same as NOR (i.e default operator becomes OR rather than AND inside NOT), which can be confusing.
It behaves the same as NOR, but for clarity NOR is preferred in case of more than 1 condition.
 
Thus for clarity NOR should be preferred in case of more than 1 condition.
|<pre>
|<pre>
NOT = { trait = on_hajj }
NOT = { trait = on_hajj }
第127行: 第123行:
|<pre>
|<pre>
NOR = {
NOR = {
  trait = seducer
   trait = seducer
  trait = seductress
   trait = seductress
}
}
</pre>
</pre>
第134行: 第130行:
<pre>
<pre>
NOT = { 
NOT = { 
  OR = { 
   OR = { 
    trait = seducer
trait = seducer
    trait = seductress
trait = seductress
  }
    }
}
</pre>
or alternately:
<pre>
AND = {
   NOT = { trait = seducer }
    NOT = { trait = seductress }
}
}
</pre>
</pre>
第145行: 第148行:
|<pre>
|<pre>
NAND = {
NAND = {
  culture = norman
   culture = norman
  religion = catholic
   religion = catholic
}
}
</pre>
</pre>
第152行: 第155行:
<pre>
<pre>
NOT = { 
NOT = { 
  AND = { 
   AND = { 
    culture = norman
culture = norman
    religion = catholic
religion = catholic
  }
    }
}
</pre>
or alternately:
<pre>
OR = {
   NOT = { culture = norman }
    NOT = { religion = catholic }
}
}
</pre>
</pre>
|-
|-
! calc_true_if
! calc_true_if
| Returns true if at least ''amount'' conditions return true.
| Returns true if at least <code>amount</code> conditions return true.
<code>amount</code> supports numerical operators as of [[patch 2.8]].
|<pre>
|<pre>
calc_true_if = {
calc_true_if = {
  amount = 3
   amount >= 3
  culture = swedish
   culture = swedish
  religion = catholic
   religion = catholic
  is_female = no
   is_female = no
  is_adult = yes
   is_adult = yes
  age = 50
   age >= 50
}
}
</pre>
</pre>
第188行: 第199行:
| = || Greater or equal || <code>ai_greed = 5</code>
| = || Greater or equal || <code>ai_greed = 5</code>
| <code>ai_greed = 5</code>
| <code>ai_greed = 5</code>
| Backward-compatible greater equivalence (mandatory in version 2.7.2 or less).  Contrary to expectation, this is not an exact comparison.  <code>ai_greed = 5</code> is read as "ai_greed is at least 5", not "ai_greed is equal to 5". When inside a NOT, it is read as "ai_greed is lesser than 5".
| Backward-compatible greater equivalence (mandatory in version [[Patch 2.7.X#Patch 2.7.2{{!}}2.7.2]] or earlier).  Contrary to expectation, this is not an exact comparison.  <code>ai_greed = 5</code> is read as "ai_greed is at least 5", not "ai_greed is equal to 5". When inside a NOT, it is read as "ai_greed is lesser than 5".
|-
|-
| < || Less than || <code>ai_greed < 5</code>
| < || Less than || <code>ai_greed < 5</code>
第220行: 第231行:
===If===
===If===


If/limit statements allow to execute commands only if certain conditions are met. The structure is:
<code>if</code> statements allow to execute commands only if certain conditions are met. The structure is:
<pre>
<pre>
if = {
if = {
  limit = {
   limit = {
   #Conditions
#Conditions
  }
   }
  #Commands to execute if the conditions match
   #Commands to execute if the conditions match
}
}
</pre>
</pre>
第232行: 第243行:
===Else_if/Else===
===Else_if/Else===


In version 2.8, "else_if" and "else" statements were added.
In version 2.8, <code>else_if</code> and <code>else</code> statements were added.
<pre>
<pre>
if = {
if = {
  limit = {
   limit = {
   #Conditions
#Conditions
  }
   }
  #IF: Commands to execute if the conditions match
   #IF: Commands to execute if the conditions match
}
}
else_if = {
else_if = {
  limit = {
   limit = {
   #Other conditions
#Other conditions
  }
   }
  #ELSE IF: Commands to execute if the previous "if" did not match, and these other conditions matched
   #ELSE IF: Commands to execute if the previous <code>if</code> did not match, and these other conditions are met
}
}
else_if = {
else_if = {
  limit = {
   limit = {
   #Additional conditions
#Additional conditions
  }
   }
  #ELSE IF (2): Commands to execute if the previous "if" or "else_if" did not match, and these additional conditions matched
   #ELSE IF (2): Commands to execute if the previous <code>if</code> and <code>else_if</code> did not match, and these additional conditions are met
}
}
else = {
else = {
  #ELSE: Commands to execute if none of the above limits passed
   #ELSE: Commands to execute if none of the above limits passed
}
}
</pre>
</pre>


In versions prior to 2.8, if/else_if/else can still be simulated by repeating the conditions in an "if" with a NOT operator:
In versions prior to 2.8, <code>if/else_if/else</code> can still be simulated by repeating the conditions in an <code>if</code> with a <code>NOT</code> operator:
<pre>
<pre>
if = {
if = {
  limit = {
   limit = {
   #Conditions
#Conditions
  }
   }
  #IF: Commands to execute if conditions match
   #IF: Commands to execute if conditions are met
}
}
if = {
if = {
  limit = {
   limit = {
   NOT = { 
NOT = { 
    #Conditions
   #Conditions
}
     #Other conditions
    }
    }
    #Other conditions
    #ELSE IF: Commands to execute if the previous conditions are not met, but these other conditions are met
  }
  #ELSE IF: Commands to execute if the previous conditions did not match, and these other conditions matched
}
}
if = {
if = {
  limit = {
   limit = {
   NOT = { 
NOT = { 
    #Conditions
   #Conditions
    #Other conditions
   #Other conditions
}
#Additional conditions
    }
    }
    #Additional conditions
    #ELSE IF (2): Commands to execute if neither the previous conditions nor other conditions are met, but these additional conditions are met
  }
  #ELSE IF (2): Commands to execute if neither the previous conditions nor other conditions matched, and these additional conditions matched
}
}
if = {
if = {
  limit = {
   limit = {
   NOT = { 
NOT = { 
    #Conditions
   #Conditions
    #Other conditions
   #Other conditions
    #Additional conditions
   #Additional conditions
}
    }
    }
  }
   #ELSE: Commands to execute if none of the above conditions are met
  #ELSE: Commands to execute if none of the above conditions matched
}
</pre>
 
===Trigger If===
 
Patch 3.0 added support for control flow statements in trigger clauses, expanding on <code>condition_tooltip</code>. <code>trigger_if</code> is an alias for <code>conditional_tooltip</code>, which can be chained with <code>trigger_else_if</code> and <code>trigger_else</code>. Additionally, <code>limit</code> can be used instead of <code>trigger</code>, matching regular syntax for <code>if</code>.
These are very useful in indicating control flow and intent in lengthy, complex triggers. Keep in mind that long chains of <code>trigger_else_if</code> that require one of the <code>limit</code> clauses to be true have to end with <code>trigger_else</code>, since the entire chain is ignored and is thus always true if none of the <code>limit</code> clauses are true.
 
Consider the following:
<pre>
OR = {
   AND = {
is_female = yes
NOR = {
   religion_group
   has_religion_feature - religion_patriarchal
}
   }
   AND = {
is_female = no
NOT = { has_religion_feature = religion_matriarchal
   }
}
</pre>
This can be written much clearer in the following way:
<pre>
trigger_if = {
   limit = { is_female = yes }
   NOR = {
religion_group = muslim
has_religion_feature = religion_patriarchal
   }
}
trigger_else = {
   NOT = { has_religion_feature = religion_matriarchal }
}
}
</pre>
</pre>
第309行: 第355行:
<pre>
<pre>
trigger_switch = {
trigger_switch = {
on_trigger = religion_group
   on_trigger = religion_group
christian = { FROM = { add_trait = sympathy_christendom } }
   christian = { FROM = { add_trait = sympathy_christendom } }
muslim = { FROM = { add_trait = sympathy_islam } }
   muslim = { FROM = { add_trait = sympathy_islam } }
pagan_group = { FROM = { add_trait = sympathy_pagans } }
   pagan_group = { FROM = { add_trait = sympathy_pagans } }
zoroastrian_group = { FROM = { add_trait = sympathy_zoroastrianism } }
   zoroastrian_group = { FROM = { add_trait = sympathy_zoroastrianism } }
jewish_group = { FROM = { add_trait = sympathy_judaism } }
   jewish_group = { FROM = { add_trait = sympathy_judaism } }
indian_group = { FROM = { add_trait = sympathy_indian } }
   indian_group = { FROM = { add_trait = sympathy_indian } }
}
}
</pre>
</pre>
第321行: 第367行:
<pre>
<pre>
any_demesne_title = {
any_demesne_title = {
trigger_switch = {
   trigger_switch = {
on_trigger = title
on_trigger = title
e_hre = { holder_scope = { ... } }
e_hre = { holder_scope = { ... } }
e_byzantium = { holder_scope = { ... } }
e_byzantium = { holder_scope = { ... } }
k_france = { holder_scope = { ... } }
k_france = { holder_scope = { ... } }
}
   }
}
}
</pre>
</pre>
第332行: 第378行:
<pre>
<pre>
any_realm_province = {
any_realm_province = {
trigger_switch = {
   trigger_switch = {
on_trigger = province_id
on_trigger = province_id
31 = { owner = { ... } }
31 = { owner = { ... } }
32 = { owner = { ... } }
32 = { owner = { ... } }
}
   }
}
}
</pre>
</pre>
第343行: 第389行:
# Gives 100 wealth to christians, 100 piety to muslims and 100 prestige to a character belonging to any other religion group
# Gives 100 wealth to christians, 100 piety to muslims and 100 prestige to a character belonging to any other religion group
trigger_switch = {
trigger_switch = {
on_trigger = religion_group
   on_trigger = religion_group
christian = { wealth = 100 }
   christian = { wealth = 100 }
muslim = { piety = 100 }
   muslim = { piety = 100 }
fallback = { prestige = 100 }
   fallback = { prestige = 100 }
}
}
</pre>
</pre>
第355行: 第401行:
<pre>
<pre>
if = {
if = {
  limit = {
   limit = {
   #Conditions
#Conditions
  }
   }
  #Commands to execute if the conditions match
   #Commands to execute if the conditions match
  break = yes
   break = yes
}
}
#Commands to execute if the conditions do not match
#Commands to execute if the conditions do not match
第371行: 第417行:
<pre>
<pre>
while = {
while = {
limit = { 
   limit = { 
# conditions
# conditions
}
   }
# effects that are executed until the limit is no longer true.
   # effects that are executed until the limit is no longer true.
}
}
</pre>
</pre>
第380行: 第426行:
Here is an example:
Here is an example:
<pre>
<pre>
set_variable = { which = count value = 20 }
set_variable = {
   which = count
   value = 20
}
 
while = {
while = {
limit = {
   limit = {
check_variable = {
check_variable = {
which = count
   which = count
value = 1
   value >= 1
}
}  
subtract_variable = {
which = count
value = 1
}
}
wealth = 5
   }
   subtract_variable = {
which = count
value = 1
   }
   wealth = 5
}
}
</pre>
</pre>
第401行: 第451行:
* make sure you don't create an endless loop... Although there is a fail-safe at 100K loops.
* make sure you don't create an endless loop... Although there is a fail-safe at 100K loops.


In patch 2.8, an additional "count" parameter was added which allows a fixed number of loops (i.e. a for loop), and does not require the use of variables.  This provides much cleaner and more concise code if a fixed number of loops is desired.
In [[patch 2.8]], an additional <code>count</code> parameter was added which allows a fixed number of loops (i.e. a for loop), and does not require the use of variables.  This provides much cleaner and more concise code if a fixed number of loops is desired.
<pre>
<pre>
while = {
while = {
count = 20
   count = 20
wealth = 5
   wealth = 5
}
}
</pre>
</pre>
第412行: 第462行:
If the two methods are combined, the loop will exit either when the limit fails, or when the count is passed, whichever happens first.
If the two methods are combined, the loop will exit either when the limit fails, or when the count is passed, whichever happens first.
<pre>
<pre>
set_variable = { which = myvar value = 25 }
set_variable = {
   which = myvar
   value = 25 
}
 
while = {
while = {
count = 20
   count = 20
limit = {
   limit = {
check_variable = {
check_variable = {
which = myvar
   which = myvar
value = 1
   value >= 1
}
}  
subtract_variable = {
which = myvar
value = 1
}
}
wealth = 5
   }
   subtract_variable = {
which = myvar
value = 1
   }
   wealth = 5
}
}
</pre>
</pre>
第545行: 第599行:
===Variables===
===Variables===


Variables are '''numeric values''' attached to character, province, title, or global scope ( global variables added with patch 2.7 ). They have a range of −2,147,483.648 to 2,147,483.647, in steps of .001 (32 bit signed fixed-point, 3 decimal places).
Variables are '''numeric values''' attached to character, province, title, global scope (added with [[patch 2.7]]) or event chains (added with [[patch 2.8]]). They have a range of −2,147,483.648 to 2,147,483.647, in steps of .001 (32 bit signed fixed-point, 3 decimal places).


Variable are persisted in saves, and though there is no command to clear variables, the increased file size is usually negligible. Variables set to 0 are not saved to file, and so will be cleared after reload.
Variable are persisted in saves, and though there is no command to clear variables, the increased file size is usually negligible. Variables set to 0 are not saved to file, and so will be cleared after reload.


Since patch 2.8, it is possible to check a variable with a trigger/condition, and to assign a variable to an effect/command.
Since [[patch 2.8]], it is possible to check a variable with a trigger/condition, and to assign a variable to an effect/command.
<pre>
<pre>
set_variable = { which = myvar value = 5 }
set_variable = { which = myvar value = 5 }
第555行: 第609行:
</pre>
</pre>


'''Important''': in versions prior to patch 2.8, scripting variables are only able to pull, not set, the other numeric variables used by the game (attributes, gold, piety, ...), i.e. it's not possible to assign the value of a variable to a trigger.
'''Important''': in versions prior to [[patch 2.8]], scripting variables are only able to pull, not set, the other numeric variables used by the game (attributes, gold, piety, ...), i.e. it's not possible to assign the value of a variable to a trigger.


Variables are always stored/retrieve in the scope where the commands/triggers are executed.
Variables are always stored/retrieve in the scope where the commands/triggers are executed.
第561行: 第615行:
<pre>
<pre>
ROOT = {
ROOT = {
  set_variable = { which = myvar value = 1 }
   set_variable = { which = myvar value = 1 }
}
}
</pre>
</pre>
Or to create a global variable, add the prefix 'global_' in front of the variable's name:
Or to create a global variable, add the prefix 'global_' in front of the variable's name:
<pre>
<pre>
  set_variable = { which = global_myvar value = 1 }
   set_variable = { which = global_myvar value = 1 }
</pre>
</pre>
and to check for it:
and to check for it:
<pre>
<pre>
ROOT = {
ROOT = {
  check_variable = { which = myvar value = 1 }
   check_variable = { which = myvar value >= 1 }
}
}
or
or
<any_scope> = {  
<any_scope> = {  
  check_variable = { which = global_myvar value = 1 }
   check_variable = { which = global_myvar value >= 1 }
}
}
</pre>
</pre>
Since the introduction of global_variables with patch 2.7, it is possible to compare two variables in separate scopes by setting the value of one of the variables to a global_variable, then comparing the global variable to any scoped variable as follows:
Since the introduction of global_variables with [[patch 2.7]], it is possible to compare two variables in separate scopes by setting the value of one of the variables to a global_variable, then comparing the global variable to any scoped variable as follows:
<pre>
<pre>
<any_scope> = {  
<any_scope> = {  
  set_variable = { which = global_myvar which = myvar_1 } # Creates a global_variable with value of myvar_1
   set_variable = { which = global_myvar which = myvar_1 } # Creates a global_variable with value of myvar_1
}
}
<any_scope> = {
<any_scope> = {
  check_variable = { which = myvar_2 which = global_myvar } # Checks that global_variable's value is >= 2nd variable
   check_variable = { which = myvar_2 which >= global_myvar } # Checks that global_variable's value is greater than or equal to 2nd variable
}
}
</pre>
</pre>
Support for event variables was added in [[patch 2.8]], which are only accessible for the duration of an event chain. These are not bound to any scope and can be convenient when dealing with scopes that do not support variables, such as bloodlines. Save event variables by starting the name with 'local_'.


Variables can be assigned the value of certain triggers using the export_to_variable command
Variables can be assigned the value of certain triggers using the export_to_variable command
<pre>
<pre>
ROOT = {
ROOT = {
  export_to_variable = { which = myvar value = stewardship }
   export_to_variable = {
which = myvar
value = stewardship
   }
}
}
</pre>
</pre>
第718行: 第776行:
<pre>
<pre>
ROOT = {
ROOT = {
  export_to_variable = { which = myvar value = stewardship who = FROM }
   export_to_variable = {
which = myvar
value = stewardship
who = FROM
   }
}
}
</pre>
</pre>
第730行: 第792行:
*<code>check_variable = { which = <variable_name> value = <value> }</code>
*<code>check_variable = { which = <variable_name> value = <value> }</code>
*<code>is_variable_equal = { which = <variable_name> value = <value> }</code>
*<code>is_variable_equal = { which = <variable_name> value = <value> }</code>
As of [[patch 2.8]], numerical operators are supported in <code>check_variable</code>, on both numerical values and other variables.


Variables can be used in localization:<ref>[[Forum:774248]]</ref>
Variables can be used in localization:<ref>[[Forum:774248]]</ref>
第790行: 第853行:
<pre>
<pre>
ROOT = { 
ROOT = { 
  # Save current scope
   # Save current scope
  save_event_target_as = target_adulterer 
   save_event_target_as = target_adulterer 
}
}
</pre>
</pre>
第797行: 第860行:
<pre>
<pre>
event_target:target_adulterer = {
event_target:target_adulterer = {
  # Saved scope is restored
   # Saved scope is restored
  add_character_modifier = {name = adulterer  years = 10}
   add_character_modifier = {name = adulterer  years = 10}
}
}
</pre>
</pre>
第850行: 第913行:
<pre>
<pre>
add_character_modifier = {
add_character_modifier = {
name = lustful_affair_timer
   name = lustful_affair_timer
duration = 2190
   duration = 2190
hidden = yes
   hidden = yes
}
}
</pre>
</pre>
第870行: 第933行:
<pre>
<pre>
add_holding_modifier = {
add_holding_modifier = {
name = nomad_population_boom
   name = nomad_population_boom
years = 10
   years = 10
stacking = yes
   stacking = yes
}
}
</pre>
</pre>
第886行: 第949行:
==Randomness==
==Randomness==


There are two commands that can be used to generate a random result: random and random_list.
There are two commands that can be used to generate a random result: <code>random</code> and <code>random_list</code>.
random_list is similar to using several random blocks, except it guarantees that only one block will execute, which is not the case with multiple random blocks.
<code>random_list</code> is similar to using several <code>random</code> blocks, except it guarantees that only one block will execute, which is not the case with multiple <code>random</code> blocks.


Since [[patch 2.3]] both commands can be weighted with some <code>modifier = { }</code> blocks, that based on conditions, can:
Since [[patch 2.3]] both commands can be weighted with some <code>mult_modifier = { }</code> blocks, that based on conditions, can:
*increase the chances if factor > 1
*increase the chances if factor > 1
*decrease the chances if factor < 1
*decrease the chances if factor < 1
*annihilate the chances if factor = 0
*annihilate the chances if factor = 0
Since [[patch 2.7]] <code>random_list</code> supports the use of <code>trigger</code>. <code>trigger</code> obsoletes the use of modifiers with factor 0 and requires no inverted logic. <code>random</code> can be wrapped inside an <code>if</code> block instead.


===Random===
===Random===
第898行: 第962行:
<pre>
<pre>
random = {
random = {
  chance = <percentage>
   chance = <percentage> (from 0-100)
  modifier = {
 
   factor = <factor_1>
   mult_modifier = {
   #Conditions for factor to apply
factor = <factor_1>
  }
#Conditions for factor to apply
  modifier = {
   }
   factor = <factor_2>
   mult_modifier = {
   #Conditions for factor to apply
factor = <factor_2>
  }
#Conditions for factor to apply
  #Commands to execute if the random check succeeded
   }
   #Commands to execute if the random check succeeded
}
}
</pre> 
</pre> 
第916行: 第981行:
<pre>
<pre>
random = {
random = {
  chance = 20
   chance = 20
  add_trait = stressed
   add_trait = stressed
}
}
</pre>
</pre>
第925行: 第990行:
<pre>
<pre>
random_list = {
random_list = {
  <weight_A> = {
   <weight_A> = {
   modifier = {
  mult_modifier = {
    factor = <factor_1>
   factor = <factor_1>
    #Conditions for factor to apply
   #Conditions for factor to apply
}
mult_modifier = {
   factor = <factor_2>
   #Conditions for factor to apply
}
#Commands for case A
    }
    }
    modifier = {
    <weight_B> = {
    factor = <factor_2>
modifier = {
    #Conditions for factor to apply
   factor = <factor_3>
   #Conditions for factor to apply
}
#Commands for case B
    }
    }
    #Commands for case A
    ...
  }
    fallback = {
  <weight_B> = {
# Fallback commands are executed if no other option has a chance above 0
    modifier = {
    factor = <factor_3>
    #Conditions for factor to apply
    }
    }
   #Commands for case B
  }
  ...
  fallback = {
   # Fallback commands are executed if no other option has a chance above 0
  }
}
}
</pre>
</pre>
第960行: 第1,025行:
<pre>
<pre>
random_list = {
random_list = {
  10 = {
   10 = {
   give_nickname = nick_the_wise
give_nickname = nick_the_wise
  }
   }
  10 = {
   10 = {
   give_nickname = nick_the_able
give_nickname = nick_the_able
  }
   }
  10 = {
   10 = {
   modifier = {
trigger = {
    factor = 0
   NOT = { religion = buddhist }
    religion = buddhist
}
give_nickname = nick_priest_hater
    }
    }
   give_nickname = nick_priest_hater
  }
}
}
</pre>
</pre>
Triggers will work within random_list, and will function as if the modifier was 0, except that if the trigger is not met then the option will be ignored entirely.


In the example below, if the player has Holy Fury installed there will be a 50/50 chance of either option being chosen. If the player does not have Holy Fury installed then only the first option will be chosen.
In the example below, if the player has Holy Fury installed there will be a 50/50 chance of either option being chosen. If the player does not have Holy Fury installed then only the first option will be chosen.
第982行: 第1,044行:
<pre>
<pre>
random_list = {
random_list = {
50 = { 
   50 = { 
set_graphical_culture = horse
set_graphical_culture = horse
culture = horse 
culture = horse 
   }
   50 = {
trigger = {
   has_dlc = "Holy Fury"
}
}
50 = {
set_graphical_culture = hedgehog_culture
trigger = {
culture = hedgehog_culture
has_dlc = "Holy Fury"
   }
}
set_graphical_culture = hedgehog_culture
culture = hedgehog_culture
}
}
}
</pre>
</pre>
第1,049行: 第1,111行:
<pre>
<pre>
any_realm_character = {
any_realm_character = {
  ROOT = { is_liege_of = THIS }
  ROOT = { is_liege_of = THIS }
}
}
</pre>
</pre>
第1,058行: 第1,120行:
<pre>
<pre>
province_event = {
province_event = {
...
   ...
trigger = {
   trigger = {
#1
#1
owner = {
owner = {
#2
   #2
top_liege = {
   top_liege = {
#3
#3
culture = PREV
culture = PREV
}
   }
#4
   #4
NOT = {
   NOT = {
#5
#5
culture = ROOT
culture = ROOT
}
   }
}
}
}
...
   }
   ...
}
}
</pre>
</pre>


In order of evaluation, at position:
In order of evaluation, at position:
* #1: <code>trigger</code> is not a scope, but the beginning of script block (aka function). ROOT is referencing the province for which the event is being evaluated. THIS points to ROOT. The stacks looks like that:
* #1: <code>trigger</code> is not a scope, but the beginning of script block (aka function). <code>ROOT</code> is referencing the province for which the event is being evaluated. <code>THIS</code> points to <code>ROOT</code>. The stacks looks like that:
<pre>
<pre>
ROOT <- THIS
ROOT <- THIS
</pre>
</pre>
* #2: <code>owner</code> is a scope, so THIS now points to the owner of the province. PREV points to ROOT (previous scope). The stacks looks like that:
* #2: <code>owner</code> is a scope, so <code>THIS</code> now points to the owner of the province. <code>PREV</code> points to <code>ROOT</code> (previous scope). The stacks looks like that:
<pre>
<pre>
owner <- THIS
owner <- THIS
ROOT <- PREV
ROOT <- PREV
</pre>
</pre>
* #3 <code>top_liege</code> is a scope, so THIS points to the top liege of the owner of the province. PREV points to the owner, PREVPREV points to ROOT. So here it is comparing the culture of the top liege with the culture of the owner. The stacks looks like:
* #3 <code>top_liege</code> is a scope, so <code>THIS</code> points to the top liege of the owner of the province. <code>PREV</code> points to the owner, <code>PREVPREV</code> points to <code>ROOT</code>. So here it is comparing the culture of the top liege with the culture of the owner. The stacks looks like:
<pre>
<pre>
top_liege <- THIS
top_liege <- THIS
第1,094行: 第1,156行:
ROOT <- PREVPREV
ROOT <- PREVPREV
</pre>
</pre>
* #4 Previous block is finished, so THIS is now pointing again to the owner of the province, PREV points to ROOT. 
* #4 Previous block is finished, so THIS is now pointing again to the owner of the province, <code>PREV</code> points to <code>ROOT</code>
* #5 <code>NOT</code> is an [[operator]], not a scope. Scope chain remains unchanged. So here it is comparing the culture of the owner with the culture of the province.
* #5 <code>NOT</code> is an [[operator]], not a scope. Scope chain remains unchanged. So here it is comparing the culture of the owner with the culture of the province.


第1,111行: 第1,173行:
|<pre>
|<pre>
current_heir = {
current_heir = {
  character = PREV
   character = PREV
}
}
</pre>
</pre>
|<pre>
|<pre>
primary_title = {
primary_title = {
  title = k_england
   title = k_england
}
}
</pre>
</pre>
第1,122行: 第1,184行:
<pre>
<pre>
location  = {
location  = {
  province = 272 # Akershus
   province = 272 # Akershus
}
}
</pre>
</pre>
第1,132行: 第1,194行:
<pre>
<pre>
trigger = {
trigger = {
  #Will evaluate to true if scope is not empty
   #Will evaluate to true if scope is not empty
  scope = {
   scope = {
   always = yes
always = yes
  }
   }
}
}
</pre>
</pre>
第1,144行: 第1,206行:


When a scope is used in a condition block, <code>count = N</code> condition will cause the scope to evaluate to true, if at least N elements are matched in the scope.
When a scope is used in a condition block, <code>count = N</code> condition will cause the scope to evaluate to true, if at least N elements are matched in the scope.
As of [[patch 2.3]] any scopes starting with any_ can be used with count.
As of [[patch 2.3]] any scopes starting with <code>any_</code> can be used with count.
As of [[patch 2.8]] count supports numerical operators in most <code>any_</code> scopes.
<pre>
<pre>
trigger = {
trigger = {
  #Will evaluate to true if at least N elements in the scope match the conditions
   #Will evaluate to true if at least N elements in the scope match the conditions
  scope = {
   scope = {
   #Conditions
#Conditions
   count = N
count >= N
   }
   }
}
}
第1,158行: 第1,221行:
<pre>
<pre>
any_courtier = {
any_courtier = {
  count = 7
   count >= 7
  trait = dwarf
   trait = dwarf
}
}
</pre>
</pre>
第1,165行: 第1,228行:
===Limit===
===Limit===


Limit can be used to further reduce what is matched by a multi-variant scope (any_*, random_*), when a scope is used in command block:
Limit can be used to further reduce what is matched by a multi-variant scope (<code>any_</code>, <code>random_</code>), when a scope is used in command block:
<pre>
<pre>
scope = {
scope = {
  limit = {
   limit = {
   #Conditions on elements of the scope
#Conditions on elements of the scope
  }
   }
  #Commands to execute for each element of the scope that has been matched
   #Commands to execute for each element of the scope that has been matched
}
}
</pre>
</pre>


For instance to scope only other rulers of same religion in the realm and fire some event to them:
For instance to scope only other rulers of same religion in the realm and fire some event to them:
<pre> 
<pre>
any_realm_lord = {
any_realm_lord = {
  limit = {
   limit = {
   religion = ROOT
religion = ROOT
  }
   }
  character_event = { id = xxx }
   character_event = { id = xxx }
}
}
</pre>
</pre>
第1,188行: 第1,251行:


====Preferred Limit====
====Preferred Limit====
In Patch 3.0, the new preferred limit syntax was added to the game, which can be used in any random_* scope to help specify multiple layers of specificity when choosing a random target.
In Patch 3.0, the new preferred limit syntax was added to the game, which can be used in any <code>random_</code> scope to help specify multiple layers of specificity when choosing a random target.


For example, say you want to find the strongest vassal in your realm who is ambitious. You cannot directly sort a list of vassals by strength and pick the top one who is ambitious, without an overly complex system of pinging events and variables, but you can have a good approximation using preferred limits.
For example, say you want to find the strongest vassal in your realm who is ambitious. You cannot directly sort a list of vassals by strength and pick the top one who is ambitious, without an overly complex system of pinging events and variables, but you can have a good approximation using preferred limits.
<pre>
<pre>
random_vassal = {
random_vassal = {
  limit = {
   limit = {
   trait = ambitious
trait = ambitious
  }
   }
  preferred_limit = {
   preferred_limit = {
   is_powerful_vassal = yes
is_powerful_vassal = yes
  }
   }
  preferred_limit = {
   preferred_limit = {
   OR = { tier = king tier = duke}
OR = { 
  }
   tier = KING
  preferred_limit = {
   tier = DUKE
   tier = count
}
  }
   }
  #Commands
   preferred_limit = {
tier = COUNT
   }
   #Commands
}
}
</pre>
</pre>


What this does is it looks for a random vassal, who follows the regular limit (is ambitious), but first looks amongst those who meet the first preferred limit (is powerful vassal). If it can find an ambitious powerful vassal, it will execute the commands and call it a day - otherwise, it will look among the king-tier and duke-tier vassals for one who is ambitious. And if there is no ambitious king or duke vassal, it will look among the count-tier vassals. If none of the preferred scopes are met, then it will disregard them all and look for any vassal who is ambitious (who will be a baron in this case, by process of elimination).
This looks for a random vassal, who follows the regular limit (is ambitious), but first looks amongst those who meet the first preferred limit (is powerful vassal). If it can find an ambitious powerful vassal, it will execute the commands and call it a day - otherwise, it will look among the king-tier and duke-tier vassals for one who is ambitious. And if there is no ambitious king or duke vassal, it will look among the count-tier vassals. If none of the preferred scopes are met, then it will disregard them all and look for any vassal who is ambitious (who will be a baron in this case, by process of elimination).
 
===Score Value===
 
Patch 3.0 added a score selection to <code>any_</code> effect scopes, used in conjunction with <code>count</code>. Rather than executing effects on random matching elements, the highest scoring elements are selected instead. This can be combined with <code>limit</code>, but not <code>preferred_limit</code>, which is exclusive to <code>random_</code> scopes.
 
<pre>
any_society_member = {
   score_value = {
value = 1
 
additive_modifier = {
   society_rank == 4
   value = 1000
}
additive_modifier = {
   society_rank == 3
   value = 100
}
   }
   count = 3
   add_trait = maimed
}
</pre>


==Tooltips==
==Tooltips==
第1,221行: 第1,310行:
=== Hidden tooltips ===
=== Hidden tooltips ===


Hides the entire evaluation of the effect from the player, typically the firing of an event.
Hides the entire evaluation of an effect or trigger from the player, typically the firing of an event, or conditions that only AI has to satisfy.
<pre>
<pre>
hidden_tooltip = { province_event = { id = CM.1106 } }
hidden_tooltip = { province_event = { id = CM.1106 } }
第1,228行: 第1,317行:
Hidden tooltips are very useful for decisions and events if the actual results are to be hidden from the player for dramatic effect, or if the automatically generated tooltips produce unnecessarily technical details (such as the setting and modification of variables).
Hidden tooltips are very useful for decisions and events if the actual results are to be hidden from the player for dramatic effect, or if the automatically generated tooltips produce unnecessarily technical details (such as the setting and modification of variables).


Hidden tooltips can also be used in triggers, but depending on the specific context it may be very confusing for the player if a decision satisfies all of its visible conditions, but fails a hidden one; a custom_tooltip is preferable in this case.
Hidden tooltips can also be used in triggers, but depending on the specific context it may be very confusing for the player if a decision satisfies all of its visible conditions, but fails a hidden one; a <code>custom_tooltip</code> is preferable in this case.
 
In [[Patch 2.6.X#Patch 2.6.2{{!}}2.6.2]] <code>hidden_effect</code> and <code>hidden_trigger</code> aliases were added, which can be used to reduce ambiguity.


=== Custom tooltips ===
=== Custom tooltips ===
第1,234行: 第1,325行:
Custom tooltips simplify some complex or hard to read triggers or effects, by replacing the tooltip content with the contents of a localisation key.
Custom tooltips simplify some complex or hard to read triggers or effects, by replacing the tooltip content with the contents of a localisation key.
<pre>
<pre>
custom_tooltip = { text = runestone_carved }
custom_tooltip = {
   text = pagan_subjugation_tip
 
   attacker = {
subjugate_or_take_under_title = { # If the target only has territory within the kingdom, he is simply vassalized
   title = PREV
   enemy = defender
}
   }
}
</pre>
 
<pre>
custom_tooltip = {
   text = UNOCCUPIED_DEMESNE_TITLE
   any_demesne_title = {
NOT = { higher_tier_than = count }
is_occupied = no
   }
}
</pre>
</pre>


These allow very complex trigger chains to be reduced to a simple message.
These allow very complex trigger chains to be reduced to a simple message.</br>
<code>custom_tooltip</code> can also be used to display a message without hiding effects or triggers:
 
<pre>
custom_tooltip = {
   text = disables_centralization_1
}
</pre>


=== Conditional tooltips ===
=== Conditional tooltips ===


Added in version 2.8, these display the tooltip conditions only if the included trigger evaluates successfully.
Added in version [[patch 2.8]], these display the tooltip conditions only if the included trigger evaluates successfully.
<pre>
<pre>
conditional_tooltip = {
conditional_tooltip = {
trigger = {
   trigger = {
liege = {
liege = {
independent = no
   independent = no
}
}
}
   }
   liege = {
liege = {
liege = {
liege = {
   will_liege_enforce_peace = no
will_liege_enforce_peace = no
   has_liege_enforced_peace = no
has_liege_enforced_peace = no
}
}
}
   }
}
}
</pre>
</pre>
第1,266行: 第1,384行:
Triggers and effects may also be filtered for better readability.
Triggers and effects may also be filtered for better readability.


==== show_scope_change (2.8.0+) ====
==== show_scope_change ([[patch 2.8]]) ====


  trigger = {
  trigger = {
location = {
   location = {
'''show_scope_change = no'''
'''show_scope_change = no'''
  
  
owner = {
owner = {
'''show_scope_change = no'''
   '''show_scope_change = no'''
  
  
opinion = { who = ROOT value = 50 }
   opinion = { who = ROOT value = 50 }
}
  }
  }
   }
  }
  }


The above example scopes to the owner of the character's current location to check their opinion of the root character.  By default, this would produce an ugly chain of your current location, followed by a blank line (the "owner" scope is not localised in the game), followed by the correct and intended line.  With the '''show_scope_change = no''' limiter, only the single intended line would be shown.
The above example scopes to the owner of the character's current location to check their opinion of the root character.  By default, this would produce an ugly chain of your current location, followed by a blank line (the "owner" scope is not localised in the game), followed by the correct and intended line.  With the '''show_scope_change = no''' limiter, only the single intended line would be shown.


==== show_only_failed_conditions (2.8.1+) ====
==== show_only_failed_conditions ([[Patch 2.8.X#Patch 2.8.1{{!}}2.8.1]]) ====


  trigger = {
  trigger = {
'''show_only_failed_conditions = yes'''
   '''show_only_failed_conditions = yes'''
 
 
always = yes
   always = yes
character = ROOT
   character = ROOT
is_vassal_of = FROM
   is_vassal_of = FROM
  }
  }


第1,296行: 第1,414行:
This feature is used heavily in the Jade Dragon decisions to show only why China will reject a given proposal, without showing all of the obvious reasons why they would accept it.
This feature is used heavily in the Jade Dragon decisions to show only why China will reject a given proposal, without showing all of the obvious reasons why they would accept it.


==== generate_tooltip (2.8.0+) ====
==== generate_tooltip ([[patch 2.8]]) ====


  hidden_tooltip = {
  hidden_tooltip = {
'''generate_tooltip = no'''
   '''generate_tooltip = no'''
any_realm_character = {
   any_realm_character = {
limit = {
limit = {
controls_religion = no
   controls_religion = no
religion_group = ROOT
   religion_group = ROOT
is_landed = yes
   is_landed = yes
NOT = {
   NOT = { character = ROOT }
character = ROOT
}
}
opinion = {
who = ROOT
modifier = opinion_declared_unjust_conquest
years = 10
}
  }
  }
opinion = {
   who = ROOT
   modifier = opinion_declared_unjust_conquest
   years = 10
}
   }
  }
  }


第1,333行: 第1,449行:
<pre>
<pre>
option = {
option = {
set_character_flag = flag_do_something # Not executed to calculate tooltip
   set_character_flag = flag_do_something # Not executed to calculate tooltip
if = {
   if = {
limit  = { has_character_flag = flag_do_something } # Executed to calculate tooltip: will be false
limit  = { has_character_flag = flag_do_something } # Executed to calculate tooltip: will be false
# Do it
# Do it
}
   }
}
}
</pre>
</pre>
第1,356行: 第1,472行:


Thereafter, the scripted block can be called in your code like any other trigger or command, using the format:
Thereafter, the scripted block can be called in your code like any other trigger or command, using the format:
'''scripted_block_name = yes'''
'''scripted_block_name = yes'''


It is always best practice to include a proper naming convention for your scripted blocks to avoid overlap with other mods, and never to overwrite the original vanilla files, as there is no reason to do so: simply define a new file for your own scripted blocks.  Also use a prefix or suffix that identifies that your trigger/command is a scripted trigger/effect, and not built-in:
It is always best practice to include a proper naming convention for your scripted blocks to avoid overlap with other mods, and never to overwrite the original vanilla files, as there is no reason to do so: simply define a new file for your own scripted blocks.  Also use a prefix or suffix that identifies that your trigger/command is a scripted trigger/effect, and not built-in:
'''mymod_helloworld_effect = yes''' ''#Paradox naming convention''
'''mymod_helloworld_effect = yes''' ''#Paradox naming convention''
'''fn_mymod_helloworld = yes''' ''#Other naming convention''
'''fn_mymod_helloworld = yes''' ''#Other naming convention''


It is important to use unique names. For instance, if you have a trait that has the same name as an effect block, it will not be parsed correctly if you refer to that trait within the block.
It is important to use unique names. For instance, if you have a trait that has the same name as an effect block, it will not be parsed correctly if you refer to that trait within the block.
第1,371行: 第1,487行:


The following scripted effect removes all "vanilla" education traits from the character:
The following scripted effect removes all "vanilla" education traits from the character:
#SCOPE: THIS = Character
#SCOPE: THIS = Character
fn_remove_education_traits = {
<pre>
#DIPLOMACY
fn_remove_education_traits = {
remove_trait = naive_appeaser
   #DIPLOMACY
remove_trait = underhanded_rogue
   remove_trait = naive_appeaser
remove_trait = charismatic_negotiator
   remove_trait = underhanded_rogue
remove_trait = grey_eminence
   remove_trait = charismatic_negotiator
#MARTIAL
   remove_trait = grey_eminence
remove_trait = misguided_warrior
   #MARTIAL
remove_trait = tough_soldier
   remove_trait = misguided_warrior
remove_trait = skilled_tactician
   remove_trait = tough_soldier
remove_trait = brilliant_strategist
   remove_trait = skilled_tactician
#STEWARDSHIP
   remove_trait = brilliant_strategist
remove_trait = indulgent_wastrel
   #STEWARDSHIP
remove_trait = thrifty_clerk
   remove_trait = indulgent_wastrel
remove_trait = fortune_builder
   remove_trait = thrifty_clerk
remove_trait = midas_touched
   remove_trait = fortune_builder
#INTRIGUE
   remove_trait = midas_touched
remove_trait = amateurish_plotter
   #INTRIGUE
remove_trait = flamboyant_schemer
   remove_trait = amateurish_plotter
remove_trait = intricate_webweaver
   remove_trait = flamboyant_schemer
remove_trait = elusive_shadow
   remove_trait = intricate_webweaver
#LEARNING
   remove_trait = elusive_shadow
remove_trait = detached_priest
   #LEARNING
remove_trait = martial_cleric
   remove_trait = detached_priest
remove_trait = scholarly_theologian
   remove_trait = martial_cleric
remove_trait = mastermind_theologian
   remove_trait = scholarly_theologian
}
   remove_trait = mastermind_theologian
}
</pre>


Then it can be called in any effect block as if it were a command included in the base game:
Then it can be called in any effect block as if it were a command included in the base game:
character_event = {
<pre>
id = ''mymod''.1
character_event = {
desc = "I can't help but feel like I'm forgetting something..."
   id = ''mymod''.1
   desc = "I can't help but feel like I'm forgetting something..."
  
  
trigger = {
   trigger = {
trait = imbecile
trait = imbecile
}
   }
mean_time_to_happen = {
   mean_time_to_happen = {
years = 800
years = 800
}
   }
 
 
option = {
   option = {
name = "Damn this amnesia!"
name = "Damn this amnesia!"
''#Remove all education traits from THIS (character)''
''#Remove all education traits from THIS (character)''
'''fn_remove_education_traits = yes'''
'''fn_remove_education_traits = yes'''
}
   }
}
}
</pre>


It is '''strongly recommended''' to document the scopes which a scripted block requires, in a comment at the beginning of that scripted block.
It is '''strongly recommended''' to document the scopes which a scripted block requires, in a comment at the beginning of that scripted block.
第1,436行: 第1,556行:


Modders skilled with other programming languages need to be aware of the following quirks of scripted blocks:
Modders skilled with other programming languages need to be aware of the following quirks of scripted blocks:
* There are no parameters or other variables that can be passed to a function; the argument is always the singular boolean "yes".  This can be worked around by setting variables prior to calling the function, and then checking those variables inside the function, bearing in mind the limited support for variables in Crusader Kings 2.
* There are no parameters or other variables that can be passed to a function; the argument is always the singular boolean "yes".  This can be worked around by setting variables prior to calling the function, and then checking those variables inside the function, bearing in mind the limited support for variables in Crusader Kings II.
* The behaviour of the function depends on the context from which the function is called.  The function does not create a new context unless it explicitly includes a new scope.  Always ensure that the function is called from the correct context.
* The behaviour of the function depends on the context from which the function is called.  The function does not create a new context unless it explicitly includes a new scope.  Always ensure that the function is called from the correct context.
* Functions cannot return a value—they behave as "void" functions.  To "return" a value, set a flag (e.g., <code>set_character_flag</code>) or set a variable (i.e., <code>set_variable</code>) inside the function.  Remember that the function takes in the same context as the place it is called, so all variables assigned will be saved to the calling scope and accessible outside the function.
* Functions cannot return a value—they behave as "void" functions.  To "return" a value, set a flag (e.g., <code>set_character_flag</code>) or set a variable (i.e., <code>set_variable</code>) inside the function.  Remember that the function takes in the same context as the place it is called, so all variables assigned will be saved to the calling scope and accessible outside the function.

2020年5月3日 (日) 10:04的版本

Scripting allows to modify dynamically the world and govern A.I. character behaviors. It is opposed to static history modding, though some scripts may exceptionally be triggered from history files.

Scripting is event-based, with Clausewitz engine being optimized to filter and process massive amounts of events on characters and provinces.

The structure is based on definitions of entities, that have:

  • properties (or "flags")
  • pre-defined functions (or "blocks") attached and called in a specific order by the engine, and containing scopes, conditions and commands.

A relatively unique characteristic of the engine is the ability to dynamically display a textual representation of those "blocks" in tooltips (each scope, condition and command being localized), for the player to preview the effects of an action before taking it or know why a decision isn't available, and without any extra work required by modders.

Basics

The scripts are plain .txt files, and use a JSON-like syntax:

  • = separates key from a value
  • { } is a structured/complex value
  • # is the start of a comment
# Definition of entity_1
entity_1 = {
    property_1 = value1
    property_2 = value2

    block_1 = { # A condition block
	scope = {
	    condition = yes
	}
    }
    block_2 = { # A command block
	scope = {
	    command = yes
	}
    }
}

# Another entity
entity_2 = {
}

The grammar of allowed properties and blocks depend on the relative path of the file toward the base game directory:

  • common/religions will contain .txt files where entities are religion groups and religion, with properties specific to religions (see religion modding).
  • events will contain .txt files where entities are events, with properties and blocks specific to events (see event modding).

Value types

Most frequent value types are:

Type Description
bool Boolean value yes/no
int Integer value
float Floating point value
date YYYY.MM.dd format, starting from 1.1.1.

Note: negative dates are not properly handled by the engine.

string String literal values, e.g. "Orsini". Quotes are only strictly needed when the value contains spaces, though recommended for readability.
color Color values either as RGB list { 255 255 255 } or hexadecimal 0xff0000.
clause A complex structure where parameters depend on the condition.
<condition> = {
 <parameter1> = <value1>
 <parameter2> = <value2>
}
ID An ID referencing an entity defined in another file (culture, religion, culturegfx, localization key,...). Does not contain spaces (_ is typically used as word separator). It may be quoted, though it leads to confusion with string literals.

Boolean operators

Operators used to combine several conditions:

Operator Description Example
AND Returns true if all enclosed conditions return true. This is the default operator after a scope change.
AND = {
    age >= 15
    age < 35
}
OR Returns true if at least one enclosed condition returns true
OR = {
    culture = norman 
    culture = saxon
}
NOT Returns true if the enclosed condition is false.

It behaves the same as NOR, but for clarity NOR is preferred in case of more than 1 condition.

NOT = { trait = on_hajj }
NOR Returns true if none of the enclosed conditions return true.
NOR = {
    trait = seducer
    trait = seductress
}

which is equivalent to:

NOT = { 
    OR = { 
	trait = seducer
	trait = seductress
    }
}

or alternately:

AND = {
    NOT = { trait = seducer }
    NOT = { trait = seductress }
}
NAND Returns true if at least one enclosed condition return false.
NAND = {
    culture = norman
    religion = catholic
}

which is equivalent to:

NOT = { 
    AND = { 
	culture = norman
	religion = catholic
    }
}

or alternately:

OR = { 
    NOT = { culture = norman }
    NOT = { religion = catholic }
}
calc_true_if Returns true if at least amount conditions return true.

amount supports numerical operators as of patch 2.8.

calc_true_if = {
    amount >= 3
    culture = swedish
    religion = catholic
    is_female = no
    is_adult = yes
    age >= 50
}

Numeric operators

With patch 2.8 comparison operators have been generalized to all numeric triggers.

Operator Comparison Example Pre-2.8 Notes
= Greater or equal ai_greed = 5 ai_greed = 5 Backward-compatible greater equivalence (mandatory in version 2.7.2 or earlier). Contrary to expectation, this is not an exact comparison. ai_greed = 5 is read as "ai_greed is at least 5", not "ai_greed is equal to 5". When inside a NOT, it is read as "ai_greed is lesser than 5".
< Less than ai_greed < 5 NOT = { ai_greed = 5 } Lesser unequivalence. True if value is less than numeral and not equal to numeral.
> Greater than ai_greed > 5 ai_greed = 6 Greater unequivalence. True if value is greater than numeral, but not equal to numeral. Pre-2.8 equivalent works only for integers (otherwise, will need to check against the target number plus a very small floating point, such as scaled_wealth = 5.00001).
<= Lesser or equal ai_greed <= 5 NOT = { ai_greed = 6 } Lesser equivalence. True if variable is less or equal to numeral. Pre-2.8 equivalent works only for integers (otherwise, will need to check against the target number plus a very small floating point, such as NOT = { scaled_wealth = 5.00001 }).
>= Greater or equal ai_greed >= 5 ai_greed = 5 Greater equivalence. True if value is greater than or equal to numeral. Identical, but lexically superior, to =.
== Equal ai_greed == 5 ai_greed = 5

NOT = { ai_greed = 6 }

Exact comparison. True if and only if value equals numeral, and no other value. Pre-2.8 equivalent works only for integers (otherwise, will need to check against the target number plus a very small floating point, such as wealth = 5 NOT = { wealth = 5.00001 }).

Warning: the above feature applies neither to the tier nor among_most_powerful_vassals triggers. This is for backward compatibility reasons, since existing code might assume that "tier = duke" means equal and not greater-than-or-equal comparison. This would break multiple mods if they required a change to reflect the new operators.

Control flow statements

If

if statements allow to execute commands only if certain conditions are met. The structure is:

if = {
    limit = {
	#Conditions
    }
    #Commands to execute if the conditions match
}

Else_if/Else

In version 2.8, else_if and else statements were added.

if = {
    limit = {
	#Conditions
    }
    #IF: Commands to execute if the conditions match
}
else_if = {
    limit = {
	#Other conditions
    }
    #ELSE IF: Commands to execute if the previous <code>if</code> did not match, and these other conditions are met
}
else_if = {
    limit = {
	#Additional conditions
    }
    #ELSE IF (2): Commands to execute if the previous <code>if</code> and <code>else_if</code> did not match, and these additional conditions are met
}
else = {
    #ELSE: Commands to execute if none of the above limits passed
}

In versions prior to 2.8, if/else_if/else can still be simulated by repeating the conditions in an if with a NOT operator:

if = {
    limit = {
	#Conditions
    }
    #IF: Commands to execute if conditions are met
}
if = {
    limit = {
	NOT = { 
	    #Conditions
	}
        #Other conditions
    }
    #ELSE IF: Commands to execute if the previous conditions are not met, but these other conditions are met
}
if = {
    limit = {
	NOT = { 
	    #Conditions
	    #Other conditions
	}
	#Additional conditions
    }
    #ELSE IF (2): Commands to execute if neither the previous conditions nor other conditions are met, but these additional conditions are met
}
if = {
    limit = {
	NOT = { 
	    #Conditions
	    #Other conditions
	    #Additional conditions
	}
    }
    #ELSE: Commands to execute if none of the above conditions are met
}

Trigger If

Patch 3.0 added support for control flow statements in trigger clauses, expanding on condition_tooltip. trigger_if is an alias for conditional_tooltip, which can be chained with trigger_else_if and trigger_else. Additionally, limit can be used instead of trigger, matching regular syntax for if. These are very useful in indicating control flow and intent in lengthy, complex triggers. Keep in mind that long chains of trigger_else_if that require one of the limit clauses to be true have to end with trigger_else, since the entire chain is ignored and is thus always true if none of the limit clauses are true.

Consider the following:

OR = {
    AND = {
	is_female = yes
	NOR = {
	    religion_group
	    has_religion_feature - religion_patriarchal
	}
    }
    AND = {
	is_female = no
	NOT = { has_religion_feature = religion_matriarchal
    }
}

This can be written much clearer in the following way:

trigger_if = {
    limit = { is_female = yes }
    NOR = {
	religion_group = muslim
	has_religion_feature = religion_patriarchal
    }
}
trigger_else = {
    NOT = { has_religion_feature = religion_matriarchal }
}

Switch

trigger_switch was introduced in patch 2.4 and is a shortcut to multiple if/limit blocks, in case the ifs are on different values of the same condition, specified via on_trigger.

It works with all triggers that have a non-complex right-hand-side argument and will try to do a normal evaluation during the on_triggers from the scope that the trigger_switch clause is residing in. If more than one value in on_triggers evaluate as true, it will still only execute the first in the list that evaluates to true.[1] If a fallback value is present and no other value evaluated as true, then the fallback option will be executed.

Switch doesn't work with scripted triggers, as they are macros and thus are complex right-hand-side arguments.[2]

As of patch 2.5, it was not working with the region trigger, due to a specific issue that will get fixed.[3]

Here are some examples:

trigger_switch = {
    on_trigger = religion_group
    christian 		= { FROM = { add_trait = sympathy_christendom } }
    muslim 		= { FROM = { add_trait = sympathy_islam } }
    pagan_group 	= { FROM = { add_trait = sympathy_pagans } }
    zoroastrian_group 	= { FROM = { add_trait = sympathy_zoroastrianism } }
    jewish_group 	= { FROM = { add_trait = sympathy_judaism } }
    indian_group 	= { FROM = { add_trait = sympathy_indian } }
}
any_demesne_title = {
    trigger_switch = {
	on_trigger = title
	e_hre = { holder_scope = { ... } }
	e_byzantium = { holder_scope = { ... } }
	k_france = { holder_scope = { ... } }
    }
}
any_realm_province = {
    trigger_switch = {
	on_trigger = province_id
	31 = { owner = { ... } }
	32 = { owner = { ... } }
    }
}
# Gives 100 wealth to christians, 100 piety to muslims and 100 prestige to a character belonging to any other religion group
trigger_switch = {
    on_trigger = religion_group
    christian 	= { wealth = 100 }
    muslim 	= { piety = 100 }
    fallback	= { prestige = 100 }
}

Break

break was introduced with patch 2.3 and acts as a return statement, causing everything after it (in the main block: option, immediate, ...) to be ignored.[4] It avoids the need to repeat the conditions with NOT operator in another if/limit block, when commands are exclusive. The structure is:

if = {
    limit = {
	#Conditions
    }
    #Commands to execute if the conditions match
    break = yes
}
#Commands to execute if the conditions do not match

While

while effect was added in patch 2.6[5], allowing for the implementation of while loops.

The syntax is:

while = {
    limit = { 
	# conditions
    }
    # effects that are executed until the limit is no longer true.
}

Here is an example:

set_variable = {
    which = count
    value = 20
}

while = {
    limit = {
	check_variable = {
	    which = count
	    value >= 1
	}
    }
    subtract_variable = {
	which = count
	value = 1
    }
    wealth = 5
}

This will add 100 wealth to THIS, the current scoped character, as it will loop 20 times.

Notes:

  • the while effect does not produce any tooltip due to the problems that would cause.
  • make sure you don't create an endless loop... Although there is a fail-safe at 100K loops.

In patch 2.8, an additional count parameter was added which allows a fixed number of loops (i.e. a for loop), and does not require the use of variables. This provides much cleaner and more concise code if a fixed number of loops is desired.

while = {
    count = 20
    wealth = 5
}

The above effect block will add 100 wealth to THIS, just like the previous example.

If the two methods are combined, the loop will exit either when the limit fails, or when the count is passed, whichever happens first.

set_variable = {
    which = myvar
    value = 25 
}

while = {
    count = 20
    limit = {
	check_variable = {
	    which = myvar
	    value >= 1
	}
    }
    subtract_variable = {
	which = myvar
	value = 1
    }
    wealth = 5
}

Even though the above while loop "should" execute 25 times because of myvar, it will end after 20 loops because of the count. If myvar was set to 15 instead, it would execute only 15 times, and not 20 times, because the variable check would be false.

Storing information

Here is a summary of the possible ways to store information, depending on the type of scope that information is attached to:

Type of storage /
Supported scopes
Variable (numeric) Flag (boolean + date) Modifier (boolean + date) Event target (scope reference) Earmark (boolean)
Global scope set_variable
with 'global_' prefix in front of variable name
set_global_flag No save_global_event_target_as No
Event chain set_variable
with 'local_' prefix in front of variable name
No No save_event_target_as No
Character scope set_variable set_character_flag add_character_modifier save_persistent_event_target No
Province scope set_variable set_province_flag add_province_modifier save_persistent_event_target No
Title scope set_variable set_title_flag No save_persistent_event_target No
Holding scope No No add_holding_modifier No No
Dynasty No set_dynasty_flag add_dynasty_modifier No No
Bloodline scope No set_bloodline_flag or flags = {} in bloodline types. No No No
Unit scope No No No No When using spawn_unit
Artifact scope set_variable set_artifact_flag
or flags = {} in artifact definitions.
No save_persistent_event_target No
Society scope set_variable set_flag add_society_modifier save_persistent_event_target No
Offmap power scope set_variable set_offmap_flag No save_persistent_event_target No
Religion scope set_variable set_flag religion_authority save_event_target_as No
Culture scope set_variable set_flag No save_event_target_as No

Variables

Variables are numeric values attached to character, province, title, global scope (added with patch 2.7) or event chains (added with patch 2.8). They have a range of −2,147,483.648 to 2,147,483.647, in steps of .001 (32 bit signed fixed-point, 3 decimal places).

Variable are persisted in saves, and though there is no command to clear variables, the increased file size is usually negligible. Variables set to 0 are not saved to file, and so will be cleared after reload.

Since patch 2.8, it is possible to check a variable with a trigger/condition, and to assign a variable to an effect/command.

set_variable = { which = myvar value = 5 }
wealth = myvar #gives 5 wealth

Important: in versions prior to patch 2.8, scripting variables are only able to pull, not set, the other numeric variables used by the game (attributes, gold, piety, ...), i.e. it's not possible to assign the value of a variable to a trigger.

Variables are always stored/retrieve in the scope where the commands/triggers are executed. So for instance to create a variable myvar with value 1 in ROOT scope:

ROOT = {
    set_variable = { which = myvar value = 1 }
}

Or to create a global variable, add the prefix 'global_' in front of the variable's name:

    set_variable = { which = global_myvar value = 1 }

and to check for it:

ROOT = {
    check_variable = { which = myvar value >= 1 }
}
or
<any_scope> = {  
    check_variable = { which = global_myvar value >= 1 }
}

Since the introduction of global_variables with patch 2.7, it is possible to compare two variables in separate scopes by setting the value of one of the variables to a global_variable, then comparing the global variable to any scoped variable as follows:

<any_scope> = {  
    set_variable = { which = global_myvar which = myvar_1 } # Creates a global_variable with value of myvar_1
}
<any_scope> = {
    check_variable = { which = myvar_2 which >= global_myvar } # Checks that global_variable's value is greater than or equal to 2nd variable
}

Support for event variables was added in patch 2.8, which are only accessible for the duration of an event chain. These are not bound to any scope and can be convenient when dealing with scopes that do not support variables, such as bloodlines. Save event variables by starting the name with 'local_'.

Variables can be assigned the value of certain triggers using the export_to_variable command

ROOT = {
    export_to_variable = {
	which = myvar
	value = stewardship
    }
}

Variables which can be exported[6][7]:

  • martial
  • diplomacy
  • intrigue
  • stewardship
  • learning
  • base_health
  • health
  • demesne_efficiency
  • decadence
  • dynasty_realm_power
  • fertility
  • infamy
  • mercenary_siphon_factor
  • monthly_income
  • plot_power
  • population_factor
  • relative_power_to_liege
  • religion_authority
  • revolt_risk
  • scaled_wealth
  • treasury/wealth
  • yearly_income
  • age
  • day_of_birth
  • month_of_birth
  • year_of_birth
  • ai_ambition
  • ai_greed
  • ai_honor
  • ai_rationality
  • ai_zeal
  • among_most_powerful_vassals
  • combat_rating
  • demesne_garrison_size
  • demesne_size
  • health_traits
  • imprisoned_days
  • lifestyle_traits
  • max_manpower
  • num_fitting_characters_for_title
  • num_of_baron_titles
  • num_of_buildings
  • num_of_children
  • num_of_claims
  • num_of_consorts
  • num_of_count_titles
  • num_of_count_titles_in_realm
  • num_of_demesne_castles
  • num_of_demesne_cities
  • num_of_demesne_empty_provinces
  • num_of_demesne_temples
  • num_of_demesne_tribes
  • num_of_duke_titles
  • num_of_dynasty_members
  • num_of_emperor_titles
  • num_of_empty_holdings
  • num_of_extra_landed_titles
  • num_of_feuds
  • num_of_friends
  • num_of_holy_sites
  • num_of_king_titles
  • num_of_lovers
  • num_of_max_settlements
  • num_of_plot_backers
  • num_of_prisoners
  • num_of_rivals
  • num_of_settlements
  • num_of_subrealm_castles
  • num_of_subrealm_cities
  • num_of_subrealm_empty_provinces
  • num_of_subrealm_temples
  • num_of_subrealm_tribes
  • num_of_spouses
  • num_of_titles
  • num_of_trade_posts
  • num_of_traits
  • num_of_unique_dynasty_vassals
  • num_of_vassals
  • over_max_demesne_size
  • over_vassal_limit
  • personality_traits
  • piety
  • population
  • population_and_manpower
  • prestige
  • realm_diplomacy
  • realm_intrigue
  • realm_learning
  • realm_martial
  • realm_stewardship
  • realm_levies
  • realm_levies_plus_allies
  • max_realm_levies
  • realm_size
  • republic_total_num_of_trade_posts
  • ruled_years
  • score
  • unused_manpower
  • retinue_points_max
  • retinue_points_used
  • retinue_points_free
  • total_tax_value
  • holding_tax_value
  • dynastic_prestige
  • society_currency
  • day
  • month
  • year
  • total_years_played
  • holding_garrison
  • holding_garrison_percent
  • holding_raisable_levy
  • holding_raisable_levy_percent
  • holding_total_levy
  • holding_total_levy_percent


Normally, the value is pulled from the current scope, but another scope can be set with the who parameter.

ROOT = {
    export_to_variable = {
	which = myvar
	value = stewardship
	who = FROM
    }
}

Commands on variables have a second parameter, which can be:

  • A literal value: (set|change|subtract|multiply|divide|modulo)_variable = { which = <variable_name> value = <value> }
  • A reference to another variable in same scope: (set|change|subtract|multiply|divide|modulo)_variable = { which = <variable_name> which = <another_variable_name> }
  • A scope (in the form of ROOT/FROM/PREV/THIS) which contains a variable with the same name: (set|change|subtract|multiply|divide|modulo)_variable = { which = <variable_name> which = <scope> }

Variables can be checked with usual superior or equal comparison, or with strict equality:

  • check_variable = { which = <variable_name> value = <value> }
  • is_variable_equal = { which = <variable_name> value = <value> }

As of patch 2.8, numerical operators are supported in check_variable, on both numerical values and other variables.

Variables can be used in localization:[8]

  • [<variable_name>.GetName], ex: [Root.PrimaryTitle.test_var.GetName]
  • [<variable_name>.GetValue], ex: [Root.PrimaryTitle.test_var.GetValue]

The variables themselves are localized via a .csv entry whose key is the variable name:

  • myvar;My Variable;;;;;;;;;;;;;x

Any variable command will treat a previously undefined variable as having a value of 0.

Flags

Flags are boolean values (flag is either present or not), which can be attached to the following scopes: character, province, title, dynasty, or global.

The command to set or clear a flag are:

  • set_<scope>_flag = <flag_name>
  • clr_<scope>_flag = <flag_name>

Flags can be checked via conditions, for either the flag presence or the duration since the flag has been set:

  • has_<scope>_flag = <flag_name>
  • had_<scope>_flag = { flag = <flag_name> days = <duration> }

Note that modifiers are somewhat similar to flags, but in addition they alter the statistics of the scope they are attached to.

Dynamic flags

The flag name may be dynamic, by appending @ and a scope (character, province or title):[9]

It only affects how the actual flag name is created, but doesn't change how the flag is checked.

FROM = { set_character_flag = is_friend_of_@ROOT }

For example, if a character has a character ID of 140, the saved character flag will be is_friend_of_140.

They can also be used in triggers: has_character_flag = is_friend_of_@FROM

Use of dynamic flags is currently only supported for set|clr_scope_flag commands and has|had_scope_flag conditions. They cannot be used in the flag line of the create_character command (flags used in instances other than those listed previously must be static).[10]

Note that scopes saved as variables in event targets can be used in dynamic flags as well.[11]

Earmarks

Event spawned units have a similar but more limited mechanism than flags, called earmarks.

An earmark is added when creating a unit via: spawn_unit = { earmark = <earmark> }

It can then be tested on the owner of the unit: has_earmarked_regiments = <earmark>

And finally to disband the unit with the given earmark: disband_event_forces = <earmark>

Event targets

Scopes (character, province or title) can be saved as variables and re-used during an event chain, using:[12]

  • command save_event_target_as = <target_name> or save_global_event_target_as = <target_name>
  • special scope event_target:<target_name>
ROOT = { 
    # Save current scope
    save_event_target_as = target_adulterer 
}
event_target:target_adulterer = {
    # Saved scope is restored
    add_character_modifier = {name = adulterer  years = 10}
}

Event targets can be used in the tooltip of the event in which they are saved (which is normally not possible, as tooltips are built before effects are executed).

Event targets are used in localization by referencing directly the variable name:

I have decided to punish my vassal [target_adulterer.GetFullName]

Persistent event targets

Persistent event targets work much like regular event targets, except they are tied to a scope (which can be a province, character, title, artifact, society, or offmap power), not to an event chain. That means that you can keep referencing it from different contexts long after the original event chain is over, such as from new events, society names, trait names (they are used in vanilla to store the name of the person responsible for the coronation in the "Crowned by X" traits). They are only cleared when purposely cleared, or when the holder scope (if it is a character) dies.

  • save_persistent_event_target = { name = name_of_target scope = event_target:my_character }. Scope is the scope to save. It'll be saved in whatever the current scope is scoped to, assuming it supports persistent event targets. Relative scoping like ROOT, liege, and similar work as well.
  • clear_persistent_event_target = name_of_target. Removes the given target.
  • persistent_event_target:name_of_target = { }. Scopes to the given target.
  • [name_of_target.SomeLocCommand]. Scopes to the given target in localisation. In the case of collision with a regular event target, the persistent target takes precedence.

Timers

It is sometime needed to measure how much time has elapsed since an event occurred as a condition of another event.

There are 2 techniques for that: flags or event modifiers.

Flags as timers

Scripting flags keep a reference to when they were set.

  • Setting a flag using set_<scope>_flag command:
set_character_flag = money_from_the_pope
  • Using had_<scope>_flag triggers to check elapsed time in days:
had_character_flag = { flag = money_from_the_pope days = 730 }
  • If needed, timer can be reset by using clr_<scope>_flag command followed by set_<scope>_flag:
clr_character_flag = money_from_the_pope # Reset timer
set_character_flag = money_from_the_pope

Event modifiers as timers

A modifier can act as a cooldown timer, and make the scripts simpler, but they are limited to characters, provinces and holdings.

  • Setting a hidden modifier that will expire after a given time via add_character_modifier, add_province_modifier or add_holding_modifier commands:
add_character_modifier = {
    name = lustful_affair_timer
    duration = 2190
    hidden = yes
}
  • Checking it via has_character_modifier, has_province_modifier or has_holding_modifier
has_character_modifier = lustful_affair_timer
  • Modifiers need to be defined in common\event_modifiers
lustful_affair_timer = { icon = 1 }

Note: hidden modifiers don't need localization as they are not displayed.

  • Modifiers can also be stacked
add_holding_modifier = {
    name = nomad_population_boom
    years = 10
    stacking = yes
}

After you can check the quantity of this modifier

has_instances_of_holding_modifier = { modifier = nomad_population_boom amount = 2 } 

You can also remove a quantity of this modifier

remove_holding_modifiers = { modifier = nomad_population_boom amount = 2 } 

Randomness

There are two commands that can be used to generate a random result: random and random_list. random_list is similar to using several random blocks, except it guarantees that only one block will execute, which is not the case with multiple random blocks.

Since patch 2.3 both commands can be weighted with some mult_modifier = { } blocks, that based on conditions, can:

  • increase the chances if factor > 1
  • decrease the chances if factor < 1
  • annihilate the chances if factor = 0

Since patch 2.7 random_list supports the use of trigger. trigger obsoletes the use of modifiers with factor 0 and requires no inverted logic. random can be wrapped inside an if block instead.

Random

random = {
    chance = <percentage> (from 0-100)

    mult_modifier = {
	factor = <factor_1>
	#Conditions for factor to apply
    }
    mult_modifier = {
	factor = <factor_2>
	#Conditions for factor to apply
    }
    #Commands to execute if the random check succeeded
}

The actual chances (if both <factor_1> and <factor_2> conditions match) are: <percentage> * <factor_1> * <factor_2> / 100

Example:

random = {
    chance = 20
    add_trait = stressed
}

Random list

random_list = {
    <weight_A> = {
   	mult_modifier = {
	    factor = <factor_1>
	    #Conditions for factor to apply
	}
	mult_modifier = {
	    factor = <factor_2>
	    #Conditions for factor to apply
	}
	#Commands for case A
    }
    <weight_B> = {
	modifier = {
	    factor = <factor_3>
	    #Conditions for factor to apply
	}
	#Commands for case B
    }
    ...
    fallback = {
	# Fallback commands are executed if no other option has a chance above 0
    }
}

The actual chances (in case all factor match) are:

  • Total = <weight_A> * <factor_1> * <factor_2> + <weight_B> * <factor_3> + ...
  • A chances: <weight_A> * <factor_1> * <factor_2> / Total
  • B chances: <weight_B> * <factor_3> / Total
  • Fallback: if all other options have 0 weight

Example:

random_list = {
    10 = {
	give_nickname = nick_the_wise
    }
    10 = {
	give_nickname = nick_the_able
    }
    10 = {
	trigger = {
	    NOT = { religion = buddhist }
	}
	give_nickname = nick_priest_hater
    }
}

In the example below, if the player has Holy Fury installed there will be a 50/50 chance of either option being chosen. If the player does not have Holy Fury installed then only the first option will be chosen.

random_list = {
    50 = { 
	set_graphical_culture = horse
	culture = horse 
    }
    50 = {
	trigger = {
	    has_dlc = "Holy Fury"
	}
	set_graphical_culture = hedgehog_culture
	culture = hedgehog_culture
    }				
}

Scope chain

To understand scope chains, imagine that every time the game engine encounters a scope while processing a script block, the scope will be put on the top of a stack. Once the triggers, commands or nested scopes in that scope have been evaluated, the current scope is removed from that stack, and game engine continues with processing the parent scope.

Note that the scope chain is different from the event chain: each time an effect that triggers an event (like character_event) is executed inside an event or decision, the base scope of that event (i.e. the caller) is added to the event chain, so that the new event (i.e the callee) may reference it.

Special scopes

When nesting scopes, special variables are used to reference other scopes in the chain. These values cannot be changed.

Variable Description
ROOT

Original and lowest scope in the chain:

  • Events: the entity (character or province) who got the event. For on_action events, varies depending on the action. In some rare cases, the default scope (THIS at the beginning of the block) and ROOT are different: this happens for major = yes events.
  • Decisions: the decision taker.
  • Targeted decisions: the targeted character.
  • Objectives: the plotting character
  • Casus Belli: the character who has a case for war.
  • Buildings: the province.
  • job_actions: the councillor
FROM
  • Events: the entity (character or province) who sent the event. Only works if an event was triggered by another event/decision, to scope to that trigger. May be chained with FROMFROM to go up the chain of events (maximum 4 FROMs as of patch 2.3). For on_action events, varies depending on the action.
  • Targeted decisions: the decision taker.
  • Objectives: the target character of the plot
  • Casus Belli: the character who is being declared war on.
  • Buildings: the character (baron) holding the settlement. FROMFROM is the holding (barony title).
  • job_actions: the ruler
ROOT_FROM

ROOT_FROM is a shortcut for ROOT = { FROM = { } }: it allows to reference the FROM of current block when inside a chain of FROM scopes.

Since FROM is relative, using FROMFROM = { FROM = { } } would be equivalent to using directly FROMFROMFROM = {}, which is not what you want. FROMFROM = { ROOT_FROM = { } } allows to apply conditions/commands referencing both FROMFROM and FROM. For instance: FROMFROM = { is_child_of = ROOT_FROM }.

PREV Previous scope used in the chain.

May be chained with PREVPREV to go several scopes up the chain (maximum 4 PREVs).
Note: PREV, ROOT and FROM do count as a scope change.

THIS Current scope.

Used in conditions/commands that work against a scope, as that argument is mandatory:

any_realm_character = {
   ROOT = { is_liege_of = THIS }
}

Note that using ROOT and FROM scopes do not change the scope of THIS.

For instance imagine the following province event:

province_event = {
    ...
    trigger = {
	#1
	owner = {
	    #2
	    top_liege = {
		#3
		culture = PREV
	    }
	    #4
	    NOT = {
		#5
		culture = ROOT
	    }
	}
    }
    ...
}

In order of evaluation, at position:

  • #1: trigger is not a scope, but the beginning of script block (aka function). ROOT is referencing the province for which the event is being evaluated. THIS points to ROOT. The stacks looks like that:
ROOT		<- THIS
  • #2: owner is a scope, so THIS now points to the owner of the province. PREV points to ROOT (previous scope). The stacks looks like that:
owner		<- THIS
ROOT		<- PREV
  • #3 top_liege is a scope, so THIS points to the top liege of the owner of the province. PREV points to the owner, PREVPREV points to ROOT. So here it is comparing the culture of the top liege with the culture of the owner. The stacks looks like:
top_liege	<- THIS
owner		<- PREV
ROOT		<- PREVPREV
  • #4 Previous block is finished, so THIS is now pointing again to the owner of the province, PREV points to ROOT.
  • #5 NOT is an operator, not a scope. Scope chain remains unchanged. So here it is comparing the culture of the owner with the culture of the province.

Scopes and conditions

Comparison

Scopes cannot be compared directly for equality: for instance primary_title = k_england is invalid. The appropriate condition must be used:

Characters Titles Provinces
current_heir = {
    character = PREV
}
primary_title = {
    title = k_england
}
location  = {
    province = 272 # Akershus
}

Checking if a scope exists

When a scope is used in a condition block, always = yes condition can be used to check if anything was matched by the scope:

trigger = {
    #Will evaluate to true if scope is not empty
    scope = {
	always = yes
    }
}

For instance to check a character has a mother: mother_even_if_dead = { always = yes }.

Count

When a scope is used in a condition block, count = N condition will cause the scope to evaluate to true, if at least N elements are matched in the scope. As of patch 2.3 any scopes starting with any_ can be used with count. As of patch 2.8 count supports numerical operators in most any_ scopes.

trigger = {
    #Will evaluate to true if at least N elements in the scope match the conditions
    scope = {
	#Conditions
	count >= N
  }
}

For instance to check if a ruler has at least 7 dwarf courtier at his court:

any_courtier = {
    count >= 7
    trait = dwarf
}

Limit

Limit can be used to further reduce what is matched by a multi-variant scope (any_, random_), when a scope is used in command block:

scope = {
    limit = {
	#Conditions on elements of the scope
    }
    #Commands to execute for each element of the scope that has been matched
}

For instance to scope only other rulers of same religion in the realm and fire some event to them:

any_realm_lord = {
    limit = {
	religion = ROOT
    }
    character_event = { id = xxx }
}

A frequent modding mistake is to try using limit inside a trigger block, which is not only unnecessary, but will also break the event.

Preferred Limit

In Patch 3.0, the new preferred limit syntax was added to the game, which can be used in any random_ scope to help specify multiple layers of specificity when choosing a random target.

For example, say you want to find the strongest vassal in your realm who is ambitious. You cannot directly sort a list of vassals by strength and pick the top one who is ambitious, without an overly complex system of pinging events and variables, but you can have a good approximation using preferred limits.

random_vassal = {
    limit = {
	trait = ambitious
    }
    preferred_limit = {
	is_powerful_vassal = yes
    }
    preferred_limit = {
	OR = { 
	    tier = KING
	    tier = DUKE 
	}
    }
    preferred_limit = {
	tier = COUNT
    }
    #Commands
}

This looks for a random vassal, who follows the regular limit (is ambitious), but first looks amongst those who meet the first preferred limit (is powerful vassal). If it can find an ambitious powerful vassal, it will execute the commands and call it a day - otherwise, it will look among the king-tier and duke-tier vassals for one who is ambitious. And if there is no ambitious king or duke vassal, it will look among the count-tier vassals. If none of the preferred scopes are met, then it will disregard them all and look for any vassal who is ambitious (who will be a baron in this case, by process of elimination).

Score Value

Patch 3.0 added a score selection to any_ effect scopes, used in conjunction with count. Rather than executing effects on random matching elements, the highest scoring elements are selected instead. This can be combined with limit, but not preferred_limit, which is exclusive to random_ scopes.

any_society_member = {
    score_value = {
	value = 1

	additive_modifier = {
	    society_rank == 4
	    value = 1000
	}
	additive_modifier = {
	    society_rank == 3
	    value = 100
	}
    }
    count = 3
    add_trait = maimed
}

Tooltips

In triggers and effects, tooltips are automatically generated by the Clausewitz engine in order to provide a detailed summary of what will occur when the player chooses an option. For instance, when the user hovers their mouse over a decision in the Intrigue menu, a detailed list of tooltips showing the conditions that allow them to take that decision will be automatically generated by the game, allowing the player to see at a glance what changes are needed before the decision becomes available -- or why they can choose that decision now, if they already meet all the conditions. Likewise, when the player moves their mouse over one of the buttons in an event, the list of results is automatically generated from the effects contained in that option.

These automatically generated tooltips usually range from helpful to very helpful, but there are rare occasions (especially with highly complex scripting) where they may produce tooltips that are unnecessarily technical or cluttered, are improperly localised, or even state the opposite of what they actually do. To correct this, the mod author is able to override these automatically generated tooltips with custom tooltips of their own.

Three special clauses can be used to change the tooltip displayed for an effect or condition:

Hidden tooltips

Hides the entire evaluation of an effect or trigger from the player, typically the firing of an event, or conditions that only AI has to satisfy.

hidden_tooltip = { province_event = { id = CM.1106 } }

Hidden tooltips are very useful for decisions and events if the actual results are to be hidden from the player for dramatic effect, or if the automatically generated tooltips produce unnecessarily technical details (such as the setting and modification of variables).

Hidden tooltips can also be used in triggers, but depending on the specific context it may be very confusing for the player if a decision satisfies all of its visible conditions, but fails a hidden one; a custom_tooltip is preferable in this case.

In 2.6.2 hidden_effect and hidden_trigger aliases were added, which can be used to reduce ambiguity.

Custom tooltips

Custom tooltips simplify some complex or hard to read triggers or effects, by replacing the tooltip content with the contents of a localisation key.

custom_tooltip = {
    text = pagan_subjugation_tip

    attacker = {
	subjugate_or_take_under_title = { # If the target only has territory within the kingdom, he is simply vassalized
	    title = PREV
	    enemy = defender
	}
    }
}
custom_tooltip = {
    text = UNOCCUPIED_DEMESNE_TITLE
	
    any_demesne_title = {
	NOT = { higher_tier_than = count }
	is_occupied = no
    }
}

These allow very complex trigger chains to be reduced to a simple message.
custom_tooltip can also be used to display a message without hiding effects or triggers:

custom_tooltip = {
    text = disables_centralization_1
}

Conditional tooltips

Added in version patch 2.8, these display the tooltip conditions only if the included trigger evaluates successfully.

conditional_tooltip = {
    trigger = {
	liege = {
	    independent = no
	}
    }
    liege = {
	liege = {
	    will_liege_enforce_peace = no
	    has_liege_enforced_peace = no
	}
    }
}

In the above example, the liege's realm peace conditions are only shown if the liege is not independent (as if the liege were independent, this would produce a strange-looking tooltip when it scopes to the non-existent liege of the liege).

This can also be used to rule out massive, ugly trigger chains for a better user experience -- for instance, when checking the tier or society membership of the ruler, and then checking specific conditions for each tier or society. Each specific block could check to see if the character is of that tier or a member of that society, therefore showing only the relevant conditions.

Tooltip filtering

Triggers and effects may also be filtered for better readability.

show_scope_change (patch 2.8)

trigger = {
    location = {
	show_scope_change = no

	owner = {
	    show_scope_change = no

	    opinion = { who = ROOT value = 50 }
	}
    }
}

The above example scopes to the owner of the character's current location to check their opinion of the root character. By default, this would produce an ugly chain of your current location, followed by a blank line (the "owner" scope is not localised in the game), followed by the correct and intended line. With the show_scope_change = no limiter, only the single intended line would be shown.

show_only_failed_conditions (2.8.1)

trigger = {
    show_only_failed_conditions = yes
	
    always = yes
    character = ROOT
    is_vassal_of = FROM
}

This feature applies to triggers only. In the above example, "always = yes" and "character = ROOT" will always be true. The only condition that can be true or false is whether the "is_vassal_of = FROM" (is FROM the vassal of ROOT?). With show_only_failed_conditions = yes, the "always" and "character" conditions will not be shown. This is useful when there are a lot of edge cases that need to be handled and the vast majority of the test cases will be true most of the time.

This feature is used heavily in the Jade Dragon decisions to show only why China will reject a given proposal, without showing all of the obvious reasons why they would accept it.

generate_tooltip (patch 2.8)

hidden_tooltip = {
    generate_tooltip = no
    any_realm_character = {
	limit = {
	    controls_religion = no
	    religion_group = ROOT
	    is_landed = yes
	    NOT = { character = ROOT }
	}
	opinion = {
	    who = ROOT
	    modifier = opinion_declared_unjust_conquest
	    years = 10
	}
    }
}

Even though the hidden_tooltip will hide the tooltips, it will still perform all of the necessary localisation processing of the tooltips. The generate_tooltip = no parameter suppresses this behaviour. Note that this will break randomisation and suppress the assignment of the localisation of event_targets and other similar behaviour. This feature is used in vanilla to suppress the processing of complex "any_" scopes for localisation when the localisation won't be shown to the player (in, e.g., casus belli).

Combining tooltips

It is possible to use a conditional_tooltip to hide a custom_tooltip. You may surround a series of custom tooltips in an over-arching conditional tooltip.

A custom_tooltip will hide all of the automatically-generated tooltips in its block, replacing it with the localised text. Therefore, it is not necessary (but not harmful) to use a hidden_tooltip inside a custom_tooltip, as the custom tooltip will already hide its contents and display only the localised text, with a (*) or (x) based on its evaluated value.

Likewise, surrounding either a custom_tooltip or conditional_tooltip with a hidden_tooltip will simply hide everything inside, rendering them unnecessary.

Notes

Tooltips can seem to be wrong when an effect both sets and reads a flag or updates and reads a variable in the same block. This is because to display the tooltip, the triggers in option block are executed, but not the commands (they will only when the option is actually chosen), so it may be inconsistent. Here is an example where tooltip cannot reflect what will actually happen:

option = {
    set_character_flag = flag_do_something # Not executed to calculate tooltip
    if = {
	limit  = { has_character_flag = flag_do_something } # Executed to calculate tooltip: will be false
	# Do it
    }
}

To resolve this, use custom_tooltip. For event options, consider moving the commands that modify flags or variables up from the option block into an immediate block.

Scripted block

A scripted trigger (or scripted effect) is a macro, comprised of conditions (respectively commands) that can be executed in a single batch.

It allows reusing common scripts throughout a mod, instead of copying and pasting that code wherever it is used (and having to remember to update each separate use every time). On the other hand, over-use of scripted triggers or scripted effects can lead to spaghetti code. Unless you are re-using the same code over and over and it is growing unnecessarily large in file size or unwieldy to maintain, it is usually preferable to keep triggers or effects where they are used instead of moving them to a scripted trigger or effect.

Definition

Scripted blocks are defined in any filename in the \common\scripted_effects\ or \common\scripted_triggers\ folders, using the extremely simple syntax:

scripted_block_name = {
	#commands
}

Thereafter, the scripted block can be called in your code like any other trigger or command, using the format: scripted_block_name = yes

It is always best practice to include a proper naming convention for your scripted blocks to avoid overlap with other mods, and never to overwrite the original vanilla files, as there is no reason to do so: simply define a new file for your own scripted blocks. Also use a prefix or suffix that identifies that your trigger/command is a scripted trigger/effect, and not built-in: mymod_helloworld_effect = yes #Paradox naming convention fn_mymod_helloworld = yes #Other naming convention

It is important to use unique names. For instance, if you have a trait that has the same name as an effect block, it will not be parsed correctly if you refer to that trait within the block.

Scopes

The scopes of the scripted blocks are always equal to the context in which the scripted block is called—THIS, ROOT, FROM, PREV, etc. will always be assigned to the same as where the scripted effect was originally used. Be extremely careful to use your scripted block in the correct scope it was created for, including the entire scope chain, or else portions of the code may fail silently. While this is unlikely to crash anything, it can result in silent failures and logic errors and can be a source of frustration because of the degree of separation between the file where the scripted block is being used and the file where the scripted block is defined—code that appears correct in either file may not be correct if the scopes are different than intended.

Example

The following scripted effect removes all "vanilla" education traits from the character:

  1. SCOPE: THIS = Character
fn_remove_education_traits = {
    #DIPLOMACY
    remove_trait = naive_appeaser
    remove_trait = underhanded_rogue
    remove_trait = charismatic_negotiator
    remove_trait = grey_eminence
    #MARTIAL
    remove_trait = misguided_warrior
    remove_trait = tough_soldier
    remove_trait = skilled_tactician
    remove_trait = brilliant_strategist
    #STEWARDSHIP
    remove_trait = indulgent_wastrel
    remove_trait = thrifty_clerk
    remove_trait = fortune_builder
    remove_trait = midas_touched
    #INTRIGUE
    remove_trait = amateurish_plotter
    remove_trait = flamboyant_schemer
    remove_trait = intricate_webweaver
    remove_trait = elusive_shadow
    #LEARNING
    remove_trait = detached_priest
    remove_trait = martial_cleric
    remove_trait = scholarly_theologian
    remove_trait = mastermind_theologian
}

Then it can be called in any effect block as if it were a command included in the base game:

character_event = {
    id = ''mymod''.1
    desc = "I can't help but feel like I'm forgetting something..."
 
    trigger = {
	trait = imbecile
    }
    mean_time_to_happen = {
	years = 800
    }
 	
    option = {
	name = "Damn this amnesia!"
	''#Remove all education traits from THIS (character)''
	'''fn_remove_education_traits = yes'''
    }
}

It is strongly recommended to document the scopes which a scripted block requires, in a comment at the beginning of that scripted block.

Scripted blocks are known to work in events, decisions, disease definitions, and character history.

If your scripted trigger uses a custom tooltip then it is best to use NOT = { scripted_trigger = yes } instead of scripted_trigger = no otherwise the custom tooltip will not be inverted correctly like automatically generated tooltips from normal conditions are.

Usage

Scripted blocks are intended to make code easier to read and maintain by placing conditions or effects in a single location of a mod.

If you have a single set of conditions being tested for repeatedly within a mod or have an effect being used frequently then it's recommended to use a scripted block to be able to easily modify and adjust behaviour. The use of scripted blocks also helps reduce the number of repeated lines from conditions or effects.

There's a great number of pre-made triggers and effects within the vanilla code and be aware that they can be used freely within your own mods.

Differences from other languages

Modders skilled with other programming languages need to be aware of the following quirks of scripted blocks:

  • There are no parameters or other variables that can be passed to a function; the argument is always the singular boolean "yes". This can be worked around by setting variables prior to calling the function, and then checking those variables inside the function, bearing in mind the limited support for variables in Crusader Kings II.
  • The behaviour of the function depends on the context from which the function is called. The function does not create a new context unless it explicitly includes a new scope. Always ensure that the function is called from the correct context.
  • Functions cannot return a value—they behave as "void" functions. To "return" a value, set a flag (e.g., set_character_flag) or set a variable (i.e., set_variable) inside the function. Remember that the function takes in the same context as the place it is called, so all variables assigned will be saved to the calling scope and accessible outside the function.
  • Scripted blocks are more like non-hygienic macros than proper functions (similar to batch files and other context-dependent macros).

See also

References