首页所有页面

脚本

Scripting 允许动态修改世界并控制人工智能角色行为。它与静态相对 history modding,尽管有些脚本偶尔会从历史文件中触发。

Scripting is event-based, with Clausewitz 引擎优化以过滤和处理大量角色和省份的事件。

该结构基于对具有以下条件的实体定义:

  • properties (or "flags")
  • 预定义的函数(或“块”)由引擎按特定顺序附加和调用,包含 scopes, conditions and commands.

该引擎的一个相对独特的特性是能够动态显示这些“块”的文本表示,在 tooltips (每个范围、条件和命令都本地化),玩家可以在采取行动前预览效果,或知道为何决策不可用,且无需模组制作者额外工作。

Basics

这些脚本是普通.txt文件,使用类似 JSON 的语法:

  • = 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 = {

}

允许属性和块的语法取决于文件通往基础游戏目录的相对路径:

  • common/religions 将包含.txt文件,其中实体为宗教团体和宗教,具有特定宗教属性(参见 religion modding).
  • events 将包含.txt个文件,其中实体为事件,具有针对事件的属性和块(参见 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.

注意:负日期不会被发动机正确处理。

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,...)。不包含空格(_ 通常用作单词分隔符)。虽然可以引用,但容易与字符串字面值混淆。

Boolean operators

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.

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 或更早)。 与预期相反,这并不是一个精确的比较。 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

NOT = { ai_greed = 6 }

完全对比。 当且仅当值等于数字且无其他值时,为真。 2.8 之前的等效方法仅适用于整数(否则需要与目标数加上一个非常小的浮点数进行检验,例如 wealth = 5 NOT = { wealth = 5.00001 }).

Warning: the above feature applies neither to the tier nor among_most_powerful_vassals 触发点。 这是出于向后兼容性考虑,因为现有代码可能假设“tier = 公爵”意味着相等,而非大于或等的比较。如果需要修改以反映新干员,这会破坏多个模组。

Control flow statements

If

if 语句仅在满足特定条件时才允许执行命令。结构如下:

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 仍可通过重复条件来模拟 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

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 }

}

Switch

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

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

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:

  • the while 效果不会产生提示,因为提示会带来问题。
  • 确保别制造无尽的循环......不过在10万圈时有保险措施。

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次,因为变量检查会是假的。

Storing information

以下是根据信息所属范围类型,可能存储信息的方法总结:

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).

变量在存档中被持久化,虽然没有清除变量的命令,但文件大小的增加通常可以忽略不计。设置为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]:

  • 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


通常,值是从当前的范围中提取的,但也可以设置另一个范围,并使用 who parameter.

ROOT = {

    export_to_variable = {

	which = myvar

	value = stewardship

	who = FROM

    }

}

变量上的命令有第二个参数,可以是:

  • 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> }
  • 一个作用域(以 ROOT/FROM/PREV/THIS 形式出现),其中包含同名变量: (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

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 与旗帜有点相似,但它们还会改变所附范围的统计数据。

Dynamic flags

旗帜名称可以动态,通过添加@和范围(角色、省份或标题):[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

事件生成单位有类似但更有限的机制,称为 earmarks.

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

然后可以在单元所有者身上进行测试: has_earmarked_regiments = <earmark>

最后,以指定代号解散该单位: disband_event_forces = <earmark>

Event targets

范围(角色、省份或头衔)可以保存为变量,并在 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}

}

事件目标可以在保存事件的工具提示中使用(通常无法实现,因为提示是在执行效果之前建立的)。

事件目标在本地化中通过直接引用变量名称来实现:

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

Persistent event targets

持久事件目标的工作原理类似于普通事件目标,但它们绑定在一个范围(可以是省份、角色、头衔、神器、社会或地图外势力),而不是事件链。这意味着你可以在原始事件链结束后,从不同上下文中继续引用它,比如新事件、社团名称、特质名称(在原版中用来存储负责加冕者的名字,体现在“由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.在与常规事件目标碰撞时,非持久目标优先。

Timers

有时需要测量自某事件发生以来经过的时间,作为另一个事件的条件。

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

修饰符可以作为冷却计时器,让脚本更简单,但限制在角色、省份和领地。

  • 设置一个隐藏修饰符,该修饰符将在指定时间内失效,通过以下方式 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 }

注意:隐藏修饰符不需要本地化,因为它们不会显示。

  • 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

有两个命令可以用来生成随机结果: 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:

  • 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 它废弃了因子为0的修饰符,且无需反逻辑。 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

}

实际概率(如果两者<factor_1> <factor_2> 和条件相符)是: <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

    }

}

在下面的例子中,如果玩家安装了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

    }				

}

Scope chain

