# 劇情編寫與接入

遊戲首頁的「劇情工作台」會開啟 `author.html`。它和分隊使用同一套節點執行器與檢查器。v0.10 先把分隊故事做成可直接編排的資料；現場案件的關係人、病史回答、條件對白及事件序列也集中回案件資料。

## 寫一段分隊故事

1. 開啟工作台，按「新增故事」。新故事預設在二樓勤務白板觸發；原有故事會保留。
2. 在左側 JSON 修改標題、互動點、說話者、文字與 `next`。按「套用並檢查」，在右側選故事並試玩。右側下方顯示目前旗標、已完成故事、結局及各選項的下一節點。
3. 分支試玩後切換另一故事，記憶會保留；用來測試前後呼應。「重新試玩」清除完成記錄，並載入「起始旗標」內的 JSON 物件。預覽略過空間、時間與返隊次數，保留實際旗標條件；正式遊戲仍會檢查全部觸發條件。
4. 「儲存草稿」保存在這台瀏覽器；「匯出 JSON」可備份或交給別人編寫。**這兩項不會改正式遊戲**。
5. 完成後按「匯出遊戲模組」，把下載的 `stories.js` 放到專案 `game/stories.js`，替換既有劇情包。重新整理遊戲後生效；不必修改 `station.js`、新增套件或重新打包。
6. 執行 `npm.cmd run check:stories`、`npm.cmd test`。開始新班，走到觸發點按 E，確認正式遊戲的空間、時間、返隊條件。若要測返隊後續章，開局選 3 趟或 5 趟；1 趟結束後不會進下一輪待命。

也可直接編輯 `game/stories.js`。每段故事只有一個 ID，`nodes` 裡的節點名只需在該段故事內唯一。例如：

```js
{
  id: 'cup-note',
  title: '杯子旁的便條',
  trigger: { spot: 'coffee' },
  entry: 'first',
  nodes: {
    first: {
      speaker: '阿賢學長', art: 'senior',
      text: '這杯先幫我留著，回來再喝。',
      choices: [
        { id: 'save', text: '好，我幫你寫名字。', next: 'saved',
          set: { savedCoffee: true } },
        { id: 'ask', text: '你確定回來還記得？', next: 'joke' }
      ]
    },
    saved: { speaker: '阿賢學長', art: 'senior', text: '有寫名字，就不怕又變成公用的。', end: true },
    joke: { speaker: '阿賢學長', art: 'senior', text: '……所以才要你幫我記得。', end: true }
  }
}
```

在工作台 JSON 中，欄位名和文字都要加雙引號、不能放註解。直接改 `.js` 可沿用上面的 JavaScript 物件格式。

## 觸發、記憶與結局

| 欄位 | 行為 |
| --- | --- |
| `trigger.spot` | 靠近該互動點，按 E 或互動鈕才開始。所有分隊互動點都支援；故事結束後再按 E，執行該地點原本的功能。 |
| `trigger.after` | 本輪待命經過幾個**遊戲秒**後才可觸發。返隊後重新計時；正常倍率每真實秒推進 15 遊戲秒。 |
| `trigger.returns` | 至少完成幾次出勤返隊。開始為 0，真正返隊才加 1。 |
| 無 `spot` | 時間／返隊條件滿足後自動開始；閱讀、睡眠、整備、樓梯、地圖及暫停期間不插入。 |
| `when: { savedCoffee: true }` | 必須符合前一段留下的旗標。未設定的旗標視為 `false`。可用在故事或選項。 |
| `set: { savedCoffee: true }` | 選下該選項才寫入記憶，與節點移動一起完成。值可為布林、數字或文字；條件採完全相等。 |
| `next: 'saved'` | 下一節點，必須存在。每個非結局節點需有 1–4 個選項。 |
| `end: true` | 結局節點；玩家按「繼續」才記為已完成。`outcome` 可另寫報告裡的結局名稱。 |
| `repeatable: true` | 只可用於互動點故事。結束後先恢復該點的原有功能；離開互動距離再回來，才可重播。自動事件要接下一章，避免無限彈出。 |

例如後續章可寫 `trigger: { returns: 1, after: 60 }`、`when: { savedCoffee: true }`，表示至少一次返隊、待命一分鐘後才接續。

預設每段故事一班一次。同時到期的自動故事依劇情包順序播放；相同互動點的多段故事也依順序選第一段符合條件者。對話期間凍結分隊時鐘，不會被下一句背景事件蓋掉。當班旗標與節點隨報告輸出，跨趟保留；重新開班會重置，目前沒有讀回遊戲報告續玩的功能。

