开发日志#30:事件脚本详解

贡献者 · 更新时间 2020-06-24 18:31
开发日志#30:事件脚本详解
原标题 Crusader Kings 3 Dev Diary #30 - Event Scripting
原发表日期 2020年6月9日

开发日志#30:事件脚本详解是十字军之王3中的开发日志

欢迎同志们收看另一篇开发日志!今天我们将来康康CK3中一个“事件”的结构,它们在脚本中是如何编写的,以及它们是怎样与CK2中相区别的。

  1. 事件解剖
  2. 事件主题与背景
  3. 肖像与动画
  4. on_actions用于所有事件!
  5. 即时代码块与你
  6. 选项:给AI以人格,予玩家以压力
  7. 你...你不打算讲讲触发器吗?

事件解剖

一个标准的角色事件的核心对你而言已经基本上是很熟悉的了:我们有一个标题,一个描述,常常有一个肖像,以及在底下有一个或更多个选项。

但是在脚本中,我们稍微更改了一小部分东西,希望随着时间的推移来改进功能性、可读性和常规脚本的健康度。以下是关于CK2和CK3事件开始的对比:

你首先会注意到的改变是我们将事件类型和事件ID进行了调换:一个事件现在将会被一个命名空间(仍然在每一个文档的最顶端被定义)和一个单独的ID所创造,并且事件类型在事件中被定义,而不是相反的情况。这意味着,当事件本身折叠了起来之后你仍然可以看到事件的ID!

其次,我们更改了触发文本的运行方式。在CK2里,这是一个非常有效的工具,来保证玩家能够根据自身的情况来显示本地化文本,以创造独特风味,能够让我们将事件制作得既有普适性又独树一帜。但是,这将会带来一点麻烦。因为我们没有简单的方法来让触发文本之间彼此独立,这就导致了下面这种情形:

倒也不是世界上最糟糕的,但与我们实际上预想的情况相比也太捉鸡了。

在CK3中,我们通过在触发文本块中使用first-valid块(正如第一张图片所示),从满足一组条件的列表中选择第一个条目来减少此操作。这意味着,不必确保触发文本块始终互斥地依赖一个触发器而运行(而且该触发器还会随着触发文本在数量上的增加变得越来越复杂),而是我们可以只是更有逻辑地通过简单的触发器来选取我们想要的文本。

举例来说,如果我手上有一个触发文本,其副本相对于一个法国的角色、一个阿什阿里派角色和一个超过80岁的角色彼此都不相同,还有一个为了不符合以上这些分类的任意角色准备的兜底文本,我将编写如下脚本:

这个list将会自动运行到底,一个法国的角色能看到其中之一,一个非法国的阿什哈里将会看到另一个东西,一个非法国也非阿什哈里但是年龄超过八十岁的角色将看到第三个东西,其他所有人将看到第四个东西。这使得给事件描述和标题添加新的关联副本变得非常容易。

另一个需要注意的小问题是,由于触发文本现在也保留在上级块的主体中,因此即时排序要容易得多:无论触发文本有多少,只需单击一下即可将主体折叠到文件中选择的编辑器,不需要再一顿操作了。在减少操作量这件事儿上任何微小的工作都是有意义的。

事件主题与背景

图片块和边框块是CK3脚本格式中几个值得注意的缺失。

但这也是有原因的!这些内容大部分都包含在新的事件主题系统中,你可以在上面的第一个CK3脚本示例中看到。

主题是被那些放置在左侧顶端的事件图标所决定的,它们将主题宽泛的成组事件聚合在一起,让玩家知道他们所期待的与事件相关的内容是什么。它们也赋予我们一个适当的默认背景(其本身也作为角色肖像的背景灯光被使用),随着你的状态和不同的SFX值进行改变

如果我们觉得有一个更加合适的背景和主题图标要进行呈现,在必要的时候也可以通过手动编写脚本来进行覆盖它们。因此,即便整个系统是被设计为减少对每一事件添加额外特质的工作量,我们也能够对我们事实上需要事件呈现为哪种风味有全权的把控。

更不必说,事件主题是完全可编写的:你可以随你喜欢在Mod中改写任意一个图标、默认背景、角色模型的背光和SFX值,你也可以很简便地创设和适用事件主题。

肖像与动画

好消息!相比于CK2,在新系统中的肖像某种意义上可动性增强了,且任意一个事件都可以呈现0-5个肖像。其中的两个是完全动画的(放置在左侧和右侧),三个是排列在事件底部的头像。你可以按你喜好任意组合这些图像,来让事件有一个合适的外观。

