开发日志#164-疯狂模组星期一
Victoria3官方
2025年11月10日 23:00
收录于文集
共42篇
维多利亚3

【译者按:本开发日志仅描述游戏中的机制与内容,请勿过度解读。】

大家好,周一快乐!系统设计师Tunay在这里报告,我已被允许离开矿洞,为大家带来本次更新中与模组制作相关的重点。我们有不少重要的内容,所以话不多说,直接开始。

数据库接入模式

让我以一个坏消息开始本篇日志吧,本次更新将使模组损坏。我想强调这句话,所以让我重复一遍:

本次更新将使模组损坏

等等,先别提刀!我需要先解释一下原因。你们可能曾读过Tinto Talks#85-模组制作​h或是已经开始尝试制作EU5的模组了!

EU5中,我们的脚本语言得到了一次重大更新,开发了数据库接入模式。这个功能允许你决定一段脚本要如何与已存在的(游戏中的或是其他模组中的)脚本共同作用,通过一系列新的关键字

这一功能将加入Victoria 3。也因此,新版本更新后,你将需要根据强制要求的新语法调整你的模组。

如果各位没有了解过这个功能,让我举例说明这一个系统:

首先,假设我现在想调整征召农兵这一法律,以体现乡村民众利益集团的精神与力量。我可以在该法律的键值前使用INJECT关键字,该关键字可以将脚本内容插入现有的法律中,而非取代它。

脚本写法

 

游戏内效果

 

你可能发现了,将修正插入现有的内容会计算为加法。加入原有内容中有+25%威望的修正,你又inject了一个+50%威望的修正,那么最终结果会是+75%威望。

 

新的关键字一共有六种,为了方便,我将它们全部列于此处:

# code

INJECT:key #此模式下,代码将尝试添加进已有内容。若不存在对应key,则报错。

REPLACE:key #此模式下,代码会替换掉已有内容。若不存在对应key,则报错。

TRY_INJECT:key #此模式等同于INJECT,但若不存在对应key,不会报错。

TRY_REPLACE:key #此模式等同于REPLACE,但若不存在对应key,不会报错。

INJECT_OR_CREATE:key #此模式等同于INJECT,但若不存在对应key,会创建新的内容。

REPLACE_OR_CREATE:key #此模式等同于REPLACE,但若不存在对应key,会创建新的内容。

# code end

使用正确的关键字能够避免许多兼容性问题,也能更容易与现有的模组进行兼容性适配——如果你和同行们热烈的交流的话。

请注意,您必须明确调用这些关键字。代码不允许隐式替换,所以如果您有时间,记得更新一下代码!

以下是这个新系统生效的数据库:

# spoiler

acceptance_statuses

ai_strategies

amendments

battle_conditions

building_groups

buildings

buy_packages

character_interactions

character_templates

character_traits

cohesion_levels

combat_unit_groups

combat_unit_types

combat_unit_experience_levels

commander_orders

company_charter_types

company_types

country_creation

country_definitions

country_formation

country_ranks

country_types

culture_graphics

cultures

decisions

decrees

diplomatic_actions

diplomatic_catalyst_categories

diplomatic_catalysts

diplomatic_plays

discrimination_trait_groups

discrimination_traits

dna_data

dynamic_company_names

dynamic_country_names

dynamic_country_map_colors

dynamic_treaty_names

technology

flag_definitions

game_concepts

geographic_regions

goods

government_types

harvest_condition_types

ideologies

institutions

interest_group_traits

interest_groups

journal_entry_groups

journal_entries

law_groups

laws

legitimacy_levels

liberty_desire_levels

military_formation_flags

mobilization_option_groups

mobilization_options

objective_subgoal_categories

objective_subgoals

objectives

parties

political_lobby_appeasement (factor)

political_lobby_appeasement (reason)

political_lobbies

political_movement_categories

political_movement_pop_support

political_movements

pop_needs

pop_types

power_bloc_coa_pieces

power_bloc_identities

power_bloc_map_textures

power_bloc_names

power_bloc_principle_groups

power_bloc_principles

prestige_goods

production_method_groups

production_methods

proposal_types

religions

social_classes

social_hierarchies

map_data/state_regions

state_traits

