欢迎光临散文网 会员登陆 & 注册

认识你与event的极限【Stellaris Events Modding 进阶教程#1】

2023-03-08 13:26 作者:群星中文Modder协会  | 我要投稿

事件究竟能做什么?

大家好,还是我XSkiper,嘛这边应该是叫画饼大师什么的()

总之继续搬教程~

事件,events文件夹里那些成堆的txt文件,究竟能改变这个游戏到何种程度?

这个问题,我曾经以为不会被接触事件不久的萌新遇到,但我发现,如果避开这个问题......就会使很多modder在事件写作中产生了错误的判断,做很多无用功,或者在群里引来高血压。

所以在进一步接触事件写作之前,让我们还是摆正心态,了解一下蠢驴游戏的半壁江山(以上)——events的基本结构,以及它能够为我们带来的东西

1 我是谁,我在哪?——scope

scope,也就是范围,相信在无言的教程中大家已经有了一定的接触。但对scope这个概念,恐怕各位还是没有一个比较直观的印象。

scope的概念是蠢驴语言的一个核心,如果对其没有一个清晰的印象,modder是无法写出较为复杂的事件的。

那么,如何理解scope在蠢驴事件中的作用呢?

大家日常的对话通常包含两个重要部分:主语谓语。例如萌新A写码,就分为萌新A这个主语,以及写码这个谓语。蠢驴的绝大多数效果语句也是相同的逻辑。

类比萌新A写码这句话,我们不妨看一下下面这个事件。

范例1-1 天灾?AWSL!

由隔壁舰R mod中某联合舰队掀起的天灾热,以相当快的速度席卷了整个modder圈,很多小mod制作者想要制作这样一个天灾以扩大订阅量。

然后我们的主人公——某个萌新modder也想要写一个强力无比的天灾,他想到:“只要把玩家灭国,天灾就无敌了!”最后写下了这个“天灾”。

图一 范例1-1代码雏形

很简单的一个事件:国家A触发该事件,国家A选择选项,国家A暴毙。wdnmd毫无游戏体验


图二 范例1-1不讲武德

相信经历了其他教程(大概这个号还没发),大家都明白了trigger、immediate、option的作用......不过我还是在这里稍微复习一下:

  • id:事件的名字,调用时就靠它,命名规则就不说了。

  • title&desc:事件的标题&描述,通常是本地化key,游戏内显示对应语言对应key的本地化文本。

  • is_triggered_only:仅由触发器触发,若这个属性为yes,除非有其他的东西(例如另一个事件,或者控制台语句)去触发它,你是不会在游戏中看见它的。

  • trigger:触发条件,如果trigger内的条件不满足,这个事件怎么都不会触发的(控制台除外),内部填充condition

  • immediate:即时效果,在事件触发瞬间就生效的效果,内部填充effect

  • option:事件选项,name属性为选项的名字(本地化key),除了部分属性语句,内部填充的也是effect,与immediate的不同在于其在选项被选择后才生效。

在这里先介绍这些,毕竟只是复习(笑),而且这些属性定义永远是实操比看教程更容易懂。

类比萌新A写码,我们可以说这个事件执行的主要效果就是国家A暴毙。事件代码中的②destroy_country = yes是一个effect(接下来会提到),代表着暴毙这个动作。那么,如何确定暴毙的国家是国家A,为什么这个事件里暴毙的不是国家B国家C

答案藏在①country_event中。这代表这个事件是一个国家事件,这个事件的scope(ROOT)就是看到这个事件弹窗的,触发这个事件的国家A。在没有经历任何scope跳跃时,所有该事件的判断和效果都关于国家A本身。

destroy_country = yes这一语句没有经历scope跳跃,直接在scopeROOT执行,因此暴毙的就是国家A了。

图三 只有一个目标的effect

由此说来,scope可以比喻成一种主语,它代表着所有效果的目标,有了scope,准确地在2200上天的才会是你(而不是住你隔壁的蟑螂兄弟),圆神才能准确地清算你的每家每户(而不是误杀了对面的海星)。

