RUN_DESIGN 文字格式指南
本指南描述一種簡潔、便於人手編輯的互動故事文字格式(RUN_DESIGN)
RUN_DESIGN 文字格式指南
本指南描述一種簡潔、便於人手編輯的互動故事文字格式(RUN_DESIGN)。同時支援可逆轉換:JSON → RUN_DESIGN → JSON,在支援的欄位範圍內不會有語義流失。
目標
簡單、可讀、利於版本控管的文字格式
能編譯成 JSON 的故事結構
能將 JSON 無語義差異地匯出回文字
檔案編碼
UTF-8
空白與註解(Whitespace & Comments)
允許空白行。
以
//
開頭的單行為註解,編譯時會被忽略。請另開新行。
頂層後設資料(Metadata)
[meta] title "<Title>"
設定故事標題[intro] <text>
追加一行故事導言(可重複多行),在劇本開始時會顯示。
顯示行為:
在
.st list
中:指定 alias 時,會完整顯示該故事的導言(若有)。
列出所有可啟動劇本時,會在每列標題下顯示導言第一行的預覽(最多 80 字)。
在
.st mylist
中:每項劇本會顯示導言第一行的預覽(最多 80 字)。
在啟動遊戲後且玩家變數尚未完成設定時:
角色設定提示前會先顯示【簡介】區塊,內容為導言全文。
範例:
[meta] title "貓咪的一天"
[intro] 歡迎來到互動故事!
玩家變數(Player Variables)
[player_var] <key> "<prompt>" ["<placeholder>"]
key:識別字,例如
cat_name
prompt:顯示給玩家的提問
placeholder:可選的提示文字
範例:
[player_var] cat_name "1. 請輸入你的貓咪名字:" "例如:橘子、Mochi、小黑"
[player_var] owner_name "2. 請輸入主人的名字:" "小明、艾蜜莉、阿傑"
遊戲屬性(Game Stats)
[stat_def] <key> <min> <max> ["<label>"]
初始化:未被明確設定時,首次開始時以
min
~max
隨機整數初始化。鎖定:若該屬性曾被內容內的
[set]
明確指定,之後將不再被隨機初始化覆寫。
範例:
[stat_def] Cuteness 1 10 "萌度 (Cuteness)"
[stat_def] Energy 1 10 "活力 (Energy)"
[stat_def] Mischief 1 10 "淘氣度 (Mischief)"
變數(Variables)
[var_def] <key> <min> <max> ["<label>"]
範例:
[var_def] rain 0 1 "下雨"
頁面(Pages)
故事由多個頁面組成。
定義頁面標籤(ID):
[label] <id>
限制:頁面 ID 只能使用數字(如
0
,1
,2
,10
等)開點頁面:
[label] 0
為故事的起始頁面,遊戲開始時會自動載入此頁面其他頁面可使用任意數字作為 ID,建議使用連續數字以保持可讀性
可選頁面標題:
[title] <text>
頁面內容(不限行數):
[text] <content>
[text|if=<expr>] <content>
條件顯示[text|else] <content>
與前一或多個連續的[text|if=...]
形成條件鏈,只會顯示第一個符合條件的項目;若皆不符合則顯示else
。也支援於結局區塊中使用。[text|ifs=<expr>] <content>
獨立條件顯示:只要命中就顯示,不會與上下鄰近的[text|if=...]
/[text|else]
形成條件鏈。[text|speaker=<key>] <content>
指定說話者[text|speaker=<key>,if=<expr>] <content>
指定說話者且具條件[random] <percent>%
僅影響「下一行」的[text]
(例如 30%),percent
為 0~100 的整數。[set] <key>=<expr>
在渲染時設定值:若key
屬於已定義的stat_def
,則寫入stats
;否則寫入variables
。<expr>
支援基本運算式(見下文)。任一屬性一旦被
[set]
明確設定,之後將不再由隨機初始化覆寫。
文字內可直接擲骰:
{xDy}
會在顯示時擲骰並以總和取代,例如{1D100}
、{2d20}
、{3d6}
。
結局標記:
[ending]
之後的[text]
行視為結局文字,會使用第一個符合條件的結局支援條件鏈:可使用多行
[text|if=...]
後接一行[text|else]
作為後備要求:一個有效的 RUN_DESIGN 必須至少包含一個帶有
[ending]
標記的頁面;若未定義結局,上傳/更新將被拒絕。
選項區塊:
[choice]
開始定義選項列表-> <text> | <頁面代號> [| if=<expr>] [| stat=a+1,b-2]
<頁面代號>
必須為數字頁面 ID,或使用帶字母尾碼的變體(例如2a
、2b
、2c
);或特殊值END
。新功能:支援
2a
,2b
,2c
等格式,其中數字部分(如2
)為實際跳轉的頁面,字母部分(如a
,b
,c
)僅用於區分不同的加成或描述變體(實際跳轉到2
)。<頁面代號>
為END
時,介面會提供「.st end
」按鈕以結束遊戲。stat=
僅支援整數加減,並在成功前往該選項之目標頁面時套用(例:Cuteness+1,Energy-2
)。
新功能:多選項同頁面跳轉
使用 2a
, 2b
, 2c
等格式可以讓多個選項都跳轉到同一個頁面(如頁面 2
),但每個選項可以有不同的加成效果:
[choice]
-> 準備好大鬧一場了! | 2a | stat=Mischief+1
-> 先伸個懶腰,看看今天心情如何。 | 2b | stat=Cuteness+1
-> 今天也要充滿活力! | 2c | stat=Energy+1
當玩家使用 .st goto 2a
、.st goto 2b
、.st goto 2c
時:
都會跳轉到頁面
2
但會分別獲得不同的加成:淘氣度+1、萌度+1、活力+1
運算式(Expressions)
條件以小型、類 JS 的子集合為語法,運行於 scope(
variables
+stats
+playerVariables
)支援運算子:
&& || ! < <= > >= == === != !== + - * / % ()
安全限制:不允許任何函式呼叫;不可存取
globalThis
、global
、process
、this
、Function
、constructor
、require
等識別字。範例:
if=Cuteness>=8 && Energy>3
支援一元否定
!expr
(可搭配括號以控制優先順序)。範例:
if=(Strength>5) && !(Agility>5)
擲骰(Dice)
在條件與賦值運算式中,可直接使用
xDy
字面量,會在運算前擲骰並以總和值替換,例如:if=2d20>25
[set] luck=3d6+2
允許的範圍:
x
1100、y110000;超出將被夾在此範圍內。
條件賦值(Conditional Set)
支援在
[set]
上使用條件選項:[set|if=<expr>] key=<expr>
。結合擲骰,可實作常見的檢定流程。
範例:
[stat_def] san 0 100 "SAN"
[set] san=70
[label] SAN_CHECK
[set] sancheck=1d100
[text] sancheck={sancheck}
[set|if=san<sancheck] san=san-1
[text|if=san<sancheck] 你扣了1san
[text|if=san>=sancheck] 你穩住了心神
範例頁面(Example Page)
[label] 0
[title] 角色創造
[text] 設定完成!現在,讓我們來看看 {cat_name} 今天的狀態...
[text] (系統會為你隨機生成 1-10 的數值)
[text] - 萌度 (Cuteness): {Cuteness}
[text] - 活力 (Energy): {Energy}
[text] - 淘氣度 (Mischief): {Mischief}
[text|ifs=Wit==1]確切屬性值 -> Wit: 1
[text|ifs=Wit==2]確切屬性值 -> Wit: 2
[text|ifs=Wit==3]確切屬性值 -> Wit: 3
[text|ifs=Wit==4]確切屬性值 -> Wit: 4
[text|if=Cuteness>Mischief+2] 他笑了笑,把它撿起來,說:「你這個小搗蛋鬼。」然後把我抱起來,親了一下。我的惡作劇成功了!
[text|if=Cuteness<=Mischief+2 && Cuteness > 4] 他嘆了口氣說:「{cat_name},不可以這樣喔。」但他還是忍不住摸了摸我的頭。看來這次被原諒了。
[text|if=Cuteness<=4] 他看起來有點生氣,把我抱起來唸了幾句,今天沒有點心了。
[text|if=Cleverness>=10] 他注意到我的聰明眼神,笑著說:「你是不是在計劃什麼?」並給了我額外獎勵。
[text|else] 他只是搖搖頭,收拾了一下。今天是普通的一天。
[choice]
-> 準備好了嗎? | 1
結局頁(Ending Page)
[label] 22
[title] 結局
[ending]
[text|if=Cuteness>8] 以賣萌獲得原諒
[text|else] 溫柔的無奈
[choice]
-> 回到開頭 | 0
-> 結束遊戲 | END
佔位符(Placeholders)
在
[text]
內使用{key}
會依序從playerVariables
、stats
、variables
取值並套入(優先順序如前,後者可覆蓋前者)。若找不到對應鍵,將保留原樣(例如
{unknown_key}
會原樣輸出)。
註:相容性與限制補充
目前不支援以純字串作為頁面 ID;請使用數字頁面 ID(例如
0
,1
,2
)。[set]
行允許在值的後方加上行尾//
註解,匯入時該註解會被忽略,不影響賦值內容。
往返轉換(Round Trip)
匯入文字(於 Discord 夾帶檔案):傳送
.st import <alias> [title]
並附上.txt
(RUN_DESIGN)或.json
檔案更新既有劇本:
.st update <alias> [title]
並附上新檔案匯出文字:
.st exportfile <alias>
(機器人將以私訊傳送文字檔)驗證可逆:
.st verify <alias>
正規化(Normalization)
編譯器會將緊鄰的
[random]
與其後的第一行[text]
合併解釋為「機率顯示」。匯出時,若頁面是結局頁,
[ending]
會緊跟在該頁[label]
之下,以維持可逆性。
慣例(Conventions)
開點頁面:
[label] 0
為故事的起始頁面,遊戲開始時會自動載入此頁面。若需要可在編譯後的 JSON 再行設定。頁面 ID 限制:只能使用數字作為頁面 ID。
說話者為可選;示例採用純文字。
若需在內文中加入隨機性,請於欲影響的
[text]
之前「緊貼」放置[random] <percent>%
(percent 為整數)。若需在選項上改變屬性值,使用
stat=a+1,b-2
(僅支援整數加減)。若需多個選項跳轉到同一頁面但有不同的加成效果,使用
2a
,2b
,2c
等格式。
最佳實務(Best Practices)
為可讀性建議使用數字且連續的 ID
條件判斷儘量簡單並以已定義的鍵為基礎
避免過長的行;可拆分成多個
[text]
確保每個結局頁面都提供重新開始或結束的選項
善用
2a
,2b
,2c
格式來創建有不同加成的選項
限制(Limits)
最多頁數:400
每段文字(每一行
[text]
,包含結局文字)最長 500 字至少需包含一個帶有
[ending]
的頁面匯入/更新之附件大小上限:約 1 MB
頁面 ID 只能使用數字
更多範例(More Examples)
說話者(Speakers)與條件鏈
[label] 5
[title] 對話示例
[text|speaker=cat] 喵~今天要做什麼呢?
[text|if=Energy>=8] 我覺得精力充沛!
[text|if=Energy>=5 && Energy<8] 還行,可以動一動。
[text|else] 有點想睡覺……
[choice]
-> 出門巡視 | 6
-> 先小睡一下 | 7
說話者為可選欄位,渲染時僅作為資料欄位保存,不影響文字輸出。
隨機顯示(Random)
[label] 6
[title] 隨機事件
[random] 30%
[text] 你意外撿到一根貓薄荷棒!
[text] 無論是否撿到,你繼續前進。
[random] <percent>%
僅作用於其後第一行 [text]
,編譯器在匯出時會保持可逆性。
內嵌擲骰(Dice)與條件檢定
[label] 8
[title] 擲骰檢定
[set] roll=1d20
[text] 你的檢定結果為:{roll}
[text|if=roll+Energy>=18] 大成功!
[text|else] 普通成功或失敗。
在運算式中可使用 xDy
字面量;在文字中可用 {xDy}
直接內嵌擲骰,顯示總和。
條件賦值(Conditional Set)與字串值
[label] 9
[title] 狀態標記
[set|if=Energy>=8] mood="energetic"
[set|if=Energy<8] mood="lazy"
[text] 目前心情:{mood}
RHS 以引號包裝時會被視為字串常值;否則會嘗試以表達式求值。
多個選項同頁面跳轉(帶加成)
[label] 10
[title] 開場選擇
[choice]
-> 走力量路線 | 2a | stat=Power+1
-> 走敏捷路線 | 2b | stat=Agility+1
-> 走智力路線 | 2c | stat=Wit+1
[label] 2
[title] 共用頁面
[text] 你來到了訓練場。
玩家輸入 .st goto 2a/2b/2c
皆會抵達頁面 2
,但各自套用不同的加成。
獨立條件顯示(ifs)
[label] 11
[title] 屬性揭示
[text|ifs=Wit==1] 你感到有點遲鈍。
[text|ifs=Wit>=8] 你靈光一閃,找到了捷徑。
[text] 無論如何,你繼續前進。
使用 ifs
的 [text]
不會與相鄰的 if/else
形成條件鏈,命中就顯示,可同時出現多行。
結局區塊的條件鏈
[label] 99
[title] 結局
[ending]
[text] 旅程告一段落。
[text|if=Power>=8] 你以力量壓倒眾人,建立了威名。
[text|if=Agility>=8] 你以身法穿梭暗影,無跡可尋。
[text|else] 你學到了寶貴的一課,準備再出發。
[choice]
-> 重新開始 | 0
-> 結束 | END
結局的多行 [text|if=...]
與一行 [text|else]
形成條件鏈,僅顯示第一個符合條件者;可在其上方書寫無條件前言文字。
佔位符的優先順序與巢狀
[label] 12
[title] 佔位符
[set] title="勇者"
[set] who="{owner_name}"
[text] {who} 稱呼你為「{title}」。
{key}
查找順序為 playerVariables
→ stats
→ variables
。字串值內若再包含 {...}
,會進行一次巢狀展開。
Last updated