为了理解范围链,想象每次游戏引擎在处理脚本块时遇到某个范围,该范围都会被放在堆栈顶部。 一旦评估了该范围中的触发器、命令或嵌套范围,当前范围会从该堆栈中移除,游戏引擎继续处理父范围。

Note that the scope chain is different from the event chain:每次触发事件的效果(如 character_event)在事件或决策中执行时,该事件的基础作用域(即调用者)被添加到事件链中,以便新的事件(即被调用者)可以引用该事件。

Special scopes

嵌套示域时,使用特殊变量引用链中其他望域。这些价值观是无法改变的。

Variable Description
ROOT

Original and lowest scope in the chain:

  • Events:获得事件的实体(角色或省份)。对于 on_action events,根据作用量不同而变化。在极少数情况下,默认作用域(块开头的THIS)和ROOT不同:这发生在 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:发送事件的实体(角色或省份)。只有当某个事件是由另一个事件或决策触发时才有效,以聚焦于该触发点。可以与FROMFROM串联,向上传递事件链(最多4个FROM,根据 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:持有定居点的人物(男爵)。FROMFROM是该庄园(男爵头衔)。
  • job_actions: the ruler
ROOT_FROM

ROOT_FROM is a shortcut for ROOT = { FROM = { } }它允许在FROM示波链中引用当前块的FROM。

Since FROM is relative, using FROMFROM = { FROM = { } } would be equivalent to using directly FROMFROMFROM = {}, which is not what you want. FROMFROM = { ROOT_FROM = { } } 允许应用条件/命令,同时引用FROMFROM和FROM。 例如: FROMFROM = { is_child_of = ROOT_FROM }.

PREV Previous scope used in the chain.

可以与PREVPREV串联,向上级(最多4个PREV)。
注意:PREV、ROOT 和 FROM 都算作范围变更。

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:

  • #1: trigger 不是作用域,而是脚本块(又称函数)的开头。 ROOT 指的是评估该事件的省份。 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 指向该省所有者的最高领主。 PREV points to the owner, PREVPREV points to ROOT.所以这里是对比顶级领主文化和所有者文化的比较。书架看起来像:
top_liege	<- THIS

owner		<- PREV

ROOT		<- PREVPREV

  • #4 上一个区块已经完成,所以现在这个又指向了省份的所有者, PREV points to ROOT.
  • #5 NOT is an operator不是瞄准镜。瞄准镜链保持不变。所以这里是比较地主文化与省份文化。

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 条件可用于检查示波器是否匹配:

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 条件 将使作用域被评估为真,前提是 函数中至少有 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

}

Limit

极限还可用于进一步减少多变体示波器的匹配范围(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 }

}

一个常见的模组错误是试图在触发块内使用限制,这不仅没必要,还会破坏事件。

Preferred Limit

在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

}

这会寻找一个随机附庸,他遵循常规限制(雄心勃勃),但首先会从满足第一个优先限制的人中寻找(是强大的附庸)。如果能找到一个雄心勃勃且强大的附庸,它会执行命令然后结束任务——否则,它会在国王级和公爵级附庸中寻找有野心的附庸。如果没有雄心勃勃的国王或公爵附庸,它就会出现在伯爵级附庸之间。如果没有满足所有偏好范围,它会全部忽略,寻找任何有野心的附庸(此例中通过排除法成为男爵)。

Score Value

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

}

Tooltips

In triggers and effects,工具提示由克劳塞维茨引擎自动生成,以详细总结玩家选择选项时将发生的事情。 例如,当用户将鼠标悬停在阴谋菜单中的某个决策上时,游戏会自动生成一份详细的工具提示列表,显示允许他们做出该决定的条件,让玩家一目了然地看到决策可用前需要做哪些更改——或者为什么他们现在可以选择该决定, 如果他们已经满足所有条件。 同样,当玩家将鼠标移动到事件中的某个按钮上时,结果列表会自动生成,这些效果包含在该选项中。

These automatically generated tooltips usually range from helpful to very 很有帮助,但极少数情况下(尤其是脚本复杂时),他们可能会生成一些不必要的技术提示或杂乱无章、本地化不当,甚至说明与实际内容相反的情况。 为了纠正这个问题,模组作者可以用自己的自定义提示覆盖这些自动生成的工具提示。

可以使用三个特殊条款来更改显示的工具提示,以应对某个效果或条件:

Hidden tooltips

它隐藏了对某个效果或触发器的所有评估,通常是事件的触发,或者只有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 tooltips

自定义提示通过用本地化键的内容替换工具提示内容,简化了一些复杂或难以辨认的触发或效果。

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

}

Conditional tooltips

Added in version patch 2.8,只有当包含的触发器成功评估时,这些工具才会显示提示条件。

conditional_tooltip = {

    trigger = {

	liege = {

	    independent = no

	}

    }

    liege = {

	liege = {

	    will_liege_enforce_peace = no

	    has_liege_enforced_peace = no

	}

    }

}