当然这显然不是scope的全部,我还并没有说明如何进行scope跳跃,以及很多关于scope的,别的操作。但作为一个概念理解,绝大部分scope的相关内容都是由我上边的话延伸。我们可以先暂停在此处,更详细的问题留到下一章进行说明。

2 老三样——effectcondition以及modifier

说到蠢驴的代码,最多的就是这三个东西,在自定义程度较高的代码(不止事件!),几乎都能找到这老三样。

下面我借用范例1-1,简要介绍一下老三样的前两个:effectcondition的作用。

回到这个“天灾”事件,大家可以看到我做的后两个标记:

②days_passed > 15

这是一个标准的condition语句,判断的是【距离2200年1月1日的日期】,只有当经过日期大于15天时,才会返回true

③destroy_country = yes

这是一个标准的effect语句,执行的效果为【摧毁当前scope的国家】(scope会在之后详细介绍,在该事件中被摧毁的就是事件触发者)

 

很基础的两条语句,相信大家都能看英文字面意思理解它们(真不能的话你可以机翻)。在这里我可以给出一个effectcondition的定义,希望各位不要将两者混淆。

effect

执行的效果,例如【摧毁国家】、【增加资源】、【消灭人口】等,它们都是实际执行的效果,改变了这一局游戏的数据。

例如国家A暴毙,如果说国家A是当前的scope,那么暴毙就是一个effectdestroy_country = yes在这里相当于表动作谓语

condition

判断的条件,例如【检查日期】、【检测人口数量】、【判断国家思潮】等,它们是判断的条件,本身不会改变这局游戏的内容。

例如国家A有唯物主义,你可以通过代码is_materialist = yes进行表达,这句话不会将A变成唯物主义,只是进行判断罢了。主语是国家Ais_materialist = yes有唯物主义condition,可以说是表状态谓语

effectcondition都有各自独立的一套代码,该填effect的地方不要塞condition,反之亦然。倘若你填错了,VSC往往会警告你(只要你正确使用了插件):

图四 unexpected就是蠢驴和我都不希望你填的意思

那么如何判断这里应该填effect还是condition,或者你手上这条代码是哪种呢?

这里提供四种方法:

1、查表:你可以在https://stellaris.paradoxwikis.com/Effects#Scripted_Effects查询到绝大多数的effect代码,在https://stellaris.paradoxwikis.com/Conditions查询到condition们。只要和已有代码比对,你就能轻松发现这是哪一种。

这是最蠢的办法,正经人谁翻效果表啊()

2、依靠VSC的自动补全和报错功能:看到图4了吗?插件都告诉你不能在这填了,你就别填这类代码了。

图五 所以不管咋样去用插件吧

一般都用这种法子,自动补全配合一定的英文基础,大部分的代码都能毫不费力的被你找到。

3、找到两者常用的英语词汇:正如我之前的比喻,蠢驴的effectcondition格式都可以总结为主语谓语,只是存在表动作表状态的差异。

那我们不妨像做英语语法题时一样,根据代码的字面含义判断其种类,就像这样:

    <1>表动作的单词:destroysetremovespawnadd等等——>effect

    <2>表状态的单词:hasiscan等等——>condition

    <3>特殊情况:checkcount这类表示检查的动作词语,是condition

只要看懂了这些词的大致含义,就能了解这是一个动作还是状态,进而就能分清代码属于effect还是condition

4、找到两者的常见代码:有些代码的使用频率是远远高于其他的,当你看到一坨代码不知道咋改时,可以通过寻找它们来快速进行判断。

    <1>很常见的逻辑判断语句

    ANDORNOTNORNAND,这些逻辑门的含义相信大家比较熟悉(不熟悉就去百度),它们都是condition里边包着的是,和它们并列的也是。

    <2>很常见的流程控制语句

    ifelseelse_ifwhileswitch......这些流程控制语句也在蠢驴语中占有一席之地。它们都是effect、与它们并列的都是,里面包含的(除了limit)也都是。

    在ifelse_ifwhile以及很多遍历型scope跳跃语句中,你都可以在内部看到limit语句,这里面需要填conditionlimit本身不能单独存在!),具体含义分情况,例如if中的limit代表if判定的条件,遍历型scope跳跃语句中的limit代表目标必须符合的条件(之后还会提到)。

    <3>很常见的遍历型scope跳跃语句

    遍历型scope跳跃语句,在之后我会进行进一步说明,但你依然可以靠它们的前缀来辨认种类。

