劇情 MOD 給原版的關卡加事件:第幾回合、誰被擊破、誰和誰交戰、選了「說服」……的時候,播你寫的臺詞,讓人物加入、離場,或者改劇情變數讓後面的原版劇情換一條路走。觸發判斷由遊戲來做,你只寫條件和要做的事,不用懂原版的事件表。原版的事件一個都不會被改動或擠掉。
包的安裝和管理見 MOD 包:安裝與製作。需要 0.5.3 或更新的版本。
完整的示例是「東方不敗存活」,原始檔在 config/recomp/mods/master-asia:獨立軍線「死鬥!大嶼山大決戰(下)」和 OZ 線「絕望與悲傷之拳(下)」裡,多門在戰鬥中和東方不敗交談過之後可以說服他,擊墜數 50 以上時東方不敗撤退,過關後加入。
包的結構
yourname.story/
mod.json
story/
<任意名>.json 劇情指令碼,可以有幾份
data/
pilots.json 需要時順便改數值(見數值 MOD)
打包時(tools/mods/pack.py)story/ 裡的劇情指令碼會被編譯成遊戲讀的格式,寫錯了會報出是哪個事件的哪一項。
寫法
{
"schema": "srw64.story.v1",
"events": [
{
"id": "persuade",
"scene": 129,
"when": { "persuade": { "by": 4, "target": 18 } },
"if": { "vars": { "131": 0 }, "kills": { "4": 50 } },
"do": [
{ "say": 4, "text": { "ja": "「師匠!」", "zh-Hans": "「師父!」", "en": "“Master!”" } },
{ "say": 18, "mode": 2, "text": { "ja": "「……」", "zh-Hans": "“……”", "en": "“...”" } },
{ "leave": 18 },
{ "set": { "131": 3 } }
]
},
{
"id": "joins",
"scene": 129,
"when": { "after": "end" },
"if": { "fired": ["persuade"] },
"do": [ { "join": { "pilot": 18, "unit": 27 } } ]
}
]
}
每個事件:
| 欄位 | 說明 |
|---|---|
id |
事件名,包裡唯一,別的事件用它引用 |
scene |
哪一關,寫場景號(0–255)。各關的場景號見場景號對照表 |
when |
什麼時候觸發,見下 |
if |
可選,還要滿足什麼 |
do |
做什麼,按順序執行 |
repeat |
可選,寫 true 可以反覆觸發 |
set_flags |
可選,事件跑完時設上的標記,給後面的關用 |
note |
可選,隨便寫說明,遊戲不讀 |
每個事件每關預設只觸發一次,遊戲自己記,跟著中斷存檔走,不用像原版那樣自己寫變數防止重複播放。
人物號、機體號見數值 MOD 的人物表和機體表。
when:什麼時候
| 寫法 | 意思 |
|---|---|
{"turn": {"turn": 3, "side": "player"}} |
第 3 回合起、該陣營的回合開始(player、enemy、third) |
{"defeated": 18} |
某人物的單位被擊破(離場不算) |
{"hp_below": {"who": 18, "percent": 30}} |
某人物的 HP 降到 30% 以下 |
{"battle": {"between": [4, 18], "moment": "after"}} |
兩人交戰之後(before 是交戰之前;人物號寫 0 表示任何人) |
{"enemies_gone": true} |
敵方全滅;寫 {"by_turn": 5} 限第 5 回合以內 |
{"remaining": {"side": "enemy", "at_most": 3}} |
敵方(或 third 第三方)只剩 3 臺以下 |
{"persuade": {"by": 4, "target": 18}} |
4 號站在 18 號旁邊時,指令選單出現「說服」,選了就觸發 |
{"after": "opening"}、"setup"、"end" |
接在這關原版的開場、初期配置、結束事件之後 |
同一時刻原版也有事件要觸發時,先跑原版的,你的事件在下一次檢查時再看。開場、配置、結束之後的事件不能寫 repeat。
if:還要滿足什麼
| 寫法 | 意思 |
|---|---|
"vars": {"131": 0} |
劇情變數(0–199)等於這個值(0–3)。原版用劇情變數記分支,例如場景 129 的 131 號是「多門在戰鬥中和東方不敗說過話」(說過是 0) |
"kills": {"4": 50} |
我方這個人物的擊墜數至少這麼多 |
"kills_below": {"4": 50} |
擊墜數不到這麼多 |
"fired": ["persuade"] |
本包的這些事件本關已經觸發過 |
"not_fired": [...] |
本關還沒觸發過 |
"roster": {"pilots": [18], "units": [27]} |
這些人物、機體在我方名冊裡(不管出沒出擊) |
"not_roster": {...} |
都不在名冊裡 |
"flags": ["persuaded"] |
本包的這些標記已經設過 |
"not_flags": [...] |
都沒設過 |
人物不在我方名冊裡時,kills 和 kills_below 都不成立。
成功和失敗兩套臺詞:同一個時機寫兩個事件,讓條件互斥,遊戲會選符合的那個。示例裡擊墜數夠就成功,不夠就播被拒的臺詞:
{ "id": "persuade", "when": { "persuade": { "by": 4, "target": 18 } },
"if": { "vars": { "131": 0 }, "kills": { "4": 50 }, "not_fired": ["refused"] }, "do": [] },
{ "id": "refused", "when": { "persuade": { "by": 4, "target": 18 } },
"if": { "vars": { "131": 0 }, "kills_below": { "4": 50 } }, "do": [] }
被拒的事件只觸發一次,成功的又要求 not_fired: refused,所以被拒之後本關兩個都不成立,選單裡不再出現「說服」,劇情照原版走。想讓玩家能再試,就給被拒的寫 "repeat": true,並去掉成功那個的 not_fired。
跨關:標記
fired 只管本關,到下一關就清空。要讓後面的關知道前面發生過什麼,在事件上寫 "set_flags": ["標記名"](小寫字母、數字、_、-,最多 32 個字),事件跑完時設上。標記只屬於你的包,和原版劇情變數無關,跟著中斷存檔和場間存檔走,開新遊戲才清空。
{ "id": "persuade", "scene": 129, "set_flags": ["persuaded"], "when": { "persuade": { "by": 4, "target": 18 } }, "do": [] },
{ "id": "comes-along", "scene": 81, "when": { "after": "opening" },
"if": { "roster": { "pilots": [18] }, "flags": ["persuaded"] },
"do": [ { "say": 18, "text": { "ja": "「ドモン、遅れを取るでないぞ!」", "zh-Hans": "「多門,別落在後面!」" } } ] }
這裡同時看名冊,是因為他之後可能又被原版劇情或別的事件從名冊刪掉;只看標記的話,人不在也會說話。
do:做什麼
| 動作 | 意思 |
|---|---|
{"say": 18, "text": {...}, "mode": 0} |
一句臺詞,見下 |
{"join": {"pilot": 18, "unit": 27}} |
人物和機體加入我方 |
{"remove": {"pilot": 18, "unit": 27}} |
從名冊刪掉(單位要先離場) |
{"leave": 18} |
人物的單位離場(不算擊破) |
{"leave_group": 11}、{"deploy": 11} |
原版這關的出擊組離場、登場 |
{"set": {"131": 3}} |
設劇情變數 |
{"if": {"var": 128, "is": 2}, "then": [...], "else": [...]} |
按劇情變數分支 |
{"focus": 18} |
鏡頭移到這個人物 |
{"bgm": 11}、{"sound": 124} |
換音樂、放音效 |
{"fade": "out"}、{"fade": "in"}、{"wait": 30} |
淡出、淡入、等待(幀) |
{"close": "window"} |
關掉對白視窗 |
{"op": "3D35", "args": [2335, 1]} |
原版指令碼指令照寫,見關卡指令碼指令參考。可以直接用原版的臺詞,例如 {"op": "3D40", "args": [43215]} |
臺詞:text 必須有 ja,其他語言沒寫的顯示日文(繁體沒寫時用簡體)。一句最多 200 字,閱讀器會自己分頁,不要寫 <STOP>。引號要自己寫,和原版臺詞一樣:日文、繁體用「」,簡體、英文用 「」。mode 是原版對白的顯示方式,地圖上一般我方寫 0、敵方寫 2;世界地圖上 0 和 1 上下交替。
加入的等級:照原版規則取這關的基準等級(由我方前 15 人的平均等級算出)。"level": {"offset": 3} 在基準上加減,"level": {"like": 4} 和 4 號人物同級(按加入那一刻算)。SP、能力、技能、精神由原版按等級算好;基礎值要改就用 data/pilots.json(敵方人物的 SP、精神常常是空的,示例就補了 SP)。
要注意的
- 原版事件只看原版變數。想讓原版的某段事件不播或者換一段,就要改它判斷的變數。示例裡說服之後寫
131 = 3,原版結束事件只在 131 為 1 或 2 時播東方不敗的死別。 - 單位離場(
leave)不會觸發原版的擊破事件。原版擊破後要做的事(示例裡是王登場、開啟過關條件),要在你的事件裡自己做一遍。 - 同一段劇情搬到另一關前,先看那關的原版事件:交談用哪個變數、某人被擊破後原版接著做什麼、結束事件按哪些變數播哪些段。示例的 OZ 線事件就是照場景 137 的原版事件改的。
- 標記存在存檔旁邊(存檔資料夾
saves/裡的story-state/),不在存檔檔案本身裡。把存檔拿到別的模擬器,或者刪掉這個資料夾,讀回來就沒有標記,跨關的事件不會觸發;本關之內的事件不受影響。 - 追加劇本(
campaign/)裡的關卡不跑劇情 MOD。
檢查和打包
python3 tools/mods/story.py compile story/master.json -o out.json
python3 tools/mods/pack.py yourname.story -o build/mods
第一條只編譯一份劇情指令碼,看每個事件編譯出的指令。第二條打包並用和遊戲相同的規則檢查。遊戲讀不了的包會在日誌裡報 SRW64_STORY_MOD_PROBLEM。
倉庫的 docs/guide/story-mods/ 資料夾裡有完整的格式說明和場景號對照表,可以整個交給 AI 助手,讓它照你的想法寫劇情指令碼。