貢獻指南

感謝你願意幫忙維護 rd2-wiki(Random Dice 2 骰子樹非官方玩家攻略站)!

這份文件同時是 repo 的貢獻指南,也是網站 關於頁 的內容來源 ——正本只有這一份,不會有第二份「網站上寫的」版本跟這裡不一致。

如果你不確定用詞,全站與所有資料一律使用「渾沌」這個寫法(遊戲原始資料的正式用字就是如此), 不要用發音相近但不同的另一種寫法。

1. 資料正本在哪裡

你能改、也只需要改這三個地方:

⚠️ 兩份正本的節點 id 必須完全對得起來:SVG 裡的每個 data-idnodes.json 都要有 一筆,反之亦然,多一筆少一筆都會被 CI 擋下(規則 19)。新增節點=兩邊都要加。

💡 想知道哪個 id 是哪顆節點,跑 npm run preview:它會用 data/nodes.json 的標籤產出 一份 data/dice-tree.preview.svg,每個節點都標上名字與 id,用瀏覽器打開就看得懂整張圖。 那個檔是產生物、不進版控,改它不會有任何效果。

(2026-08-22 之前文字是寫在 SVG 的 data-* 屬性與 <title> 裡的。那份 <title> 是名稱與 說明的第二份副本,改一個字要同時改兩處,而且整個節點擠在一行 500 字元的標籤裡沒辦法 review。)

其他東西(src/generated/tree.jsonpublic/assets/)都是 npm run build:data 從上面 自動產生出來的建置產物,不要手動編輯、也不會被提交進 repo(在 .gitignore 裡)。你改了 正本之後,本機跑 npm run build:datanpm run dev 就會重新產生。

2. 可以用 Inkscape 等 GUI 工具編輯,但送 PR 前一定要先跑一次正規化

data/dice-tree.svg 用 Inkscape、Illustrator 之類的 GUI 向量編輯器直接開來改是可以的,改節點位置、 連線都沒問題。但它只有幾何、沒有任何文字,直接打開就是 239 個無名圖示。

建議的動線是改預覽檔、存回正本

npm run preview                 # 產出 data/dice-tree.preview.svg(有名字、有 id)
# 用 Inkscape 打開預覽檔,移動節點/加減連線
cp data/dice-tree.preview.svg data/dice-tree.svg   # 存回正本
npm run normalize               # 把標籤與 GUI 留下的東西清掉,還原成乾淨的正本

npm run normalize 會把注回去的標籤刪掉——那些字的正本是 data/nodes.json,SVG 裡留一份 就會變成兩份會漂掉的文案。

⚠️ 如果你在 GUI 裡動到了某個標籤的文字(或不小心解散了某個節點的群組、把標籤拖了出來), normalize 會逐筆列出對不上的地方、一個位元組都不寫、然後失敗。你的檔案原封不動, 照它的指示改完 data/nodes.jsonlabel 再跑一次就好。

但這類工具在存檔時,習慣會把檔案「重寫」成它們自己偏好的形式,包括:

這些改變肉眼看起來畫面完全一樣,但會讓 CI 判讀失敗(見下一節規則 0),因為我們的解析器只認 「絕對座標、單一位移、沒有多餘巢狀圖層」這種乾淨形式。

所以編輯完、送出 PR 之前,一定要在專案根目錄執行:

npm run normalize

這支腳本會把 GUI 工具重寫出來的形式,攤平回乾淨的正規形式(matrix 還原成 translate、相對座標 指令還原成絕對座標、多餘的巢狀圖層拿掉、座標統一取到小數點後兩位)。跑完後再用 git diff 檢查 一下改動是否符合預期,才進行提交。

3. 新增圖示:用 npm run add-icon <path>,不要自己命名檔案

data/icons/ 裡每張圖的檔名,規則是「內容 sha256 雜湊值的前 12 碼 + .png」,例如 a1b2c3d4e5f6.png。這是為了:同一張圖不管被幾個節點共用都只存一份、換圖時檔名一定會跟著變 (不會有「內容換了但檔名沒換,瀏覽器快取吃到舊圖」的問題)。

因此新增或替換圖示時,不要自己手動存檔命名,改用:

npm run add-icon <你的圖片路>

它會自動計算雜湊、用正確檔名複製進 data/icons/,並印出這個檔名,你只要把 data/dice-tree.svg 對應節點的 <image href="icons/..."> 指到這個檔名即可。圖片必須是有效 PNG,且最長邊至少 96px(太小會被 CI 擋下,見規則 7)。