前缀为everyrandom的,例如every_owned_planetrandom_owned_pop等代码,都是effect中的遍历型scope跳跃语句

前缀为any的,例如any_owned_planetany_owned_pop等代码,都是condition中的遍历型scope跳跃语句

any不能出现在effect中,反之亦然,不要想当然换着填,没有用的。

你可以发现我并没有在前面提到modifier们,尽管它出现在标题中。因为你是无法直接在事件代码中使用modifier代码的。它们相当于附加在各种东西上的buffdebuff,要想通过事件添加,必须自行创建一组新的静态修正(之后可能提到)。

在这一小节的最后,我再次强调一遍:effectcondition都有各自独立的一套代码,该填effect的地方不要塞condition,该填condition的地方不要塞effect

3 如何讲蠢驴语——事件代码构思思路

在接触了scopeeffectcondition之后,让我们再回首看一下我在第一节做出的比喻。

scope相当于主语,表明要做这个动作的人;effectcondition相当于谓语,表明这个人要做什么动作,或者要满足怎样的条件。将这两部分混合起来,就是一句正常的蠢驴语。

因此我们写作事件时,也要按照蠢驴语的表述方式进行构思。

让我们看看下面这个范例,想一想我们应该怎样构思事件。

范例1-2 烂大街的自动人口迁移(其一)

尽管创意工坊的自动人口迁移mod已经相当多且非常完善了,仍然有很多萌新将其作为事件写作的第一个课题。

......但这个系统,也并没有他们想象的那么简单。

好,现在让我们想想人口迁移怎么写。

作为第一章的一个范例,我们只要求把失业的人口处理掉。(你也看到了,这是其一,迁移的问题咱们目前可解决不了)

让我们先把要求转化为蠢驴事件的表达方式,我们可以得到:

人口失业

人口被鲨

我们可以认定这两个表述的scope都是pop(人口),其中①包含condition失业”,②包含effect消灭人口”。

那么我们写这个事件,就需要首先找到目标人口scope,再对其进行处理

那么让我们先完成第一步:


图六 范例1-2:找到目标(1)

我们利用遍历型scope跳转语句,把当前scope转换为触发国家拥有的全部人口,并使用if语句进行判断:如果那个人口失业的话,我们就可以动手了。

当然,我们可以先简化一下代码:

图七 范例1-2:找到目标(2)

这里利用含limit遍历型scope跳转语句来获取失业中的人口,可以让代码量短一些(这些处理之后还会详细解释,总之我们找到了帝国的所有失业人口。)

然后是进行处理,我们现在能够鲨掉这个人口:


图八 范例1-2:鲨了目标

kill_pop = yes能够直接把当前scope代表的人口消灭,而写下这个effect后,我们第一次的“自动迁移事件”事件写作就结束了。

这个事件能够人道毁灭你的所有失业人口,起码能够让你的岗位界面清爽一些。(说好的迁移呢)

从这个事件的写作历程可以看出,我们的构思与码字是按照这样的逻辑进行:

  1. 把你的需求,翻译成一条条蠢驴语表述的句子

  2. 找到每个句子对应的主语,尝试使用事件寻找scope

  3. 找到scope后,对其进行处理

这是目前我们由上面已经学习过的内容总结出来的事件写作经验。当你想到了一个想用事件完成的新点子时,就要按照这样的思路把你的需求梳理一下。

实际上,我们在写作事件时,可以把它拆成三种需求:

  1. 何时何地,触发该事件?

  2. 该事件的目标,包含哪些?

  3. 我们要对这些目标进行怎样的处置?