strategic_regions

subject_types

terrain_manipulators

terrain

themes

tutorial_lessons

tutorial_lesson_chains

labels

war_goal_types

alert_groups

alert_types

commander_ranks

scripted_buttons

scripted_progress_bars

treaty_articles

gfx/map/city_data/city_building_vfx

gfx/map/fleet_dioramas

gfx/map/fleet_entities

gfx/map/army_dioramas

gfx/map/front_entities

state_regions

sound/persistent_objects

music

notifications

modifier_type_definitions

ethnicities

script_values

scripted_guis

scripted_lists

scripted_modifiers

gui_animations

achievements

modifier_icons

gfx/portraits/accessories

gfx/portraits/portrait_modifiers

# spoiler end

建筑组清理

说到会导致模组损坏的事情:

首先是,我们重命名了一系列建筑,在版本更新后你应该可以很快确认这些变化,并通过大规模替换解决问题。此前,有一些建筑键值明明不规范,我们决定让一切更一致。

其次是,地区资源现在按照建筑类型操作,而非建筑组。让我们举例说明,在map_data/state_regions中,定义发生了以下变化:

新写法:

# code

arable_resources = { "building_wheat_farm" }

capped_resources = {

building_fishing_wharf = 4

}

resource = {

type = "building_oil_rig"

undiscovered_amount = 40

}

# code end

旧写法:

# code

arable_resources = { "bg_wheat_farms" }

capped_resources = {

bg_fishing = 4

}

resource = {

type = "bg_oil_extraction"

undiscovered_amount = 40

}

# code end

此改动意图消灭那些仅在资源定义中被用到的建筑组。现在,每种农场不再属于各自的建筑组,而是属于一个bg_staple_crops建筑组。被移除的建筑组仍被记录在数据库中,但已不再被使用,并将在1.13版本被彻底移除。我们也增加了临时方案,当你输入建筑组时,代码会尝试读取建筑组的默认建筑类型。该临时方案也将在1.13版本删除,所以我们依然推荐更新你的模组。

可模组化战争目标

无论如何,我们要进行下一个主题了:脚本化战争目标类型

我们清楚这是你们中的许多人很久以来一直期待的东西。我十分高兴的宣布,实现了。我们新建了一个数据库,存放在common/war_goal_types文件夹中。

当更新上线后,我们此前的战争目标都将被放在这个文件夹中。不过需要说明的是,很多原版的行为都是由代码编写的的,所以在进行修改时一定要加倍小心。

# code