在上述例子中,只有当封建国不独立时,才会显示其领地的和平条件(比如如果封建者是独立的,当它涉及到不存在的封建时,会出现奇怪的提示)。

这也可用于排除大规模、丑陋的触发链,以提升用户体验——例如,在检查统治者的等级或社会成员身份时,再检查每个等级或社会的具体条件。 每个具体方块都可以检查角色是否属于该等级或该社会成员,因此只显示相关条件。

Tooltip filtering

触发和效果也可以进行过滤以提高可读性。

show_scope_change (patch 2.8)

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.

show_only_failed_conditions (2.8.1)

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决策中被大量使用,只用来展示中国为何拒绝某项提案,而未展示其接受该提案的所有明显理由。

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 它会隐藏提示,但仍然会执行所有必要的提示的定位处理。 该 generate_tooltip = no 参数抑制了这种行为。 注意,这将破坏随机化,抑制event_targets及其他类似行为的定位分配。 该功能在原版中用于抑制复杂“any_”范围的处理,当本地化未向玩家显示时(例如casus belli)。

Combining tooltips

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.

Notes

当一个效果既设置又读取 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.

Scripted 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. 除非你反复使用同一段代码,文件体积变得不必要地庞大或维护不便,否则通常最好保留触发器或效果,而不是移到脚本触发或效果上。

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

}

此后,脚本块可以在代码中调用,就像其他触发器或命令一样,格式为: scripted_block_name = yes

最佳实践是为脚本块制定合适的命名规范,以避免与其他模组重叠,且绝不覆盖原始原版文件,因为没有必要这样做:只需为自己的脚本块创建一个新文件即可。 还要用一个前缀或后缀,表明你的触发/命令是脚本触发/效果,而不是内置的: mymod_helloworld_effect = yes #Paradox naming convention fn_mymod_helloworld = yes #Other naming convention

使用独特的名字非常重要。例如,如果你有一个特性与某个效果方块同名,如果你在方块内引用该特质,它就不会被正确解析。

Scopes

The scopes 脚本块中的全部都等于调用脚本块的上下文——THIS、ROOT、FROM、PREV 等总是与脚本效果最初使用的上下文相同。务必非常小心地在正确的作用域中使用脚本模块,包括整个范围链,否则代码的部分可能会无声失效。 虽然这不太可能导致任何故障,但可能导致无声故障和 logic errors 并且由于使用脚本块的文件与定义脚本块的文件之间的分离度,可能会带来挫折——即使代码在任一文件中看起来正确,如果作用域与预期不同,也可能不正确。

Example

以下脚本效果将角色中所有“原版”教育特质移除:

  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

}

然后可以在任何效果方块中调用,就像它是基础游戏中包含的命令一样:

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 否则自定义提示不会像正常条件下自动生成的工具提示那样正确反转。

Usage

脚本块旨在通过在模组的单一位置放置条件或效果,使代码更易阅读和维护。

如果你在模组中反复测试单一组条件,或者某个效果频繁使用,建议使用脚本块,以便轻松修改和调整行为。使用脚本块还有助于减少因条件或效果而出现的重复行数。

原版代码中有大量预设触发和效果,请注意它们可以在自己的模组中自由使用。

Differences from other languages

擅长其他编程语言的模组制作者需要注意脚本块的以下特性:

  • 函数中没有可传递的参数或其他变量;论证总是单数布尔“是”。 这可以通过在调用函数前设置变量,然后在函数内部检查这些变量来解决,考虑到《列王纪Crusader II》对变量的支持有限。
  • 函数的行为取决于调用该函数的上下文。 该函数除非明确包含新的作用域,否则不会创建新的上下文。 始终确保函数调用时处于正确的上下文。
  • 函数不能返回值——它们表现为“虚函数”。 要“返回”一个值,设置一个标志(例如, set_character_flag) or set a variable (i.e., set_variable)在函数内。 请记住,函数的上下文与被调用的地点相同,因此所有赋值的变量都会保存到调用作用域,并且在函数之外可访问。
  • Scripted blocks are more like non-hygienic macros 而不是合适的函数(类似于批处理文件和其他上下文依赖宏)。

See also

References

相关页面 (81)

Building moddingCasus Belli moddingCommandsConditionsDecision moddingEU4转档器模组制作Event 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事件模组制作作用域修正内阁成员模组制作决议模组制作剧本模组制作单位模组制作历史模组制作另类开局模组制作图形模组制作地图外政权模组制作地图模组制作头衔模组制作奇观模组制作宗教模组制作定义宝物模组制作宣战理由模组制作家族模组制作小地图模组制作建筑模组制作指令控制台指令政体模组制作故障排除文化模组制作朝贡国类型模组制作本地化条件模组制作