Scripting 允许动态修改世界并控制人工智能角色行为。它与静态相对 history modding,尽管有些脚本偶尔会从历史文件中触发。
Scripting is event-based, with Clausewitz 引擎优化以过滤和处理大量角色和省份的事件。
该结构基于对具有以下条件的实体定义:
该引擎的一个相对独特的特性是能够动态显示这些“块”的文本表示,在 tooltips (每个范围、条件和命令都本地化),玩家可以在采取行动前预览效果,或知道为何决策不可用,且无需模组制作者额外工作。
这些脚本是普通.txt文件,使用类似 JSON 的语法:
# 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 = {
}
允许属性和块的语法取决于文件通往基础游戏目录的相对路径:
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.
注意:负日期不会被发动机正确处理。 |
| string | 字符串的字面值,例如“Orsini”。只有当值包含空格时才严格需要引用,但建议保持可读性。 |
| 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 | 一个 ID 引用另一个文件中定义的实体(culture、宗教、culturegfx、localization key,...)。不包含空格(_ 通常用作单词分隔符)。虽然可以引用,但容易与字符串字面值混淆。 |
Operators used to combine several conditions:
| Operator | Description | Example |
|---|---|---|
| AND | 如果所有包含的条件都返回为真,则返回为真。这是范围变更后的默认操作员。 | AND = {
age >= 15
age < 35
}
|
| OR | 如果至少有一个封闭条件返回为真,则返回为真 | OR = {
culture = norman
culture = saxon
}
|
| NOT | Returns true if the enclosed condition is false.
它的行为与NOR相同,但为了明确起见,在多于一种情况下优先考虑NOR。 |
NOT = { trait = on_hajj }
|
| NOR | 如果包含的条件都不返回真,则返回为真。 | 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 | 如果至少有一个包含的条件返回为假,则返回为真。 | 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.
|
calc_true_if = {
amount >= 3
culture = swedish
religion = catholic
is_female = no
is_adult = yes
age >= 50
}
|
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 或更早)。 与预期相反,这并不是一个精确的比较。 ai_greed = 5 读作“ai_greed至少为5”,而不是“ai_greed等于5”。当处于非状态内时,读作“ai_greed小于5”。
|
| < | Less than | ai_greed < 5
|
NOT = { ai_greed = 5 }
|
较小的不等价性。 如果值小于数字且不等于数字,则为真。 |
| > | Greater than | ai_greed > 5
|
ai_greed = 6
|
更不平等。 如果值大于数字,但不等于数字,则为真。 2.8 之前的等效方法仅适用于整数(否则需要与目标数加上一个非常小的浮点数进行检验,例如 scaled_wealth = 5.00001).
|
| <= | Lesser or equal | ai_greed <= 5
|
NOT = { ai_greed = 6 }
|
较小的等价。 如果变量大小于数字,则成立。 2.8 之前的等效方法仅适用于整数(否则需要与目标数加上一个非常小的浮点数进行检验,例如 NOT = { scaled_wealth = 5.00001 }).
|
| >= | Greater or equal | ai_greed >= 5
|
ai_greed = 5
|
更为等同。 当值大于或等于数字时,则为真。 与 相同,但词汇上优于 =.
|
| == | Equal | ai_greed == 5
|
ai_greed = 5
|
完全对比。 当且仅当值等于数字且无其他值时,为真。 2.8 之前的等效方法仅适用于整数(否则需要与目标数加上一个非常小的浮点数进行检验,例如 wealth = 5 NOT = { wealth = 5.00001 }).
|
Warning: the above feature applies neither to the tier nor among_most_powerful_vassals 触发点。
这是出于向后兼容性考虑,因为现有代码可能假设“tier = 公爵”意味着相等,而非大于或等的比较。如果需要修改以反映新干员,这会破坏多个模组。
if 语句仅在满足特定条件时才允许执行命令。结构如下:
if = {
limit = {
#Conditions
}
#Commands to execute if the conditions match
}
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 仍可通过重复条件来模拟 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
}
3.0 补丁增加了对触发子句控制流程语句的支持,扩展了 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.
这些在指示长时间复杂触发时的控制流程和意图非常有用。请记住,长链 trigger_else_if that require one of the limit clauses to be true have to end with trigger_else,因为整个链条被忽略,因此如果没有以下条件,则总是成立。 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
}
}
这可以更清楚地写成:
trigger_if = {
limit = { is_female = yes }
NOR = {
religion_group = muslim
has_religion_feature = religion_patriarchal
}
}
trigger_else = {
NOT = { has_religion_feature = religion_matriarchal }
}
trigger_switch was introduced in patch 2.4 并且是多个IF/极限块的捷径,如果IF值相同,则具体如下: on_trigger.
它适用于所有具有非复右边自变量的触发条件,并会尝试在on_triggers中从trigger_switch子句所在的范围进行正常评估。如果on_triggers中多个值被评为真,它仍然只执行列表中第一个被判定为真值的值。[1] If a fallback 值存在且没有其他值被评估为真,则会执行备用选项。
Switch doesn't work with scripted trigger因为它们是宏,因此是复杂的右侧参数。[2]
As of patch 2.5, it was not working with the region 触发,因为某个具体问题,会被修复。[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 was introduced with patch 2.3 并且作为返回语句,导致之后的所有内容(在主块中:option、immediate、...)都被忽略。[4] 当命令互斥时,避免了在另一个if/limit块中重复使用非操作符的条件。结构如下:
if = {
limit = {
#Conditions
}
#Commands to execute if the conditions match
break = yes
}
#Commands to execute if the conditions do not match
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
}
这会为当前的瞄准角色增加100点财富,因为它会循环20次。
Notes:
while 效果不会产生提示,因为提示会带来问题。In patch 2.8, an additional count 新增了参数,允许固定次数的循环(即for循环),且无需使用变量。 如果需要固定的循环次数,这能提供更简洁、更简洁的代码。
while = {
count = 20
wealth = 5
}
上述效果方块会像之前的例子一样,给这个加100点财富。
如果两种方法结合使用,循环将在极限失败或计数通过时退出,以先发生者为准。
set_variable = {
which = myvar
value = 25
}
while = {
count = 20
limit = {
check_variable = {
which = myvar
value >= 1
}
}
subtract_variable = {
which = myvar
value = 1
}
wealth = 5
}
尽管上述 while 循环“应该”执行 25 次,因为 myvar,循环20圈后会结束,因为 count. If myvar 设置为15,它只执行15次,而不会执行20次,因为变量检查会是假的。
以下是根据信息所属范围类型,可能存储信息的方法总结:
| Type of storage / Supported scopes |
Variable (numeric) | Flag (boolean + date) | Modifier (boolean + date) | Event target (scope reference) | Earmark (boolean) |
|---|---|---|---|---|---|
| Global scope | set_variablewith 'global_' prefix in front of variable name |
set_global_flag
|
No | save_global_event_target_as
|
No |
| Event chain | set_variablewith '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_flagor 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 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).
变量在存档中被持久化,虽然没有清除变量的命令,但文件大小的增加通常可以忽略不计。设置为0的变量不会保存到文件中,因此在重新加载后会被清除。
Since patch 2.8可以检查带有触发条件的变量,并将变量分配给效果/命令。
set_variable = { which = myvar value = 5 }
wealth = myvar #gives 5 wealth
Important: in versions prior to patch 2.8脚本变量只能拉取游戏中使用的其他数值变量(属性、金币、虔诚度等),也就是说,无法将变量的值分配给触发器。
变量总是存储/检索在命令/触发器的作用域中。 比如说,创建一个变量 myvar with value 1 in ROOT scope:
ROOT = {
set_variable = { which = myvar value = 1 }
}
或者要创建全局变量,可以在变量名称前加上前缀 'global_':
export_to_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可以通过将其中一个变量的值设为global_variable,然后将全局变量与任意有作用域的变量进行比较,方法如下:
<any_scope> = {
set_variable = { which = global_myvar which = myvar_1 } # Creates a global_variable with value of myvar_1
}
<any_scope> = {
check_variable = { which = global_myvar which >= myvar_2 } # Checks that global_variable's value is greater than or equal to myvar_2
}
也可以统计作用域的实例数:
export_to_variable = {
which = global_debug_count
value = 0
}
<any_scope> = {
change_variable = {
which = global_debug_count
value = 1 # increase by 1 for every instance of scope
}
}
Support for event variables was added in patch 2.8,这些只能在事件链的持续时间内访问。这些不绑定于任何范围,对于不支持变量的范围(如血统)来说非常方便。保存事件变量时,名称以“local_”开头。
变量可以通过 export_to_variable 命令赋予特定触发器的值
ROOT = {
export_to_variable = {
which = myvar
value = stewardship
}
}
Variables which can be exported[6][7]:
通常,值是从当前的范围中提取的,但也可以设置另一个范围,并使用 who parameter.
ROOT = {
export_to_variable = {
which = myvar
value = stewardship
who = FROM
}
}
变量上的命令有第二个参数,可以是:
(set|change|subtract|multiply|divide|modulo)_variable = { which = <variable_name> value = <value> }(set|change|subtract|multiply|divide|modulo)_variable = { which = <variable_name> which = <another_variable_name> }(set|change|subtract|multiply|divide|modulo)_variable = { which = <variable_name> which = <scope> }变量可以通过通常的优比较或相等比较,或严格等式来检验:
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]变量本身通过一个.csv条目进行局部化,其键为变量名:
myvar;My Variable;;;;;;;;;;;;;x任何变量命令都会将之前未定义的变量视为值为0。
Flags are boolean values (旗帜存在或不存在),可以附加到以下范围:角色、省份、头衔、王朝或全球范围。
The command to set or clear a flag are:
set_<scope>_flag = <flag_name>clr_<scope>_flag = <flag_name>可以通过条件检查旗帜,无论是旗帜存在还是旗帜设置后的持续时间:
has_<scope>_flag = <flag_name>had_<scope>_flag = { flag = <flag_name> days = <duration> } Note that modifiers 与旗帜有点相似,但它们还会改变所附范围的统计数据。
旗帜名称可以动态,通过添加@和范围(角色、省份或标题):[9]
它只影响旗帜名称的创建方式,但不改变旗帜的检查方式。
FROM = { set_character_flag = is_friend_of_@ROOT }
例如,如果角色ID为140,保存的角色标志将为 is_friend_of_140.
They can also be used in triggers: has_character_flag = is_friend_of_@FROM
动态标志目前仅支持用于 set|clr_scope_flag commands and has|had_scope_flag条件。它们不能用于create_character命令的旗帜行(除上述情况外使用的旗帜必须是静态的)。[10]
Note that scopes saved as variables in event targets can be used in dynamic flags as well.[11]
事件生成单位有类似但更有限的机制,称为 earmarks.
An earmark is added when creating a unit via:
spawn_unit = { earmark = <earmark> }
然后可以在单元所有者身上进行测试:
has_earmarked_regiments = <earmark>
最后,以指定代号解散该单位:
disband_event_forces = <earmark>
范围(角色、省份或头衔)可以保存为变量,并在 event chain, using:[12]
save_event_target_as = <target_name> or save_global_event_target_as = <target_name>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}
}
事件目标可以在保存事件的工具提示中使用(通常无法实现,因为提示是在执行效果之前建立的)。
事件目标在本地化中通过直接引用变量名称来实现:
I have decided to punish my vassal [target_adulterer.GetFullName]
持久事件目标的工作原理类似于普通事件目标,但它们绑定在一个范围(可以是省份、角色、头衔、神器、社会或地图外势力),而不是事件链。这意味着你可以在原始事件链结束后,从不同上下文中继续引用它,比如新事件、社团名称、特质名称(在原版中用来存储负责加冕者的名字,体现在“由X加冕”特质中)。只有在有意清除,或者持有者(如果是角色)死亡时,这些任务才会被清除。
save_persistent_event_target = { name = name_of_target scope = event_target:my_character }.范围就是节省的范围。它会保存在当前作用域的范围里,前提是支持持久事件目标。相对范围,比如ROOT、Liege等,也有效。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.在与常规事件目标碰撞时,非持久目标优先。有时需要测量自某事件发生以来经过的时间,作为另一个事件的条件。
There are 2 techniques for that: flags or event modifiers.
Scripting flags keep a reference to when they were set.
set_<scope>_flag command:set_character_flag = money_from_the_pope
had_<scope>_flag triggers to check elapsed time in days:had_character_flag = { flag = money_from_the_pope days = 730 }
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
修饰符可以作为冷却计时器,让脚本更简单,但限制在角色、省份和领地。
add_character_modifier, add_province_modifier or add_holding_modifier commands:add_character_modifier = {
name = lustful_affair_timer
duration = 2190
hidden = yes
}
has_character_modifier, has_province_modifier or has_holding_modifierhas_character_modifier = lustful_affair_timer
lustful_affair_timer = { icon = 1 }
注意:隐藏修饰符不需要本地化,因为它们不会显示。
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 }
有两个命令可以用来生成随机结果: random and random_list.
random_list is similar to using several random 块,但它保证只执行一个块,而多个块则不然 random blocks.
Since patch 2.3 both commands can be weighted with some mult_modifier = { } blocks, that based on conditions, can:
Since patch 2.7 random_list supports the use of trigger. trigger 它废弃了因子为0的修饰符,且无需反逻辑。 random can be wrapped inside an if block instead.
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
}
实际概率(如果两者<factor_1> <factor_2> 和条件相符)是: <percentage> * <factor_1> * <factor_2> / 100
Example:
random = {
chance = 20
add_trait = stressed
}
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:
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
}
}
在下面的例子中,如果玩家安装了Holy Fury,选择任一选项的概率是50/50。如果玩家没有安装Holy Fury,则只会选择第一个选项。
random_list = {
50 = {
set_graphical_culture = horse
culture = horse
}
50 = {
trigger = {
has_dlc = "Holy Fury"
}
set_graphical_culture = hedgehog_culture
culture = hedgehog_culture
}
}
为了理解范围链,想象每次游戏引擎在处理脚本块时遇到某个范围,该范围都会被放在堆栈顶部。 一旦评估了该范围中的触发器、命令或嵌套范围,当前范围会从该堆栈中移除,游戏引擎继续处理父范围。
Note that the scope chain is different from the event chain:每次触发事件的效果(如 character_event)在事件或决策中执行时,该事件的基础作用域(即调用者)被添加到事件链中,以便新的事件(即被调用者)可以引用该事件。
嵌套示域时,使用特殊变量引用链中其他望域。这些价值观是无法改变的。
| Variable | Description |
|---|---|
| ROOT |
Original and lowest scope in the chain:
|
| FROM |
|
| ROOT_FROM |
ROOT_FROM is a shortcut for Since FROM is relative, using |
| PREV | Previous scope used in the chain. 可以与PREVPREV串联,向上级(最多4个PREV)。 |
| THIS | Current scope. 用于对作用范围不利的条件/命令,因为该论点是强制的: any_realm_character = {
ROOT = { is_liege_of = THIS }
}
注意,使用 ROOT 和 FROM 作用域不会改变 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:
trigger 不是作用域,而是脚本块(又称函数)的开头。 ROOT 指的是评估该事件的省份。 THIS points to ROOT. The stacks looks like that:ROOT <- THIS
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
top_liege is a scope, so THIS 指向该省所有者的最高领主。 PREV points to the owner, PREVPREV points to ROOT.所以这里是对比顶级领主文化和所有者文化的比较。书架看起来像:top_liege <- THIS owner <- PREV ROOT <- PREVPREV
PREV points to ROOT.NOT is an operator不是瞄准镜。瞄准镜链保持不变。所以这里是比较地主文化与省份文化。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
}
|
When a scope is used in a condition block, always = yes 条件可用于检查示波器是否匹配:
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 }.
When a scope is used in a condition block, count = N 条件 将使作用域被评估为真,前提是 函数中至少有 N 个元素匹配。
截至 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
}
}
例如,要检查统治者宫廷中是否有至少7名矮人宫廷侍从:
any_courtier = {
count >= 7
trait = dwarf
}
极限还可用于进一步减少多变体示波器的匹配范围(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
}
例如,只对同一宗教的其他统治者进行范围,并向他们发送某个事件:
any_realm_lord = {
limit = {
religion = ROOT
}
character_event = { id = xxx }
}
一个常见的模组错误是试图在触发块内使用限制,这不仅没必要,还会破坏事件。
在3.0补丁中,游戏新增了首选限制语法,可用于任何游戏 random_ 在选择随机目标时,帮助指定多层特异性。
比如说,你想找到你领地里最强且有野心的附庸。你不能直接按实力排序附庸名单,挑选最有野心的附庸,否则会用过于复杂的事件和变量定位系统,但你可以用优选限制来做出不错的近似值。
random_vassal = {
limit = {
trait = ambitious
}
preferred_limit = {
is_powerful_vassal = yes
}
preferred_limit = {
OR = {
tier = KING
tier = DUKE
}
}
preferred_limit = {
tier = COUNT
}
#Commands
}
这会寻找一个随机附庸,他遵循常规限制(雄心勃勃),但首先会从满足第一个优先限制的人中寻找(是强大的附庸)。如果能找到一个雄心勃勃且强大的附庸,它会执行命令然后结束任务——否则,它会在国王级和公爵级附庸中寻找有野心的附庸。如果没有雄心勃勃的国王或公爵附庸,它就会出现在伯爵级附庸之间。如果没有满足所有偏好范围,它会全部忽略,寻找任何有野心的附庸(此例中通过排除法成为男爵)。
Patch 3.0 added a score selection to any_ effect scopes, used in conjunction with count.游戏不再对随机匹配元素执行效果,而是选择得分最高的元素。这可以结合使用 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
}
In triggers and effects,工具提示由克劳塞维茨引擎自动生成,以详细总结玩家选择选项时将发生的事情。 例如,当用户将鼠标悬停在阴谋菜单中的某个决策上时,游戏会自动生成一份详细的工具提示列表,显示允许他们做出该决定的条件,让玩家一目了然地看到决策可用前需要做哪些更改——或者为什么他们现在可以选择该决定, 如果他们已经满足所有条件。 同样,当玩家将鼠标移动到事件中的某个按钮上时,结果列表会自动生成,这些效果包含在该选项中。
These automatically generated tooltips usually range from helpful to very 很有帮助,但极少数情况下(尤其是脚本复杂时),他们可能会生成一些不必要的技术提示或杂乱无章、本地化不当,甚至说明与实际内容相反的情况。 为了纠正这个问题,模组作者可以用自己的自定义提示覆盖这些自动生成的工具提示。
可以使用三个特殊条款来更改显示的工具提示,以应对某个效果或条件:
它隐藏了对某个效果或触发器的所有评估,通常是事件的触发,或者只有AI必须满足的条件。
hidden_tooltip = { province_event = { id = CM.1106 } }
隐藏工具提示对于决策和事件非常有用,尤其是当实际结果需要对玩家隐藏以达到戏剧效果,或者自动生成的工具提示产生不必要的技术细节(如变量的设置和修改)时。
隐藏提示也可以用于触发器,但根据具体情境,如果一个决策满足所有可见条件,却在隐藏条件中失败,玩家可能会感到非常困惑;a custom_tooltip is preferable in this case.
In 2.6.2 hidden_effect and hidden_trigger 还添加了别名,可以用来减少歧义。
自定义提示通过用本地化键的内容替换工具提示内容,简化了一些复杂或难以辨认的触发或效果。
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
}
}
这些链条可以将非常复杂的触发链简化为一条简单的消息。
custom_tooltip 也可以用来显示消息而不隐藏效果或触发器:
custom_tooltip = {
text = disables_centralization_1
}
Added in version patch 2.8,只有当包含的触发器成功评估时,这些工具才会显示提示条件。
conditional_tooltip = {
trigger = {
liege = {
independent = no
}
}
liege = {
liege = {
will_liege_enforce_peace = no
has_liege_enforced_peace = no
}
}
}
在上述例子中,只有当封建国不独立时,才会显示其领地的和平条件(比如如果封建者是独立的,当它涉及到不存在的封建时,会出现奇怪的提示)。
这也可用于排除大规模、丑陋的触发链,以提升用户体验——例如,在检查统治者的等级或社会成员身份时,再检查每个等级或社会的具体条件。 每个具体方块都可以检查角色是否属于该等级或该社会成员,因此只显示相关条件。
触发和效果也可以进行过滤以提高可读性。
trigger = {
location = {
show_scope_change = no
owner = {
show_scope_change = no
opinion = { who = ROOT value = 50 }
}
}
}
上述示例是让角色当前所在地的所有者查看他们对根角色的看法。 默认情况下,这会生成一条难看的当前位置链,接着是一行空白线(游戏中“拥有者”范围没有本地化),再接着是正确且预期的线。 与 show_scope_change = no limiter, only the single intended line would be shown.
trigger = {
show_only_failed_conditions = yes
always = yes
character = ROOT
is_vassal_of = FROM
}
此功能仅适用于触发器。 在上述例子中,“总是 = 是”和“字符 = 根”总是成立。 唯一可能为真或假的条件是“is_vassal_of = FROM”(FROM 是根的附庸?)。 其中 show_only_failed_conditions = yes,“始终”和“角色”条件不会显示。 当需要处理大量边缘情况且绝大多数测试用例都成立时,这非常有用。
这一特征在Jade Dragon决策中被大量使用,只用来展示中国为何拒绝某项提案,而未展示其接受该提案的所有明显理由。
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 它会隐藏提示,但仍然会执行所有必要的提示的定位处理。 该 generate_tooltip = no 参数抑制了这种行为。 注意,这将破坏随机化,抑制event_targets及其他类似行为的定位分配。 该功能在原版中用于抑制复杂“any_”范围的处理,当本地化未向玩家显示时(例如casus belli)。
It is possible to use a conditional_tooltip to hide a custom_tooltip. 你可以在一个整体条件提示中包围一系列自定义工具提示。
A custom_tooltip 会隐藏所有自动生成的工具提示,并用本地化文本替换。 因此,使用一个 hidden_tooltip inside a custom_tooltip,因为自定义提示已经隐藏内容,只显示本地化文本,带有 (*) 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.
当一个效果既设置又读取 flag or updates and reads a variable 在同一栋楼里。这是因为显示工具提示时,选项块中的触发器会执行,但命令不会执行(只有在选项被选中时才会执行),所以可能不一致。 这里有一个提示无法反映实际情况的例子:
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.对于事件选项,考虑将修改标志或变量的命令从以下位置移到上方。 option block into an immediate block.
A scripted trigger (or scripted effect) is a macro, comprised of conditions (respectively commands) that can be executed in a single batch.
它允许在模组中重复使用常用脚本,而不是复制粘贴代码(而且每次都得记得更新)。 另一方面,过度使用脚本触发或脚本效果可能导致 spaghetti code. 除非你反复使用同一段代码,文件体积变得不必要地庞大或维护不便,否则通常最好保留触发器或效果,而不是移到脚本触发或效果上。
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
}
此后,脚本块可以在代码中调用,就像其他触发器或命令一样,格式为: scripted_block_name = yes
最佳实践是为脚本块制定合适的命名规范,以避免与其他模组重叠,且绝不覆盖原始原版文件,因为没有必要这样做:只需为自己的脚本块创建一个新文件即可。 还要用一个前缀或后缀,表明你的触发/命令是脚本触发/效果,而不是内置的: mymod_helloworld_effect = yes #Paradox naming convention fn_mymod_helloworld = yes #Other naming convention
使用独特的名字非常重要。例如,如果你有一个特性与某个效果方块同名,如果你在方块内引用该特质,它就不会被正确解析。
The scopes 脚本块中的全部都等于调用脚本块的上下文——THIS、ROOT、FROM、PREV 等总是与脚本效果最初使用的上下文相同。务必非常小心地在正确的作用域中使用脚本模块,包括整个范围链,否则代码的部分可能会无声失效。 虽然这不太可能导致任何故障,但可能导致无声故障和 logic errors 并且由于使用脚本块的文件与定义脚本块的文件之间的分离度,可能会带来挫折——即使代码在任一文件中看起来正确,如果作用域与预期不同,也可能不正确。
以下脚本效果将角色中所有“原版”教育特质移除:
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
}
然后可以在任何效果方块中调用,就像它是基础游戏中包含的命令一样:
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 在脚本块开头的注释中记录脚本块所需的作用域。
脚本块已知能在事件、决策、疾病定义和角色历史中发挥作用。
如果你的脚本触发器使用自定义工具提示,那最好用 NOT = { scripted_trigger = yes } instead of scripted_trigger = no 否则自定义提示不会像正常条件下自动生成的工具提示那样正确反转。
脚本块旨在通过在模组的单一位置放置条件或效果,使代码更易阅读和维护。
如果你在模组中反复测试单一组条件,或者某个效果频繁使用,建议使用脚本块,以便轻松修改和调整行为。使用脚本块还有助于减少因条件或效果而出现的重复行数。
原版代码中有大量预设触发和效果,请注意它们可以在自己的模组中自由使用。
擅长其他编程语言的模组制作者需要注意脚本块的以下特性:
set_character_flag) or set a variable (i.e., set_variable)在函数内。 请记住,函数的上下文与被调用的地点相同,因此所有赋值的变量都会保存到调用作用域,并且在函数之外可访问。| 历史 | 角色 • 家族 • 省份 • 头衔 • 剧本 |
| 脚本 | 指令 • 条件 • 作用域 • 修正 • 事件 • 决议 |
| 常规 | 定义 • 游戏规则 • 另类开局 • 宗教 • 文化 • 政体 • 特质 • 血脉 • 科技 • 法律 • 建筑 • 宣战理由 • 朝贡国 • 单位 • 目标 • 疾病 • 死亡 • 荣誉头衔 • 社团 • 宝物 • 地图外政权 • 内阁成员 • 贸易路线 • 继承 • 奇观 • 称号 |
| 图像/音效/本地化 | 地图 • 图形 • 盾徽 • 肖像 • 界面 • 小地图 • 音乐 • 本地化 |
| 其他 | 故障排除 • 验证器 • 控制台指令 • 编辑游戏存档 • Steam创意工坊 • EU4转档器模组制作 |
Building moddingCasus Belli moddingCommandsConditionsDecision moddingEU4转档器模组制作EarmarkEvent moddingEvent targetFlagHistory moddingLocalisationModifierModifiersObjective moddingOperatorPatch 2.3Patch 2.4Patch 2.5Patch 2.6Patch 2.6.XPatch 2.7Patch 2.7.XPatch 2.8Patch 2.8.XReligion moddingScopesScripted triggerScriptingSteam创意工坊Variable事件模组制作作用域修正内阁成员模组制作决议模组制作剧本模组制作单位模组制作历史模组制作另类开局模组制作图形模组制作地图外政权模组制作地图模组制作头衔模组制作奇观模组制作宗教模组制作定义宝物模组制作宣战理由模组制作家族模组制作小地图模组制作建筑模组制作指令控制台指令政体模组制作故障排除文化模组制作朝贡国类型模组制作本地化条件