每個節點必須存在一條**不受旗標限制**的結束路徑。檢查器會擋重複 ID、未知欄位／互動點、錯誤的 `next`、不可抵達的節點及無法退出的迴圈。這是保守的編寫規則；即使設計上認為旗標一定會開，也要保留可離開的路。

立繪 `art` 可選 `partner`、`senior`、`rookie`、`scene`。不填時依阿哲、阿賢學長、小林學弟的名字選圖，其餘使用場景旁白。文字會跳脫顯示，不執行 HTML 或 JavaScript。

## 可以用的互動點

| 樓層 | ID |
| --- | --- |
| 1F | `locker`, `bag`, `monitor`, `partner`, `ambulance`, `desk`, `stairs-1-up` |
| 2F | `breakfast`, `fridge`, `coffee`, `senior`, `tv`, `notice`, `stairs-2-down`, `stairs-2-up` |
| 3F | `bed`, `gym`, `laundry`, `rookie`, `stairs-3-down` |

樓梯故事結束後再按 E 上下樓；器材點的故事不會代替器材整備或放寬出勤條件。敘事只寫入獨立旗標，不改隨身裝備、處置、生命徵象或案件結案狀態。

## 編寫現場案件

分隊節點故事與現場照護共享「內容寫在資料裡」的方向，但**工作台目前只編輯分隊故事**。現場的自由評估、並行分工、器材操作與來源條件仍由既有現場系統執行。

現場每案的入口是 `game/field-cases.js`：

- `layout`：選擇現場 3D／2D 空間配置。v0.11 已接上 10 種場景；新增家具、照護互動點與可走動範圍見 [可走動的救護現場](FIELD-SPACE.md)。

- `witness: [名字, 身分, 圖像, 開場白, 閒談]`：現場關係人，不再另改 `characters.js` 的案件表。
- `history: [發生經過, 疾病用藥過敏, 最後進食]`：可省略，未編寫的項目依授權來源轉述，無資料保留無法確認。
- `patientLines: [{ when: { stage: 1, done: ['某行動'] }, text: '…' }]`：依順序取第一筆符合全部條件者；可省略 `when` 作為預設句。這只描述已成立的狀態，不會套用病況。
- `actions`、`supplies`、`goals`、`deterioration`：既有照護與來源條件。涉及醫療內容時仍需核對來源與審閱。
- `event`：既有單一插話格式仍相容。
- `events`：改寫成有明確 ID 的事件陣列，即可編排多件插話。`events` 存在時取代 `event`。`radio` 為系統保留 ID。

事件可組合 `at`（到場後秒數）、`after`（單一已完成行動）、`requires`（多項行動都完成）、`unless`（此行動未完成）、`stage`（目前階段）、`afterEvent`（指定事件已**處理完成**）。事件欄位 `speaker/text/resolve/reply/ignore/effect` 沿用現有格式。例如：

```js
events: [
  { id: 'clear-path', at: 20, requires: ['safety'],
    speaker: '同事', text: '通道要留哪邊？',
    resolve: '說明安全位置與通道', reply: '同事退到安全位置。',
    ignore: '同事仍在等你的說明。', effect: 'none' },
  { id: 'path-ready', at: 30, afterEvent: 'clear-path',
    speaker: '同事', text: '通道已經留好了。',
    resolve: '確認他留在安全位置', reply: '同事點頭，等候後續安排。',
    ignore: '同事維持原來的位置。', effect: 'none' }
]
```

新增可玩救護案件仍需在 `game/cases.js` 的 `MAPPINGS` 登記授權來源 ID、街區位置與級別篩選，並在 `game/art.js` 登記場景。這些空間及來源資料不是純對話，尚未納入工作台。現場條件與測試見 `tests/field.test.mjs`；不要用敘事旗標取代臨床事件或自行推導生命徵象。

## 檔案分工

| 檔案 | 用途 |
| --- | --- |
| `game/stories.js` | 正式分隊劇情包；工作台可匯出完整替換模組 |
| `game/story.js` | 共用節點執行器、條件及結構驗證 |
| `game/station-story.js` | 正式遊戲的互動距離、時鐘、返隊與紀錄接入 |
| `author.html`, `game/story-editor.*` | 本機草稿、JSON 匯入匯出、分支預覽與流程卡片 |
| `game/field-cases.js` | 各救護現場的角色對話、照護與事件資料 |
| `tools/check-stories.mjs` | `npm.cmd run check:stories`；可另傳入匯出的 JSON 路徑 |

以《我熱愛值班》範本製作 · [drcharming.net](https://drcharming.net/)。對照範本的 [資料編排與檢查方式](https://github.com/7harming/ilove-oncall-template)，授權與醫療內容界線仍依專案 README 及 LICENSE。
