認識你與event的極限【Stellaris Events Modding 進階教程#1】

事件究竟能做什麼?

大家好,還是我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跳躍,直接在根scope(ROOT)執行,因此暴斃的就是國家A了。

圖三 只有一個目標的effect

由此說來,scope可以比喻成一種主語,它代表著所有效果的目標,有了scope,準確地在2200上天的才會是你(而不是住你隔壁的蟑螂兄弟),圓神才能準確地清算你的每家每戶(而不是誤殺了對面的海星)。

當然這顯然不是scope的全部,我還並沒有說明如何進行scope跳躍,以及很多關於scope的,別的操作。但作為一個概念理解,絕大部分scope的相關內容都是由我上邊的話延伸。我們可以先暫停在此處,更詳細的問題留到下一章進行說明。

2 老三樣——effect、condition以及modifier

說到蠢驢的程式碼,最多的就是這三個東西,在自定義程度較高的程式碼(不止事件!),幾乎都能找到這老三樣。

下面我借用範例1-1,簡要介紹一下老三樣的前兩個:effect與condition的作用。

回到這個“天災”事件,大家可以看到我做的後兩個標記:

②days_passed > 15

這是一個標準的condition語句,判斷的是【距離2200年1月1日的日期】,只有當經過日期大於15天時,才會返回true。

③destroy_country = yes

這是一個標準的effect語句,執行的效果為【摧毀當前scope的國家】(scope會在之後詳細介紹,在該事件中被摧毀的就是事件觸發者)

 

很基礎的兩條語句,相信大家都能看英文字面意思理解它們(真不能的話你可以機翻)。在這裡我可以給出一個effect與condition的定義,希望各位不要將兩者混淆。

effect

執行的效果,例如【摧毀國家】、【增加資源】、【消滅人口】等,它們都是實際執行的效果,改變了這一局遊戲的資料。

例如國家A暴斃,如果說國家A是當前的scope,那麼暴斃就是一個effect。destroy_country = yes在這裡相當於表動作的謂語。

condition

判斷的條件,例如【檢查日期】、【檢測人口數量】、【判斷國家思潮】等,它們是判斷的條件,本身不會改變這局遊戲的內容。

例如國家A有唯物主義,你可以通過程式碼is_materialist = yes進行表達,這句話不會將A變成唯物主義,只是進行判斷罷了。主語是國家A,is_materialist = yes是有唯物主義的condition,可以說是表狀態的謂語。

effect與condition都有各自獨立的一套程式碼,該填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、找到兩者常用的英語詞彙:正如我之前的比喻,蠢驢的effect、condition格式都可以總結為主語和謂語,只是存在表動作和表狀態的差異。

那我們不妨像做英語語法題時一樣,根據程式碼的字面含義判斷其種類,就像這樣:

    <1>表動作的單詞:destroy、set、remove、spawn、add等等——>effect

    <2>表狀態的單詞:has、is、can等等——>condition

    <3>特殊情況:check、count這類表示檢查的動作詞語,是condition。

只要看懂了這些詞的大致含義,就能瞭解這是一個動作還是狀態,進而就能分清程式碼屬於effect還是condition。

4、找到兩者的常見程式碼:有些程式碼的使用頻率是遠遠高於其他的,當你看到一坨程式碼不知道咋改時,可以通過尋找它們來快速進行判斷。

    <1>很常見的邏輯判斷語句

    AND、OR、NOT、NOR、NAND,這些邏輯閘的含義相信大家比較熟悉(不熟悉就去百度),它們都是condition、裡邊包著的是,和它們並列的也是。

    <2>很常見的流程控制語句

    if、else、else_if、while、switch......這些流程控制語句也在蠢驢語中佔有一席之地。它們都是effect、與它們並列的都是,裡面包含的(除了limit)也都是。

    在if、else_if、while以及很多遍歷型scope跳躍語句中,你都可以在內部看到limit語句,這裡面需要填condition(limit本身不能單獨存在!),具體含義分情況,例如if中的limit代表if判定的條件,遍歷型scope跳躍語句中的limit代表目標必須符合的條件(之後還會提到)。

    <3>很常見的遍歷型scope跳躍語句

    遍歷型scope跳躍語句,在之後我會進行進一步說明,但你依然可以靠它們的字首來辨認種類。

字首為every、random的,例如every_owned_planet、random_owned_pop等程式碼,都是effect中的遍歷型scope跳躍語句。

字首為any的,例如any_owned_planet、any_owned_pop等程式碼,都是condition中的遍歷型scope跳躍語句。

any不能出現在effect中,反之亦然,不要想當然換著填,沒有用的。