some_war_goal = {

icon = "gfx/interface/icons/war_goals/icon.dds"

### List of Kinds

# annex_country

# ban_slavery

#  Will convert to law commitment treaty article for slavery banned

# colonization_rights

# conquer_state

# contain_threat

# enforce_treaty_article

# force_nationalization

# foreign_investment_rights

#  Will convert to investment rights treaty article

# humiliation

# increase_autonomy

# independence

# join_power_bloc

# leave_power_bloc

# liberate_country

# liberate_subject

# make_dominion

# make_protectorate

# make_tributary

# open_market

# reduce_autonomy

# regime_change

# return_state

# revoke_all_claims

# revoke_claim

# secession

# take_treaty_port

#  Will convert to treaty port treaty article

# transfer_subject

# unification

# unification_leadership

# custom

#  No predefined effect. In case you only want to execute the on_enforced effect, but nothing else.

kind = war_goal_kind # The Kind of the war goal defines the code-side predefined package of behavior that war goal will have, primarily defining what effect the war goal has when executed.

### List of settings

# require_target_be_part_of_war

#  Target country has to be in the war, can't target neutral countries

# can_add_for_other_country

#  Allows adding the goal for other participating countries

# annexes_entire_state

#  Flag for if the goal is expected to always annex the entire target state. This is used to calculate conflicts with other goals

# annexes_entire_country

#  Flag for if the goal is expected to always annex the entire target state. This is used to calculate conflicts with other goals

# country_creation

#  Flag for if the goal creates a new country

# overlord_is_stakeholder

#  Flag for if the stakeholder of the war goal should be the overlord rather than the target country itself

# can_target_decentralized

#  If the war goal can target decentralized countries

# has_other_stakeholder

#  If the war goal has a different stakeholder than the target itself

# turns_into_subject

#  If the war goal turns the target country into a subject. For conflict resolution purposes

# skip_build_list

#  If the war goal should be available to be picked in the diplomatic play or not

# targets_enemy_subject

#  If the war goal should target an enemy subject specifically rather than all enemies in the war goal

# targets_enemy_claims

#  If the war goal should target the claims of a country, rather than the country itself

# requires_interest

#  If the war goal requires you to have an interest in the relevant strategic zone

# debug

#  No effect, used for code debug purposes.

# validate_subject_relation

#  Validation behavior that checks if the resulting subject relation of this war goal is valid

# validate_formation_candidate_self

#  Validation check to make sure the goal holder is a formation candidate

# validate_formation_candidate_target

#  Validation check to make sure the goal target is a formation candidate

# validate_sole_formation_candidate

#  Validation check to make sure the goal holder is the only formation candidate

# validate_target_not_treaty_port

#  Validation check to make sure the target state is not a treaty port

# validate_join_power_bloc

#  Special validation for join power bloc war goal kind

# validate_colonization_rights

#  Special validation for colonization rights war goal kind

# validate_force_nationalization

#  Special validation for force nationalization war goal kind

# validate_foreign_investment_rights

#  Special validation for investment rights war goal kind

# validate_regime_change

#  Special validation for regime change war goal kind

# validate_contain_threat

#  Special validation for contain threat war goal kind

# validate_revoke_claims

#  Special validation for revoke claims war goal kind

# validate_increase_autonomy

#  Special validation for increase autonomy war goal kind

# validate_take_treaty_port

#  Special validation for take treaty port war goal kind

# validate_independence

#  Special validation for independence war goal kind

# validate_conflicts_war_goals_holder

#  Validate conflicts with war goals of the same type from holder

# validate_conflicts_war_goals_all

#  Validate conflicts with war goals of the same type from all participating countries

# validate_conflicts_conquer_state

#  Validate conflicts with war goals that conquer states (i.e. that have the conflicts_with_annex_state)

# validate_conflicts_annex_country

#  Validate conflicts with war goals that annex countries (i.e. that have the conflicts_with_annex_country)

# validate_conflicts_make_subject

#  Validate conflicts with war goals that make new subjects (i.e. that have the conflicts_with_make_subject)

# validate_conflicts_existing_subject

#  Validate conflicts with war goals that make new subjects (i.e. that have the conflicts_with_make_subject)

# conflicts_with_make_subject

#  Marks the war goal as potentially conflicting with make subject war goals

# conflicts_with_country_creation

#  Marks the war goal as potentially conflicting with country creation war goals

# conflicts_with_annex_country

#  Marks the war goal as potentially conflicting with annex country war goals

# conflicts_with_annex_state

#  Marks the war goal as potentially conflicting with annex state war goals

# conflicts_with_existing_subject

#  Marks the war goal as potentially conflicting with existing subject war goals

settings = { # Settings further customize how the war goal is treated in different checks. A war goal can only have one kind, but multiple settings.

setting_1

setting_2

}

execution_priority = 80

### List of Contestion Types

# control_target_state

# control_target_country_capital

# control_any_target_country_state

# control_any_target_incorporated_state

# control_own_state

# control_own_capital

# control_all_own_states

# control_all_target_country_claims

# control_any_releasable_state

contestion_type = control_type

## Target Type

#What kind of entity the war goal primarily "targets". This primarily defines how the game generates potential alternatives for each war goal type when selecting one from the diplomatic play panel. Most war goal kinds will require a specific target type to work well and can't be changed (i.e. Conquer State can't have a Treaty Article target type). This field primarily allows you to have custom war goals target different entities.

### List of Target Types

# Country

#  Loops over enemy countries to generate war goal alternatives

# State

#  Loops over states belonging to enemy countries

# Treaty Article

#  Loops over article types and then enemy countries

target_type = target_type

possible = {

# trigger to determine if a goal with its target data is listed when selecting a war goal in the diplo play panel

# scopes: root = holder, creator_country, diplomatic_play, target_country, target_state, stakeholder, target_region, article_options

}

valid = {

# trigger in addition to some basic validation code-side

# scopes: root = holder, creator_country, diplomatic_play, target_country, target_state, stakeholder, target_region, article_options

}

maneuvers = {

# script value

# scopes: root = holder, creator_country, diplomatic_play, target_country, target_state, stakeholder, target_region, article_options

value = 10

}

infamy = {

# script value

# scopes: root = holder, creator_country, diplomatic_play, target_country, target_state, stakeholder, target_region, article_options

value = 15

}

on_enforced = {

# script effect on top of the predefined code effect

# scopes: root = holder, creator_country, diplomatic_play, target_country, target_state, stakeholder, target_region, article_options

}

}