在上面的构思逻辑,我们回答了后两个问题,至于第一个,就涉及到on_action等触发器的介绍,我们之后再展开讲。

4 你与事件的极限——mod做不到的事

我们已经讲完了写作一个事件需要的思路,代码分类以及概念,现在就可以开工实现那些只改common无法实现的梦想了。

不过在此之前,我还是需要在最后谈一谈,如何做出一个合理的构思,最起码,可行的构思。

一、事件能不能实现这个?

这是你的构思能否实现的最大物理限制,如果蠢驴提供给你的代码无法实现你想要实现的功能,或者你希望调整的东西是硬代码......

尽快放弃!不要被这种陷阱困住了!

尤其是对于萌新,倘若跟一个无法实现的功能死磕,写了一堆无用无效的代码,不论对接受提问的群友,还是对萌新自己都有害无益。如果萌新自己依旧一意孤行的话,也只能是自己受到最大的伤害。

如何判断这个东西能否完成,你可以通过我对需求的分类,进行思考:

  1. 何时何地,触发该事件?——>有没有这样的触发机制?

  2. 该事件的目标,包含哪些?——>有没有办法找到目标?

  3. 我们要对这些目标进行怎样的处置?——>是否存在这样的effect?

没有effect,你就有极大概率(在你不理解游戏机制时,就是1000%)写不出这个事件,其他的判断要素也是这个道理。

二、我能不能做到这个

第二个要素,就是modder自身的能力。

在不熟悉机制的情况下,尽量不要尝试去处理过于复杂的事件。

这句话是对那些抄代码的同学说的,当你需求的功能很巧的被包含在某个原版,或者其他mod的事件时,是一个很好的主意。

但,很多时候,有些东西不是你单纯抄了改key就能解决的。其中涉及到的封装效果,变量处理,特殊属性很可能大大超出了你的处理能力,进而就会出现你一更改改“炸”了,或者他行你不行的情况。

抄代码的技巧我之后可能会再讲,我还是希望各位能够在抄一段代码时知道自己在抄什么的,毕竟,这大概就是这篇教程的存在价值之一。

5 小结

好啦,负能量发言结束了()

这一篇教程中,我们了解了事件的几个基本要素,以及基本的写作思路。下面我们会像正经教科书一样进行一些总结。

  1. 事件效果描述可以按照主语+谓语的格式,scope主语effectcondition谓语

  2. effect是执行的效果condition是判断的条件,两者分别有不同的代码,不能搞混

  3. 事件写作思路:触发时机地点——>找到目标——>处理目标

记住这些内容,就可以开始写一些更复杂的事件了。

下一章,让我们更详细地探讨一下scope,回答一下如何找到目标的问题(以及更加靠谱的人口迁移!)。

习题一

(1)指出以下事件表达句式scope对应的部分。

领袖年龄大于45

舰船属于军事舰船

国家有唯物主义

国家不是格式塔

人口公民权利为居留权

星球大小超过20

星系中存在多个宜居星球

种族拥有灵能特质

舰队存在领袖

星球添加建筑:水培农场

(2)下面的代码中,哪些是effect,哪些是condition

set_country_flag

establish_communications

create_country

has_ethic

if

is_moon

exist

add_building

OR

remove_ship

(3)试着把下面的需求转换为事件表达句式

消灭战力为1的舰队

为年龄大于85的领袖添加尊者特质

所有有种族洁癖国策的国家获得5000合金

所有拥有的殖民地获得5个人口,倘若此举使得殖民地人口超过20,摧毁那个殖民地。

目标国家失去700能量,500食物。

有灵能特质的领袖所有权将被转移到当前国家的宗主国。

(4)试着找到这些效果对应的代码。

获得资源

检查资源

检查资源月收入

添加modifier(修正)

获得随机科研选项

播放声音

增加信任度

检查人口数目


这些习题是放着玩的,启发一下思考即可,没人会去批,大概()

认识你与event的极限【Stellaris Events Modding 进阶教程#1】的评论 (共 条)

分享到微博请遵守国家法律