你可以發現我並沒有在前面提到modifier們,儘管它出現在標題中。因為你是無法直接在事件程式碼中使用modifier程式碼的。它們相當於附加在各種東西上的buff與debuff,要想通過事件新增,必須自行建立一組新的靜態修正(之後可能提到)。

在這一小節的最後,我再次強調一遍:effect與condition都有各自獨立的一套程式碼,該填effect的地方不要塞condition,該填condition的地方不要塞effect!

3 如何講蠢驢語——事件程式碼構思思路

在接觸了scope、effect與condition之後,讓我們再回首看一下我在第一節做出的比喻。

scope相當於主語,表明要做這個動作的人;effect與condition相當於謂語,表明這個人要做什麼動作,或者要滿足怎樣的條件。將這兩部分混合起來,就是一句正常的蠢驢語。

因此我們寫作事件時,也要按照蠢驢語的表述方式進行構思。

讓我們看看下面這個範例,想一想我們應該怎樣構思事件。

範例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後,我們第一次的“自動遷移事件”事件寫作就結束了。

這個事件能夠人道毀滅你的所有失業人口,起碼能夠讓你的崗位介面清爽一些。(說好的遷移呢)

從這個事件的寫作歷程可以看出,我們的構思與碼字是按照這樣的邏輯進行:

把你的需求,翻譯成一條條蠢驢語表述的句子。

找到每個句子對應的主語,嘗試使用事件尋找scope。

找到scope後,對其進行處理。

這是目前我們由上面已經學習過的內容總結出來的事件寫作經驗。當你想到了一個想用事件完成的新點子時,就要按照這樣的思路把你的需求梳理一下。

實際上,我們在寫作事件時,可以把它拆成三種需求:

何時何地,觸發該事件?

該事件的目標,包含哪些?

我們要對這些目標進行怎樣的處置?

在上面的構思邏輯,我們回答了後兩個問題,至於第一個,就涉及到on_action等觸發器的介紹,我們之後再展開講。

4 你與事件的極限——mod做不到的事

我們已經講完了寫作一個事件需要的思路,程式碼分類以及概念,現在就可以開工實現那些只改common無法實現的夢想了。

不過在此之前,我還是需要在最後談一談,如何做出一個合理的構思,最起碼,可行的構思。

一、事件能不能實現這個?

這是你的構思能否實現的最大物理限制,如果蠢驢提供給你的程式碼無法實現你想要實現的功能,或者你希望調整的東西是硬程式碼......

儘快放棄!不要被這種陷阱困住了!

尤其是對於萌新,倘若跟一個無法實現的功能死磕,寫了一堆無用無效的程式碼,不論對接受提問的群友,還是對萌新自己都有害無益。如果萌新自己依舊一意孤行的話,也只能是自己受到最大的傷害。

如何判斷這個東西能否完成,你可以通過我對需求的分類,進行思考:

何時何地,觸發該事件?——>有沒有這樣的觸發機制?

該事件的目標,包含哪些?——>有沒有辦法找到目標?

我們要對這些目標進行怎樣的處置?——>是否存在這樣的effect?

沒有effect,你就有極大概率(在你不理解遊戲機制時,就是1000%)寫不出這個事件,其他的判斷要素也是這個道理。

二、我能不能做到這個?

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

在不熟悉機制的情況下,儘量不要嘗試去處理過於複雜的事件。

這句話是對那些抄程式碼的同學說的,當你需求的功能很巧的被包含在某個原版,或者其他mod的事件時,抄是一個很好的主意。

但,很多時候,有些東西不是你單純抄了改key就能解決的。其中涉及到的封裝效果,變數處理,特殊屬性很可能大大超出了你的處理能力,進而就會出現你一更改改“炸”了,或者他行你不行的情況。

抄程式碼的技巧我之後可能會再講,我還是希望各位能夠在抄一段程式碼時知道自己在抄什麼的,畢竟,這大概就是這篇教程的存在價值之一。

5 小結

好啦,負能量發言結束了()

這一篇教程中,我們瞭解了事件的幾個基本要素,以及基本的寫作思路。下面我們會像正經教科書一樣進行一些總結。

事件效果描述可以按照主語+謂語的格式,scope是主語,effect和condition是謂語。

effect是執行的效果,condition是判斷的條件,兩者分別有不同的程式碼,不能搞混。

事件寫作思路:觸發時機地點——>找到目標——>處理目標。

記住這些內容,就可以開始寫一些更復雜的事件了。

下一章,讓我們更詳細地探討一下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(修正)

⑤ 獲得隨機科研選項

⑥ 播放聲音

⑦ 增加信任度

⑧ 檢查人口數目

這些習題是放著玩的,啟發一下思考即可,沒人會去批,大概()

隨便看看 更多