# code end

无上下文日志条目

无上下文日志条目,或称“全局日志条目”,是一种新的日志条目标记类型。在我们谈到这个功能的有趣之处之前,我还是不得不先遗憾的警告大家,本次更新将使模组损坏

你需要为你的日志条目组(common/journal_entry_groups)设置context来修复模组。你需要为每个日志条目组添加一行代码“context = none/country”。在多数情况下,如果你的日志条目绑定在国家上,那么此处就应该输入country,而如果输入为none,那么意味着这个日志是一个全局日志条目。

全局日志条目的撰写方式与其他日志条目类似,不过拥有一系列新的触发器与效果,以满足其同时被多个国家分享的需要。总的来说,这应该很容易理解,但它是一个非常酷且令人兴奋的功能,我们今后会经常使用,希望你们也会喜欢!

秘鲁-玻利维亚联邦日志条目得到了更新,成为全局日志条目。

 

下面是一个全局日志条目的例子:

# code

# Sample JE for global journal entries

je_global_test = {

icon = "gfx/interface/icons/event_icons/event_map.dds"

group = je_group_global_test

is_shown_when_inactive = {

always = no

#always = yes

}

modifiers_while_active = {

lippe_crisis_ongoing

}

scripted_button = je_global_test_button

scripted_button = je_global_test_button_2

should_be_involved = {

OR = {

country_rank >= rank_value:great_power

country_definition = cd:LIP

country_definition = cd:SCM

}

}

should_show_when_not_involved = {

OR = {

country_rank >= rank_value:major_power

country_definition = cd:LIP

country_definition = cd:SCM

}

}

possible = {

always = no

}

complete = {

any_country = {

count < 1

OR = {

country_definition = cd:LIP

country_definition = cd:SCM

}

}

}

fail = {

any_country = {

count > 2

OR = {

country_definition = cd:LIP

country_definition = cd:SCM

}

}

}

immediate_all_involved = {

if = {

limit = { exists = c:LIP }

change_relations = { country = c:LIP value = -10 }

}

if = {

limit = { exists = c:SCM }

change_relations = { country = c:SCM value = -10 }

}

}

on_become_involved_after_activation = {

if = {

limit = { exists = c:LIP }

change_relations = { country = c:LIP value = -10 }

}

if = {

limit = { exists = c:SCM }

change_relations = { country = c:SCM value = -10 }

}

}

on_no_longer_involved = {

if = {

limit = { exists = c:LIP }

change_relations = { country = c:LIP value = 10 }

}

if = {

limit = { exists = c:SCM }

change_relations = { country = c:SCM value = 10 }

}

}

on_complete_all_involved = {

add_modifier = { name = lippe_crisis_averted years = 10 }

}

on_fail_all_involved = {

add_modifier = { name = lippe_crisis years = 10 }

}

on_timeout_all_involved = {

add_modifier = { name = lippe_crisis years = 10 }

}

on_weekly_pulse = {

effect = {

scope:journal_entry = {

every_scope_je_involved = {

limit = {

NOR = {

country_rank >= rank_value:great_power

country_definition = cd:LIP

country_definition = cd:SCM

}

}

scope:journal_entry = { remove_involved_country = prev }

}

}

scope:journal_entry = { remove_involved_country = c:FRA }

scope:journal_entry = { add_involved_country = c:ARG }

}

}

weight = 1000

timeout = 1825

should_be_pinned_by_default = yes

}

# code end

地理区域

另一个新功能是地理区域,它是可以被任意定义且固定的脚本列表,可以被任何触发器或是检查引用。

地理区域也会在地图上高亮显示!

 