骰盤編輯器(/board)用的是另一批圖——不含底板的「純骰子圖」,放在 data/board-icons/, 而且哪顆骰子用哪張圖記在 data/board-icons.json。要新增或替換它,多帶一個 --board 與節點 id:

npm run add-icon -- --board <節點 id> <你的圖片路>

這樣圖檔與對應表會在同一次指令裡一起更新——只做其中一邊,CI 的規則 21 就會紅(見下一節)。

骰子圖鑑(/dice)的卡片又是第三批——遊戲的 3D 立體骰子圖,放在 data/dice3-icons/, 對應表是 data/dice3-icons.json,形狀與規矩跟 --board 完全一樣:

npm run add-icon -- --dice3 <節點 id> <你的圖片路>

⚠️ 同一顆骰子在站上有三張長得不一樣的圖(節點圖有底板、/board 是扁平卡片視角、圖鑑是 3D 立體),三條路徑彼此獨立,不要互相借用——接錯了畫面上只是「圖變成另一種樣子」,看起來 完全正常,規則 30 就是為了擋這件事(見下一節)。

戰術(/tactic)與 Boss(/boss)也各有自己一批圖,放在 data/tactic-icons/data/boss-icons/,而雜湊直接寫在 data/tactics.jsondata/boss.json 那一筆的 icon 欄裡 (不像 /board 另有一份對應表——這兩份資料本來就是站台自己的,不必對到 SVG 裡的節點 id):

npm run add-icon -- --tactic <戰術編> <你的圖片路>
npm run add-icon -- --boss <Boss> <你的圖片路>

⚠️ 這兩個只會更新既有那一筆的 icon,不會新增紀錄。要加一條全新的戰術,得先把名稱、 階段、適用模式、效果全文與內部ID 補進 data/tactics.json——那些只有看著官方資料表的人知道, 工具猜不出來。找不到那個編號時它會直接失敗,而且圖還沒被寫進目錄

4. 送 PR 前建議自己先跑一遍

npm run normalize    # 把 GUI 工具的重寫攤平回正規形式(跑完 git diff 要是乾淨的,見下一節)
npm run preview      # 產出帶名字與 id 的 data/dice-tree.preview.svg,用來肉眼確認版面
npm run validate     # 檢查資料本身正確不正確(規則 0–10、13–30,見下一節)
npm run build        # 或 npm run build:data;順便檢查組裝後的體積有沒有超出效能預算(規則 12)
npm run typecheck    # 型別檢查(tsc --noEmit);動到 src/ 或 tools/ 一定要跑
npm run test         # 純函式與解析器的單元測試

npm run validatenpm run build 檢查的不是同一件事:validate 只讀 data/dice-tree.svgdata/nodes.json 本身,檢查資料正確不正確build(或單獨跑 build:data)會先把 資料組裝成正式產出的 tree.json,再檢查組裝後的檔案體積有沒有超標兩個都要跑—— 只跑 validate 不會抓到體積超標的問題,等 CI 才發現就白跑一趟。

如果你動到的是 src/ 底下的前端程式碼(畫布渲染、互動邏輯、版面⋯),而不只是 data/nodes.jsondata/dice-tree.svgdata/icons/,送 PR 前也建議跑一次 npm run e2e(它會自動先建置一次) (第一次跑 E2E 前要先執行一次 npx playwright install --with-deps chromium 裝瀏覽器)—— CI 會用獨立的 job 跑同一套 E2E 測試,本機先跑過能提早抓到問題。

5. CI 會擋什麼(白話版)