左侧和右侧的肖像可以使用任意相当丰富的动画来创建和发布。头像是非动画的,但它们允许你将事件描述中提及的次要角色进行可视化呈现。

on_actions用于所有事件!

所有的事件都来自于某处,在CK2中(尤其是在这个游戏生命周期的早期),是通过“平均发生时间MTTH”的系统来实现的。这让我们能够简单定义一个事件(通常是)在几个月的时间内能够被触发。不幸的是,系统在极大数量的事件之间寻求平衡的时候,其灵活性就变得前所未有的不利。这使得在没有严格的触发器的时候很难把控事件出现的频率。由于机制纯粹建立在概率上,这同时也造成了很多奇怪的数据异常和全然失控的行为。

在CK2的漫长开发中,我们开始向着通过on_actions来触发事件的方向发展,即在与特性相关、或是与规则的时间段有关的代码中包含的小钩子来激活事件(在必要情况下也可以激活效果),无论这个钩子被称为什么。为了平衡这些东西需要付出更多的工作量,但是它们能够让我们对调用事件的时间和方式有更好的把控,也让对这些复杂事物的调整工作变得更加容易。在性能上则变得非常高效。

在CK3中,我们完全使用on_actions来激活事件。代码中有相当多的相互勾连,允许我们在以下条件下触发事件:当一个特定的行为在世界中发生时(例如,当一个角色出生时)或在一个规则的时间段(例如,每五年)。可以将这些设置为在每次on_action触发时关闭,或放置在可能触发的潜在事件的加权列表中(在这种情况下,权重乘数仍然适用并支持常规变数等)。

关于CK2的on_actions方面的的一个主要改进(有一个更完备的合理的使用系统让我很兴奋,但我依然欣赏那些生活过得比我更有意思的人,他们也许有更严格的标准)是增加了可脚本编写的on_actions的。这允许我们(当然也有你们!)完全在脚本中创建和连接on_actions,而不必总是依赖于硬编码的on_actions。脚本编写的on_actions将会与硬编码的on_actions行为一致,作为加权和非加权事件/效果的复合,只不过可以从可访问脚本的某个地方调用,而不是从难以访问的代码中调用。

进一步来讲,假如我已经针对“内战后的和解”设置了一组新的事件,邻居们在各自站队互相争战后也学会了共同生存,而我希望这一类事件每当内战结束就被钩取出来。我要做的就只是在相关的战争结束的效果中添加这样的东西:

接着在合适的目录下创建一个含有以下代码的文档:

只要你能在某处编写一个效果,你就能够编写一个与之关联的新的(或者是现有的)on_action。

即时代码块与你

脚本列表是CK 脚本的一项主要新增功能,我们在即时代码块中极其规范地使用它。

这允许我们在不同的群组中进行排序,从中选择符合条件的角色,之后又可以简单地在其相关角色的列表中进行分类筛选。

举例来说,我想从我所有的封臣和廷臣中获取所有讨人厌的老人,我会模糊地写这些东西:

之后,我想从他们之中选出最愤怒和最讨厌的,这样我们就能让他们在一个事件或者随便什么事情之中陷入争论。

筛选好了!之后我就能给这两名角色加上本地化文本、对他们适用效果、把他们设定成肖像角色,等等。你也许注意到我们超酷的新功能alternative_limit:这些是紧接其上方的限制失败时执行的检查。

换句话说,我们在最大程度地减小不必要的维护,以及在潜在的bug存在前消除他们。这一脚本仍有个问题敲响警钟,它在一个单独的脚本触发器中用相同的条件触发两个单独的列表。

我有几种方法可以解决此问题,但先让我们通过ordered_in_list效果展示一些新功能:

Ordered_in_list会取得一个列表,然后用一个名为script_maths的系统(希望我们能另找时间讨论它),为该列表中的项目分配数值。之后,它将代码块中的效果适用于默认情况下列表中数值最高的项目(尽管我们也可以让效果适用于列表中任意数目的对象)。在这里我们仅仅通过相当有限的因素对相对较小的项目进行筛选,但这种筛选功能可以根据你的需求变得复杂而广泛。

还有其他与即时代码块相关的消息,我们还让保存作用域(就是以前的事件目标,你可以在之前的例子中看到一些)和变量变得更容易。他们好比音乐上的引子,我们惯于利用他们在一场戏剧中去营造最强的戏剧性。标准即时代码块(在显示事件之前执行)保持不变,即时代码块中的可见效果会在所有的事件选项提示栏中显示为“已发生”。

选项:给AI以人格,予玩家以压力