地理区域的添加非常简单,你只需要在common/geographic_regions文件夹创建一个文件,然后把列表写进去就好。

一些中亚地区的新地理区域。

 

其完整定义如下:

# code

geographic_region_key = {

# All state/strategic regions are added together to form a geographic region

# the key to access this in script lists i.e <any/..>_<state/...>_in_europe

short_key = "europe"

# The Strategic Regions this geographic region encompasses

strategic_regions = { sr:<key> sr:<key> sr:<key> }

# The State Regions this geographic region encompasses

state_regions = { <STATE_REGION> <STATE_REGION> }

}

# code end

区域性生产方式

与地理区域紧密相连的新功能区域性生产方式向大家问好。该功能允许你定义一种生产方式,该生产方式仅对世界的一部分生效。这种方式其实类似于一种“建筑变体”,比如潮湿咖啡种植带?

请注意,功能仍处于制作中,数值可能发生变化哦!

尽管大部分区域型生产方式只影响很小的地区,不过潮湿咖啡种植带占据的面积还是挺大的。

文化性自定义姓名顺序

现在,你可以通过一个文化数据库中的标记定义名字的展示顺序。默认情况下,显示方式被定义为first_last,但你也可以把它设置为last_first,意味着你可以把姓氏放在前面,把名字放在后面。

朝鲜是一个姓前名后的国家。图片中是王大妃摄政金氏(月姬:图中的名字是金明敬,来源于尊号),等等,她之前在这里吗?

 

我们已将此系统应用于世界各地我们认为适用的文化。老实说,你们当中有多少人知道匈牙利语传统上也是把姓放在前面?(月姬:致敬传奇模组汉语圈与匈牙利文化姓前名后。)

控制台

控制台按钮也面貌一新,许多常用命令和作弊码的按钮也得到了快捷按钮。

眼尖的玩家可能还发现了新的Inspect Country按钮,点击该按钮会为当前选中/观察的国家打开脚本运行器窗口。你也可以通过控制台指令打开观察器,例如,“inspect_country GBR”会打开英国的窗口。

看看我的按钮吧,伟大的兄弟们!

受CK3启发,我们也在其他地方添加了一些debug按钮,如右键杀死一个所选角色。仅仅是为了测试目的哦!

我们计划在未来添加更多这类按钮。

欢迎我们的新雇员!

假修正

在漫长的等待过后,我们加入了定义仅脚本修正的功能——或者叫假修正更容易让人理解。在common/modifier_type_definitions中的文件定义修正时,你可以添加一个“script_only = yes”标记,将修正标记为假修正。这么做可以抑制报错,拯救你的C盘。

法律修正案

上一篇开发日志中介绍的法律修正案功能也是完全可模组化的!你可以在common/amendments数据库中自定义新的修正案,随后通过新的效果添加至法律中。

我还是要把示例放在下方:

# code

amendment_test = {

parent = law_peasant_levies # optional reference to a law that dictates what stances IGs and movements have toward this amendment

allowed_laws = { # list of laws that the amendment can be added to

law_professional_army

law_national_militia

law_mass_conscription

law_census_voting

}

modifier = {}

tax_modifier_very_low = {}

tax_modifier_low = {}

tax_modifier_medium = {}

tax_modifier_high = {}

tax_modifier_very_high = {}

possible = {

always = yes

}

can_repeal = {

  always = yes

   }

ai_will_revoke = {

always = yes

}

}

# The effect:

# add_amendment

# Adds an amendment to the scoped law.

add_amendment = {

type = amendment_example

sponsor = interest_group

cooldown = 120 # months

timeout = 240 # months (optional, 0 = none)

}

**Supported Scopes**: law

# code end

结语

回过头一看,确实挺多的,但其实还没结束!如往常一样,我们还新增了一系列新的触发器、新的修正类型、效果,并对模组制作进行了小型改进。不过考虑到篇幅问题,我还是决定把剩下的部分留到更新日志时再说。

再次感谢大家一直以来的支持,也感谢大家创作的令人惊叹的模组!:)

本周四,你们将看到下一篇开发日志。日志将讲述西班牙在1.12版本与在Iberian Twilight中得到的更新。不见不散!