PR 送出後,CI 會用 npm run validate 檢查兩份正本本身的正確性(座標與結構一律 用瀏覽器同款的 SVG 解析方式判讀,不是用簡單的文字比對,所以看起來「差不多」不代表過得了)。 以下任何一條沒過,PR 就會被擋下:

  1. SVG 檔案結構必須是我們認得的乾淨形式:連線只能是「一條直線、絕對座標、附箭頭」;節點只能 是「單純位移、沒有多餘圖層」。—— 這條幾乎都是忘記跑 npm run normalize 才會中,錯誤訊息會提醒你。 另外節點裡不可以有 <text><title>——名稱、標籤與說明一律寫在 data/nodes.json, 留在 SVG 就會變成第二份會漂掉的副本。npm run normalize 會自動清掉它們。

  2. data/nodes.json 的每一筆都要結構完整namelabeltypegameIdcostmaxLeveldescription 都要填、都不能是空的,長度不超過 500 字(label 是 20 字), 也不能多出我們不認得的欄位。 選用的 categoryawakening 不用時要整個省略那個鍵,不要留 ""

  3. 每個節點的編號(id)不能重複,而且要符合命名規則(開頭數字代表屬性分支、第二碼代表類型)。

  4. 節點外框顏色要跟它的屬性分支對得上:例如粉紫色外框只能用在支援類節點,不能拿去畫骰子。

  5. 解鎖成本的寫法要合乎格式cost 只寫核心與金幣的數字,格式固定、不能換行——等級上限 寫在 maxLevel 欄位,不要寫回 cost 裡。

  6. 連線兩端要準確接在節點正中央,而且要有箭頭:連線畫歪、沒接到節點中心點,或忘記加箭頭樣式, 都會被擋下(座標容許誤差很小,肉眼看起來「差不多對齊」通常不夠)。

  7. 整棵樹不能有循環、也不能有孤立節點:不可以出現「A 的前置是 B、B 的前置又繞回 A」這種環, 而且除了五個屬性的起手骰之外,每個節點都要能沿著連線往回追到某個起手骰,不能憑空飄在樹外接不到任何東西。

  8. 圖示要對得上:節點指到的圖示檔案要存在;圖示檔名(sha256 前 12 碼)要跟檔案實際內容算出來 的雜湊一致(也就是前面說的,新增圖示要用 npm run add-icon,不要自己改檔名);檔案要是合法的 PNG,而且最長邊至少 96px。

  9. 效果說明裡的 #關鍵字(例如 #僵硬)必須是白名單裡已經有的詞,白名單在 data/keywords.json。要用新關鍵字,先把詞加進這個檔案——而且一併寫上解釋,因為那份檔案 同時就是站上顯示給玩家看的詞彙表(詳情面板會把節點用到的每個關鍵字連同解釋一起列出來):

    "冰凍": { "code": "FROZEN", "color": "#9B6BFF", "desc": "移動速度減少" }
    • code:遊戲資源包裡的代碼,只給貢獻者比對原始資料用,站台不顯示、也不進 tree.json
    • color:遊戲內這個標記的底色,#RRGGBB 格式;同色代表同一類機制,站上照用。
    • desc:解釋文字。裡面可以再引用別的 #關鍵字,但同樣要是白名單裡有的詞。

    三個欄位缺一個、色碼寫成 藍色、或解釋裡引用到不存在的詞,都會被規則 8(b) 擋下。

    遇到同一個效果在遊戲裡有兩個名字時(例如詞彙表寫 #果實、花骰子的覺醒文案寫 #播種), 不要抄第二份解釋,用別名指回本尊:"播種": { "aliasOf": "果實" }。別名不准再指向別名。

  10. 成長數值的單位前後要一致:像「每級 +2%(最高 +20%)」這種寫法,括號內外的單位要一樣, 不能一邊寫 % 一邊寫「秒」。

  11. 中央樞紐要接得上:畫面正中央那個「骰子樹」圖示(正本裡的 <g class="tree-center">) 不是節點、沒有 id,但它有五條放射線接到五顆起手骰。這條會檢查:圖檔(data/tree-center.png) 存在、是合法 PNG、而且解析度至少是顯示尺寸的兩倍;data-links 列的 id 都真的存在;每一條 放射線的終點確實落在對應節點的中心(順序也要跟 data-links 一致)。 連線集合若跟五顆起手骰對不起來,只會警告、不會擋。

    編輯這一組時記得先跑 npm run normalize:它必須是 <svg> 的直屬子元素、不能帶 transform(跟節點同一條規矩),圖也必須以樞紐中心對齊,否則 npm run validate 會直接擋下來。

規則 13:畫布與座標

(編號接在 12 後面,是為了不動已經寫在別處的規則 11=差異摘要留言、規則 12=效能預算。)

另外幾條散在既有規則裡的補強,都是同一個道理(畫面上看不出來、資料卻變了):

規則 14–16:覺醒、升級花費表、管理 ID

(2026-08-20 依遊戲原始資料表補齊資料時新增的三條。編號同樣接在既有的後面,不動舊的。)

⚠️ 規則 14 與 16 說的「不能有」是指整個省略那個鍵,不是寫成 "awakening": ""。 空字串會被規則 1 擋下來——它看起來像「有這個欄位、只是還沒填」,語意不一樣。

規則 17–19:官方滿級值、解鎖例外、兩份正本同步

規則 20–21:更新日誌、/board 的純骰子圖

規則 24–25:戰術與 Boss

規則 26:前置節點的等級條件

以上規則 0–10 與 13–30 都是 npm run validate 實際會檢查的內容,本機先跑過一次就能提早抓到,不用等 CI。

另外兩道 CI 檢查:正規化定點、型別檢查

這兩條不在上面的規則編號裡,但一樣會讓 PR 被擋下:

規則 12:效能預算(npm run validate 不會查,npm run build 才會查)

這條不是npm run validate 檢查,是 npm run build(或單獨執行 npm run build:data) 把資料組裝成正式的 tree.jsonsprite.webp 之後,順便檢查兩者的體積有沒有超過上限(目前 門檻:tree.json 壓縮後不超過 20 KB、sprite.webp 不超過 400 KB)。這個指令執行時會直接印出 目前實際用量與距離門檻還剩多少餘裕,例如:

tree.json gzip 15.7 KB / 20 KB,餘裕 4.3 KB
sprite.webp 132.3 KB / 400 KB,餘裕 267.7 KB

這個餘裕平常看起來很寬鬆,但 tree.json 那條其實很緊(目前只剩不到 3 KB,換算大約是 再加 40~50 個節點就會超標)——遊戲改版一次加一整個新分支很容易就吃光。所以就算你只是新增 幾個節點、沒有動到既有資料,送 PR 前也建議看一眼這行輸出,餘裕快見底了及早跟其他貢獻者提一下, 不要等真的超標變成紅燈才發現。

超過門檻的話,npm run build(或 build:data)本身就會印出 錯誤訊息並以非 0 狀態結束, CI 的建置步驟也會因此失敗——效果上一樣會擋下 PR,只是踩線的時間點跟 validate 不同。單獨跑 npm run validate 過了,不代表這條也過,送 PR 前記得也跑一次 npm run build(見上一節)。

CI 也會另外跑一次端對端測試(E2E)

除了上面這些資料層級的檢查,CI 還有一個獨立的 job 會把整站建置起來、用真的瀏覽器(Chromium) 跑一遍互動流程(點選節點、搜尋篩選、鍵盤操作、手機版面⋯)。這個 job 因為要另外安裝瀏覽器, 通常比資料驗證那個 job 慢一些,兩者會平行跑、不互相等待——資料本身有沒有問題,通常很快就能 看到結果,不用等瀏覽器裝完。E2E 測試失敗一樣會讓 PR 的 CI 檢查顯示不通過。一般貢獻者如果只是 改 data/dice-tree.svgdata/icons/,通常不會影響到這個 job;只有動到 src/ 底下前端 程式碼時才比較需要留意。

這兩個 job 綠了才能合併

verify(資料驗證與建置)與 e2e(端對端測試)在 GitHub 上被設成 main 的必要檢查:只要 其中一個沒通過,PR 的 Merge 按鈕就會被鎖住。main 也不接受直接推送,維護者自己的變更同樣 要走 PR。換句話說,這份文件裡寫的每一條規則,都是真的擋得下來的,不是「建議照做」。

6. 這些情況只會警告、不會擋 PR

以下這幾種情況,npm run validate 只會印出警告(本機執行時終端機就看得到;CI 上則是在 PR 的 Checks 分頁裡展開「資料驗證」那個步驟的記錄檔才看得到),不會讓 PR 被擋下,也不會 另外觸發 PR 留言——只有下一段的規則 11 才會自動貼留言:

另外還有一項提醒不太一樣——它自動貼在 PR 底下(不只是記錄檔裡才看得到):

關於這則留言的三個細節:

7. 關於 fork PR 的重要提醒

如果你是從自己 fork 出來的 repo 送 PR(而不是直接推到這個 repo 的分支),Cloudflare Pages 不會自動幫你的 PR 建立 preview 網址(這是 Cloudflare Pages 對 fork PR 的預設限制),所以你在 PR 底下不會自動看到「這個改動實際長怎樣」的預覽連結。

這種情況下:

不確定的話,直接在 PR 留言請維護者幫忙看一下即可。

8. 不要上傳遊戲原始資源包本體

data/dice-tree.svgdata/icons/ 是我們整理過、拆解出來的必要素材(骰子樹結構、圖示), 請不要把遊戲客戶端解包出來的完整資源包、聲音檔、模型檔等其他素材放進 PR。這個 repo 只收錄 網站呈現骰子樹所需要的最小資料集。

9. 授權說明

再次感謝你願意花時間幫忙維護這份資料,讓其他玩家能更清楚地規劃自己的骰子樹!