最后来谈谈选项。选项的行为和CK2很相似,每个可见事件都至少有一个选项,而每个选项都需要一个文字标签。但除此之外你可以加入任何你喜欢的花哨效果。

你会注意到,此区域两项主要的新增内容,是使用ai_chance和很常见的stress_impact,他们的使用范围大大扩张。

AI 机率,就像CK2里一样,控制着NPC角色选择那个选项的大概概率。在CK3中,我们让这一代码块更有扩展性,并且暴露了ai_value_modifiers(为每个角色基于他们相应数值的多少构建了他们的性格,这是反过来由他们的特质得出的),也让每个事件选项多少权衡了这一数值,以确保角色在几乎所有事件选项中都尽可能基于他们性格行动。这个代码块也依然接受其他的触发器,这样我们就能让AI或多或少基于其他因素做决定,比如他们的特质,他们在打仗,或者选项和宿敌有关,等等。

stress_impact,另一方面,是我们组织新的压力机制的方式。你也许还记得好几篇日志之前的内容。如果你不记得,也别感到有压力(叹气)。概括地说,压力用来衡量你的角色在执行与他们的性格相反的举动时获得的负面影响(举例来说,有同情心的角色不会享受折磨他人)。

选择特定选项时任何与特质相关东西的都被我们填进了stress_impact代码块,并以此在这里做检查,使用了脚本化编写的stress_impact数值。由于不可思议的自动化魔法,我们能在这里合并任何stress_impact代码块中的压力增减,他们会在事件选项中计算为一个数字。

你也可以在stress_impact代码块外将压力作为一个普通效果添加。在这种情况下,它并不会合并。如果你想,你也可以增加多个压力影响代码块,在这种情况下,每个代码块会独立合并各自的压力修正。或者,你也可以完全省略这个代码块。无论如何,你的脚本之船都能漂浮其中。

至此,我们到达了另一篇开发日志的结尾。我会待几小时来回答你可能遇到的问题,我们期待着下次...

你...你不打算讲讲触发器吗?

哈,你被我出其不意的转折给骗了。如果你注意到之前截图里藏匿着的触发器剧透,就把手举起来。你赢得一点互联网积分!

至于其他人,再来看看这张截图。

现在,快速回顾一下:CK2中的脚本触发器是一种将很长的需求列表组合到同一个引用的办法,之后在需要检查事项的时候就会引用这个引用。

举例来说,假设我在一个事件中有两个地方需要检查20个以上的条件:如果我们把所有的条件写两次,事件会完美地运行。但假如将来有个人只更新了其中一组触发器,但没改另一组?这会立即产生Bug。现在,假如我必须检查那些触发器两次以上,又或者他们跨越了多个事件,又或者,在最悲观的情况下,跨越了不同文件中的多个事件?

就像你想的,这迅速演变为混乱。但是,我们不想去用复杂度更小的触发器,部分是因为这会让游戏变糟糕,也让所有人更不开心(如果没有那些多到可笑的特殊事件,这还是CK吗?),部分是因为这仅仅让问题变小而不是让问题得到解决。

取而代之的是,我们创建了脚本触发器,这是在一个文件中一次写出的触发器列表,能够被其他文件引用。然后,在任何需要检查那些触发器的地方,我们都调用这个脚本触发器,这将检查那些内容。每当触发器需要更新或修复时,只需简单地修复脚本触发器一次,而通常后续检查这个脚本触发器的地方也通过这一代理修好了。马上节省了维护的时间,急剧减少了潜在的Bug!

然而,在CK2中,这些脚本触发器必须存储在/common下的特定文件夹内,与引用它们的事件(和其他脚本)分开。这听起来可能不算什么大事,但是它在创建和维护脚本触发器时增加了一些额外的工作量,而且通常只给你一个巨大的脚本触发器列表,其中一些可能只在几个地方用了几次。

在CK3中,我们通过添加行内脚本触发器对此进行了改进,这意味着可以将脚本触发器直接写入使用它们的文件中,前提是这些触发器仅在该文件中使用。对于需要在多个文件中使用的更加实用的脚本触发器,旧的文件夹系统仍然有效。这样一来,我们就可以拆分主要的和次要的脚本触发器,并且让使用脚本触发器(以及脚本效果)贯彻了整个游戏始终,让我们(还有你!)明显地更容易去创建既不会影响复杂性或细节、又易于维护的脚本。

这样。我们就来到了本期日志的真正结尾。我们下周再见!


翻译来自牧游社(翻译:飞星穿月 Yakuky 校对:三等文官猹中堂)