Lashtw commited on
Commit
a51efaa
·
verified ·
1 Parent(s): b0e725d

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +132 -140
  2. function.html +482 -198
  3. sequence.html +5 -0
README.md CHANGED
@@ -1,140 +1,132 @@
1
- ---
2
- title: MathCity
3
- emoji: 🌖
4
- colorFrom: gray
5
- colorTo: pink
6
- sdk: static
7
- pinned: false
8
- ---
9
-
10
-
11
- # Math City: Cyber Chronicles (數學特區:未來都市)
12
-
13
- ## 1. 願景 (Project Vision)
14
- 本專案為八年級下學期數學課程的遊戲化教學平台。透過「未來都市」的沉浸式包裝,讓學生以「特務/維護者」的身分,在解決城市危機的過程中學習數學概念。
15
-
16
- **核心體驗**:
17
- * **平台**:iPad 橫向全螢幕體驗 (Mobile Web App)
18
- * **風格**:Cyberpunk / Neon Noir (賽博龐克/霓虹暗黑)
19
- * **技術**:HTML5 Canvas + Tailwind CSS + Vanilla JS (無須建置工具)。
20
-
21
- ## 2. 檔案結構 (File Structure)
22
- ```
23
- /
24
- ├── index.html # [入口] 城市全息地圖 (Math City Map)
25
- ├── sequence.html # [數列] 數列峽谷 (The Glitch Canyon)
26
- ├── function.html # [函數] 能源核心 (The Energy Core)
27
- ├── congruence.html # [全等] 全等重案 (Congruence District) - {待開發}
28
- ├── parallel.html # [平行] 平行建構區 (Parallel Skyline) - {待開發}
29
- └── assets/ # (建議) 存放圖片與音效資源
30
- ```
31
-
32
- ## 3. 視覺設計系統 (Design System)
33
-
34
- ### 色彩計畫 (Neon Palette)
35
- 這座城市由四個區域組成,每個區域有其代表的主色調:
36
- * **🟦 能源核心 (Cyan)**: `#06b6d4` (Cyan-500) - 代表科技、冷靜、函數邏輯
37
- * **🟨 數列峽谷 (Amber)**: `#f59e0b` (Amber-500) - 代表古老遺跡、警示、規律
38
- * **🟪 全等重案組 (Magenta)**: `#d946ef` (Fuchsia-500) - 代表神秘、霓虹街頭、偵探氛圍。
39
- * **🟩 平行建構區 (Green)**: `#22c55e` (Green-500) - 代表建設、雷射光束、結構。
40
- * **🌑 背景基調**: `#050510` (Very Dark Blue) - 極致黑,襯托霓虹光。
41
-
42
- ### UI 規範
43
- * **字體**: 'Orbitron' (數字/英文標題), 'Noto Sans TC' (中文內文)
44
- * **介面**: 玻璃擬態 (Glassmorphism),半透明深色背景 + 亮色邊框
45
- * **互動**: 針觸控優化大按鈕,避免依賴 Hover 效果
46
-
47
- ## 4. 遊戲模組詳解 (Game Modules)
48
-
49
- ### A. 數列峽谷 (The Glitch Canyon) - `sequence.html` [已完/需微調]
50
- * **數學單元**:等差數列與級數。
51
- * **劇情**:城市邊緣的古老程式碼層出現 Glitch階梯消失
52
- * **玩法**:動作跳躍遊戲。
53
- * **Phase 1 (觀念檢測)**:進入關卡前,先通過數列填空測驗,確認對首項與公差的理解。
54
- * **Phase 2 (列跑酷)**:動作遊戲階段
55
- * **視覺風格**:全區統一碎石 (Gravel) 紋理數位荒野主題
56
- * **規則**:僅有列的「前兩項」會有琥珀色外框提示,後續特定平台外觀一致,玩家需自行計算並跳上正確數字平台
57
- * **裝飾**:仙人掌與骨骸等裝飾物僅生成於無數字的安全平台上不干擾作答
58
- * **Phase 3 (召喚儀式)**:終點互動教學。利用倒轉複製概念將階梯補成長方形,引導學生推導出等差級數求和公式 $S_n = \frac{(a_1+a_n) \times n}{2}$
59
- * **Phase 4 (核心回顧)**:(Gold Standard) 遊戲結束後顯示重點摘要包含「等差數列定義」、「公差公式」、「級數和公式」,對應學習單內容
60
-
61
- ### B. 能源核心 (The Energy Core) - `function.html` [已完成]
62
- * **數學單元**:線型函數 ($y = ax + b$)
63
- * **劇情**:中央發電廠核心不穩需要輸入正確晶石量以維持能量平衡
64
- * **玩法**:數據分析邏輯推導
65
- * **Phase 1 (蒐集數據)**:輸入 $x$ (晶石)觀察 $y$ (電力),利用描法找出函數關係
66
- * **Phase 2 (規律分析)**:觀察圖表上的點,辨識出「直線」規律,並利用規律預測未來。
67
- * **Phase 3 (過載危機)**:系統給出目標電力 $y=203$,玩家需根據 $y=2x+3$ 反求投入量 $x$。
68
- * **Phase 4 (進階推導)**:(Advanced Deduction) 核心重置,公式改變 ($y=ax+b$)。
69
- * **Step 1 (找截距 b)**:投入 $x=0$,觀察 $y$ 值,解出 $b$
70
- * **Step 2 (找斜率 a)**:投入 $x=1$,觀察變化量,出 $a$
71
- * **Step 3 (驗算)**:新公式 $y=3x+5$ 驗算 $x=10$ 的結果
72
- * **Phase 5 (核心解鎖)**:輸入正確的 $a$ 與 $b$ 值完成任務
73
-
74
- ### C. 全等重案組 (Congruence District) - `congruence.html` [規劃中]
75
- * **數學單元**:全等三角形判別 (SAS, ASA, SSS, RHS)
76
- * **劇情**:追捕偽裝大師「三魔人」
77
- * **玩法**:偵探解謎
78
- * **Phase 1 (蒐證)**:在案發現場使用放大鏡蒐集「邊長」與「角度」線索
79
- * **Phase 2 (指認)**:在警局指認牆上,利用蒐集的條件篩選嫌疑犯
80
- * **Phase 3 (審判/回顧)**:整理案情總結全等性質(SAS, ASA...),並解釋為何 SSA (搖擺) 無法全等
81
-
82
- ### D. 平行建構區 (Parallel Skyline) - `parallel.html` [規劃中]
83
- * **數學單元**:平行線截角性質、特殊四邊形。
84
- * **劇情**:建設通往雲端的摩天大樓。
85
- * **玩法**:築工程
86
- * **平行光束**:調整雷射發射器角度,利「內錯角相等」或「同側內角互補」原理接通橋樑
87
- * **四邊形物流**:輸送帶上出現各種四邊形建材,需根據對角線性質 (是否等長/垂直/平分) 快速分類
88
- * **Phase 3 (完工驗收)**:展示建築藍圖,總結平行線截角性質與特殊四邊形的判別家族樹。
89
-
90
- ## 5. 開發路徑 (Roadmap)
91
- - [x] **Phase 1: 基礎建設**
92
- - [x] 建立 `index.html` 城市地圖
93
- - [x] 生成 iPad 專用背景圖
94
- - [x] 統一導航系統 (Back to Map 按鈕)。
95
- - [ ] **Phase 2: 現有程式優化**
96
- - [ ] `function.html`: 增加音效回饋、優化引導流程、修正手機版面。
97
- - [ ] `sequence.html`: 統一視覺風格,調整難度曲線。
98
- - [ ] **Phase 3: 新關卡開發**
99
- - [ ] 開發 `congruence.html` (全等重案組) 原型。
100
- - [ ] 開發 `parallel.html` (平行建構區) 原型。
101
-
102
- ---
103
-
104
- ## 6. 修正經驗與開發筆記 (Dev Log)
105
- 此區塊記錄開發過程中的重要迭代與修正經驗,供日後參考。
106
-
107
- ### [Sequence Canyon] 視覺與機制修正 (Visual & Mechanics Polish)
108
- * **視覺一致性的陷阱 (Visual Consistency)**:
109
- * 在設計「尋找規律」類型的遊戲時**所有平台的視覺材質必須嚴格統一**(例如全部使用碎石 Gravel)
110
- * 若正確與錯誤平台有顏色差異玩家會傾向使用「顏色判斷」而非「數學計算」,導致教學目標失效
111
- * **提示機制 (Scaffolding)**
112
- * **限制提示範圍**:為了兼顧新手引導與挑戰性,提示(如光暈、外框)應僅限於首項(前兩項),後續必須完全移除提示
113
- * **嚴格判定**:程式邏輯需確保 `p.hint` 是唯一決定外觀差異的參數,避免其他狀態(如 `target` 類型預設值)意外洩漏答案。
114
- * **裝飾物與碰撞 (Decorations & Collision)**:
115
- * **空白平台 Bug**:直接在遊戲主路徑生成無功能裝飾平台容易讓玩家混淆(跳上去沒反應)
116
- * **解決方案**:採用「區塊化 (Chunking)」策略,將起點與終點的長型安全區切割多個小區塊,將裝飾物僅生成於這些絕對安全區域確保遊戲核心路徑的純粹
117
- * **音效與視覺同步 (Audio-Visual Sync)**
118
- * 過關音效(如 Victory Fanfare)應在視覺高潮(如煙火發射)**第一瞬間**觸發
119
- * 避免在動畫結束 callback 才播放音效,會造成「乾乾的」等待感。
120
-
121
- ### [Function Core] 介面優化與教學引導 (UI/UX & Pedagogy Refinement)
122
- * **介面佈局 (Layout & Alignment)**
123
- * **分割視窗 (Split-Screen)**:在 Desktop 模式下應將操作區(左)與圖表區(右)明確分開避免彈出式視窗遮擋數據
124
- * **視覺對齊 (Pixel-Perfect Alignment)**:下排列的元件(如「核心法則Banner」與「指令對話框」)寬度必須一致(例如都設為 `w-[480px]`)視覺安定感大幅提升
125
- * **教學引導與錯誤回饋 (Pedagogy & Feedback)**:
126
- * **不只是報錯**:當學生回答錯誤時,不能只顯示 Error,必須提供**具體且有數學邏輯的引導**。(例如:錯誤提示不只寫 "Wrong",而是顯示 "$203 = 2x + 3 \rightarrow 200 = 2x$",引導下一步思考)。
127
- * **即時回饋 (Instant Gratification)**:當學生發現數學規律(如「是一條直線」)時,圖表應**立刻**畫出對應的直線,讓數學發現與視覺獎勵同步。
128
- * **視覺層級 (Visual Hierarchy)**:
129
- * **關鍵變數特大化**:數學應用題的關鍵變數 ($x, y$) 字體應設定為極大 (e.g., `text-6xl`) 並嚴格遵守色碼($x$=琥珀, $y$=青),降低閱讀一段長文字的認知負荷。
130
- * **去除干擾**:輸入框周圍不應有重複的標籤文字(如重複的「投入晶石」),會造成視覺混亂。
131
- * **開發注意事項 (Dev Ops)**:
132
- * **檔案覆寫確認**:進行大幅度重構時,若 `replace_file_content` 失敗頻率高,應果斷使用 `write_to_file` 進行全檔覆寫 (`Overwrite=true`),確保版本一致性。
133
-
134
- ---
135
- ---
136
- ## 7. 專案製作群 (Credits)
137
- * **遊戲設計**: 新竹縣精華國中 藍星宇老師
138
- * **社群**: [萬物皆數](https://www.facebook.com/groups/1554372228718393)
139
-
140
- *Created by Antigravity for 11402 Semester Project*
 
1
+ # Math City: Cyber Chronicles (數學特區:未來都市)
2
+ ## ⚠️ AI 行為準則 (System Instructions)
3
+ 1. **語言要求:** 所有的對話、思考過程、Implementation Plan (實作計畫) 以及程式碼註解,請**嚴格使用「繁體中文 (Traditional Chinese)」,唯獨專有名詞可以用英文呈現**。
4
+ 2. **例外:** 只有程式碼本身的變數名稱、函數名稱���留英文。
5
+ ## 1. 專案願景 (Project Vision)
6
+ 本專案為八年級下學期數學課程的遊戲化教學平台。透過「未來都市」的沉浸式包裝,讓學生以「特務/維護者」的身分,在解決城市危機的過程中學習數學概念。
7
+
8
+ **核心體驗**:
9
+ * **平台**:iPad 橫向全螢幕體驗 (Mobile Web App)。
10
+ * **風格**:Cyberpunk / Neon Noir (賽博龐克/霓虹暗黑)。
11
+ * **技術**:HTML5 Canvas + Tailwind CSS + Vanilla JS (無須建置工具)
12
+
13
+ ## 2. 結構 (File Structure)
14
+ ```
15
+ /
16
+ ├── index.html # [入口] 城市全息地圖 (Math City Map)
17
+ ├── sequence.html # [數列] 數列峽谷 (The Glitch Canyon)
18
+ ├── function.html # [函數] 能源核心 (The Energy Core)
19
+ ├── congruence.html # [全等] 全等重案組 (Congruence District) - {待開發}
20
+ ├── parallel.html # [平行] 平行建構區 (Parallel Skyline) - {待開發}
21
+ └── assets/ # (建議) 存放圖片與音效資源
22
+ ```
23
+
24
+ ## 3. 視覺設計系統 (Design System)
25
+
26
+ ### 色彩計畫 (Neon Palette)
27
+ 這座城市由四個區域成,每個區域有其代表的主色調:
28
+ * **🟦 能源核心 (Cyan)**: `#06b6d4` (Cyan-500) - 代表科技、冷靜、函數邏輯。
29
+ * **🟨 數列峽谷 (Amber)**: `#f59e0b` (Amber-500) - 代表古老遺跡、警示、規律。
30
+ * **🟪 全等重案組 (Magenta)**: `#d946ef` (Fuchsia-500) - 代表神秘、霓虹街頭、偵探氛圍。
31
+ * **🟩 平行建構區 (Green)**: `#22c55e` (Green-500) - 代表建設、雷射光束、結構。
32
+ * **🌑 背景基調**: `#050510` (Very Dark Blue) - 極致黑,襯托霓虹光。
33
+
34
+ ### UI 規範
35
+ * **字體**: 'Orbitron' (數字/英文標題), 'Noto Sans TC' (中文內文)。
36
+ * **介面**: 玻璃擬態 (Glassmorphism),半透明深色背景 + 亮色邊框
37
+ * **互動**: 針對觸控優化的大按鈕,避免依賴 Hover 效果
38
+
39
+ ## 4. 遊戲模組詳解 (Game Modules)
40
+
41
+ ### A. 數列峽谷 (The Glitch Canyon) - `sequence.html` [已完成/需微調]
42
+ * **數學單元**:等差數列與級數。
43
+ * **劇情**:城市邊緣的古老程式碼層出現 Glitch,階梯消失
44
+ * **玩法**:動作跳躍遊戲
45
+ * **Phase 1 (觀念檢測)**:進入關卡前,先通過數列填空測驗,確認首項與公差理解
46
+ * **Phase 2 (數列跑酷)**:動作遊戲階段。
47
+ * **視覺風格**:全區統一碎石 (Gravel) 紋理,數位荒野主題。
48
+ * **規則**:僅有數列的「前兩項」會有琥珀色外框提示,後續特定平台外觀一致,玩家需自行計算並跳上正確數字平台。
49
+ * **裝飾**:仙人掌與骨骸等裝飾物僅生於無數字的安全平台上,不干擾作答。
50
+ * **Phase 3 (召喚儀式)**:終點的互動教學。利用「倒轉複製」概念,將階梯補成長方形,引導學生推導出等差級數求和公式 $S_n = \frac{(a_1+a_n) \times n}{2}$
51
+ * **Phase 4 (核心回顧)**:(Gold Standard) 遊戲結束後顯示重點摘要包含「等差數列定義」、「公差公式」、「級數和公式」,對應學習單內容
52
+
53
+ ### B. 能源核心 (The Energy Core) - `function.html` [已完成]
54
+ * **數學單元**:線型函數 ($y = ax + b$)
55
+ * **劇情**:中央發電廠核心不穩需要輸入正確的晶石量以維持能量平衡
56
+ * **玩法**:數據分析與邏輯推導
57
+ * **Phase 1 (蒐集數據)**:輸入 $x$ (晶石)觀察 $y$ (電力),利用描點法找出函數關係
58
+ * **Phase 2 (規律分析)**:觀察圖表上點,辨識出直線規律並利用規律預測未來
59
+ * **Phase 3 (過載危機)**:系統給出目標電力 $y=203$玩家需根據 $y=2x+3$ 反求投入量 $x$
60
+ * **Phase 4 (進階推導)**:(Advanced Deduction) 核心重置,公式改變 ($y=ax+b$)。
61
+ * **Step 1 (找截距 b)**:投入 $x=0$,觀察 $y$ 值,解出 $b$。
62
+ * **Step 2 (找斜率 a)**:投入 $x=1$,觀察變化量,解出 $a$
63
+ * **Step 3 (驗算)**:利用新公式 $y=3x+5$ 驗算 $x=10$ 的結果並用圖表上點 ($10,35$) 進行再確認
64
+ * **Phase 5 (核心解鎖)**:輸入正確的 $a$ $b$ 值,完成任務
65
+ * **Phase 6 (脈衝同步)**:節奏遊戲在音樂節拍下進行擊,穩定核心能量,強化回饋感
66
+
67
+ ### C. 全等重案組 (Congruence District) - `congruence.html` [規劃中]
68
+ * **數學單元**:全等三角形判別 (SAS, ASA, SSS, RHS)。
69
+ * **劇情**:追捕偽裝大師「三角魔人」
70
+ * **玩法**:偵探
71
+ * **Phase 1 (蒐證)**:在案發現場使放大鏡蒐集「邊長」與「角度」線索
72
+ * **Phase 2 (指認)**:在警局指認牆上利用蒐集的條件篩選嫌疑犯
73
+ * **Phase 3 (審判/回顧)**:整理案情,總結全等性質(SAS, ASA...),並解釋為何 SSA (搖擺分身) 無法全等。
74
+
75
+ ### D. 平行建構區 (Parallel Skyline) - `parallel.html` [規劃中]
76
+ * **數學單元**:平行線截性質、特殊四邊形
77
+ * **劇情**:建設通往雲端的摩天大樓
78
+ * **玩法**:建築工程
79
+ * **平行光束**:調整雷射發射器角度,利用「內錯角相等」或「同側內角互補」原理接通橋樑
80
+ * **四邊形物流**:輸送帶上出現各種四邊形建材需根據對角線性質 (是否等長/垂直/平分) 快速分類
81
+ * **Phase 3 (完工驗收)**:展示建築藍圖,總結平行線截角性質與特殊四邊形的判別家族樹。
82
+
83
+ ## 5. 開發路徑 (Roadmap)
84
+ - [x] **Phase 1: 基礎建設**
85
+ - [x] 立 `index.html` 城市地圖
86
+ - [x] 生成 iPad 專背景圖
87
+ - [x] 統一導航系統 (Back to Map 按鈕)
88
+ - [x] **Phase 2: 現有程式優化**
89
+ - [x] `function.html`: 音量核心遊戲完整化、Phase 4 進階邏輯完備、節奏遊戲計分平衡。
90
+ - [x] `sequence.html`: 統一視覺風格,調整難度曲線。
91
+ - [ ] **Phase 3: 新關卡開發**
92
+ - [ ] 開發 `congruence.html` (全等重案組) 原型
93
+ - [ ] 開發 `parallel.html` (平行建構區) 原型
94
+
95
+ ---
96
+
97
+ ## 6. 修正經驗與開發筆記 (Dev Log)
98
+ 此區塊記錄開發過程中的重要迭代與修正經驗,供日後參考。
99
+
100
+ ### [Sequence Canyon] 視覺與機制修正 (Visual & Mechanics Polish)
101
+ * **視覺一致性的陷阱**: 修正了平台顏色提示過於明顯的問題,統一為碎石紋理,強制學生進行計算而非辨色。
102
+ * **提示機制**: 僅由數列前兩項提供視覺提示,後續完全依賴計算。
103
+ * **裝飾物配置**: 將裝飾物移至非遊戲路徑的安全平台,避免干擾判斷。
104
+
105
+ ### [Function Core] 進階教學設計 (Advanced Pedagogy Design)
106
+ * **鷹架理論 (Scaffolding) 的應用**:
107
+ * **變數分離**: 在推導 $y=ax+b$ 時,不要同時讓學生解兩個未知數。我們設計為:
108
+ 1. 先令 $x=0$,讓 $a$ 項消失,專注解 $b$ (截距)
109
+ 2. 知道 $b$ 後再令 $x=1$,專注解 $a$ (斜率)
110
+ * 這樣能讓學生理解每個係數幾何意義,而非單純背誦解聯立方程式
111
+ * **視覺與代數的連結 (Visual-Algebra Connection)**:
112
+ * **圖表驗證**: 在 Phase 4 Step 3,驗算出 $y=35$ 後,不僅僅是數字正確,程式還引導視線看向圖表上的 $(10, 35)$ 點。這強化了「代數解」何圖形上的點」是同一回事的概念
113
+ * **正向回饋系統 (Positive Feedback Loop)**:
114
+ * **避免挫折感**: 將傳統的 `alert()` 錯誤彈窗,改為「介面上的脈衝紅框」與「引導式錯誤訊息」。
115
+ * **具體化錯誤**: 錯誤訊息不是只說,而是說「如果 x=2, y=11, 因 11 = 2a+5, 所以 a=?」這將**錯誤轉化為另一個數學題目**,而非單純的懲罰。
116
+ * **節奏遊戲獎勵**: 在完艱澀運算後Phase 6 的節奏遊戲提供純粹的感官刺激與高分回饋 (5000分),平衡大腦疲勞
117
+ * **情境化總結 (Contextual Summary)**:
118
+ * 任務完成頁面不僅列出分數,更點出核心數學素養:「預測」與「建模」。讓學生知道他們剛剛做計算,本質上就是科學家預測未來的過程
119
+
120
+ ### [Function Core] 介面優化與除錯經驗
121
+ * **按鈕鎖定邏輯**: 驗證成功後鎖定按鈕 (`disabled`) 非常重要,可防止重複觸發事件或重複計分。需確保 CSS Selector 選中正確的按鈕。
122
+ * **樣式與動畫**:
123
+ * **跳動 vs 呼吸**: `animate-bounce` 用於錯誤訊息有時過於滑稽且難以閱讀改為 `animate-pulse` (呼吸燈/閃耀) 配合紅色邊框既有警示效果又保持質感
124
+ * **Canvas 文字清晰度**: 在 High-DPI 螢幕 (Retina) ,Canvas 必須正確縮放 (Scale by DevicePixelRatio)否則文字模糊
125
+
126
+ ---
127
+
128
+ ## 7. 專案製作群 (Credits)
129
+ * **遊戲設計**: 新竹縣精華國藍星宇老師
130
+ * **社群**: [萬物皆數](https://www.facebook.com/groups/1554372228718393)
131
+
132
+ *Created by Antigravity for 11402 Semester Project*
 
 
 
 
 
 
 
 
function.html CHANGED
@@ -30,6 +30,8 @@
30
  background-image: url('Assets/index/functionbg.png');
31
  background-size: cover;
32
  background-position: center;
 
 
33
  }
34
 
35
  /* Dark overlay for the whole page */
@@ -54,51 +56,13 @@
54
  z-index: 0;
55
  }
56
 
57
- /* Dialogue Box */
58
- #dialogue-box {
59
- position: fixed;
60
- bottom: 2rem;
61
- left: 2rem;
62
- width: 450px;
63
- max-width: 90%;
64
- z-index: 40;
65
- transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
66
- }
67
-
68
- @media (max-width: 768px) {
69
- #dialogue-box {
70
- left: 50%;
71
- transform: translateX(-50%);
72
- bottom: 1rem;
73
- width: 95%;
74
- }
75
- }
76
 
77
  #dialogue-box.translate-y-full {
78
  transform: translateY(150%);
79
  }
80
 
81
- /* Banner */
82
- #formula-banner {
83
- position: fixed;
84
- top: 2rem;
85
- right: 2rem;
86
- width: 380px;
87
- z-index: 35;
88
- transition: opacity 0.5s;
89
- }
90
-
91
- @media (max-width: 768px) {
92
- #formula-banner {
93
- top: 0.5rem;
94
- right: 0.5rem;
95
- width: 200px;
96
- /* Smaller on mobile */
97
- transform: scale(0.8);
98
- transform-origin: top right;
99
- }
100
- }
101
-
102
  /* Tech UI Styling */
103
  .glass-panel {
104
  background: rgba(15, 23, 42, 0.95);
@@ -149,32 +113,38 @@
149
  <canvas id="gameCanvas"></canvas>
150
 
151
  <!-- Main UI Container -->
152
- <div id="ui-container"
153
- class="relative z-10 w-full h-screen flex flex-col justify-end md:justify-center md:items-start pb-8 px-4 md:px-20 pointer-events-none">
154
-
155
- <!-- Formula Banner (Moved to left, stacked above dialogue) -->
156
- <div id="formula-banner" class="hidden mb-6 pointer-events-auto w-full md:w-[480px]">
157
- <div
158
- class="glass-panel px-8 py-5 rounded-2xl border-2 border-cyan-500/30 flex items-center justify-between shadow-[0_0_20px_rgba(34,211,238,0.2)] w-full">
159
- <span class="text-base text-slate-400 font-bold tracking-widest">核心運作法則</span>
160
- <div class="flex items-center gap-2 text-3xl font-bold font-tech">
161
- <span class="text-cyan-400">y</span>
162
- <span class="text-slate-500">=</span>
163
- <span class="text-slate-200">2</span>
164
- <span class="text-amber-400">x</span>
165
- <span class="text-slate-500">+</span>
166
- <span class="text-slate-200">3</span>
 
 
 
 
 
 
 
 
167
  </div>
168
  </div>
169
- </div>
170
 
171
- <!-- Interactive Dialogue Box -->
172
- <!-- Mobile: Bottom Sheet / Desktop: Left Panel -->
173
- <div id="dialogue-box"
174
- class="pointer-events-auto w-full md:w-[480px] glass-panel rounded-2xl p-8 transform transition-all duration-500 translate-y-20 opacity-0 md:translate-y-0 min-h-[200px] flex flex-col justify-center shadow-[0_0_50px_rgba(0,0,0,0.5)] bg-slate-900/90 frame-border">
175
- <!-- Dynamic Content Injected Here -->
176
  </div>
177
-
178
  </div>
179
 
180
  <!-- Start Overlay -->
@@ -186,11 +156,11 @@
186
  能源核心
187
  </h1>
188
  <h2
189
- class="text-2xl md:text-3xl font-bold font-tech text-cyan-400 mb-8 tracking-[0.5em] uppercase opacity-80">
190
  Energy Core
191
  </h2>
192
  <button onclick="startGame()"
193
- class="group relative px-10 py-4 bg-cyan-600/80 hover:bg-cyan-500 text-white font-bold rounded-xl transition-all shadow-[0_0_20px_rgba(6,182,212,0.4)] text-2xl tracking-widest border border-cyan-400/50 backdrop-blur-sm overflow-hidden">
194
  <span class="relative z-10">進入核心</span>
195
  <div
196
  class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover:animate-shimmer">
@@ -219,6 +189,7 @@
219
  STORY_BRIEF: 1,
220
  PHASE1_INTRO: 2,
221
  PHASE2_Q: 3, // General Question State
 
222
  PHASE2_PATTERN: 4,
223
  PHASE2_PREDICTION_INTRO: 4.5,
224
  PHASE3_CRISIS: 5,
@@ -263,6 +234,14 @@
263
  let qStep = 0;
264
  let currentState = STATE.INIT;
265
 
 
 
 
 
 
 
 
 
266
  // --- Audio ---
267
  const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
268
  function playSound(type) {
@@ -431,6 +410,96 @@
431
  ctx.scale(dpr, dpr);
432
  }
433
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  // --- State Management ---
435
  function enterPhase(phase) {
436
  currentState = phase;
@@ -439,21 +508,54 @@
439
 
440
  function updateUI(errorMsg = null) {
441
  const box = document.getElementById('dialogue-box');
442
- box.classList.remove('translate-y-full', 'opacity-0');
443
  const formulaBanner = document.getElementById('formula-banner');
444
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  let html = '';
446
 
447
  switch (currentState) {
448
  case STATE.STORY_BRIEF:
449
  formulaBanner.classList.add('hidden');
 
450
  html = `
451
- <h2 class="text-2xl font-bold text-amber-400 mb-4">MATH CITY 電力危機</h2>
452
- <p class="text-slate-200 text-lg leading-relaxed mb-6">
453
  能源核心是整個城市的電力來源,但現在供電系統出了一些問題,需要你來協助修復。<br><br>
454
  但在正式維修前,我們需要先檢測你的運算能力。
455
  </p>
456
- <button onclick="enterPhase(STATE.PHASE1_INTRO)" class="w-full py-4 bg-cyan-600 hover:bg-cyan-500 text-white rounded-xl font-bold text-xl shadow-lg">
457
  開始檢測能力
458
  </button>
459
  `;
@@ -462,16 +564,16 @@
462
  case STATE.PHASE1_INTRO:
463
  formulaBanner.classList.remove('hidden');
464
  html = `
465
- <h2 class="text-xl font-bold text-cyan-400 mb-2">核心運作法則確認</h2>
466
- <div class="text-center bg-slate-800/80 p-6 rounded-xl border border-cyan-500/30 mb-6">
467
- <div class="font-tech text-5xl mb-4">
468
  <span class="text-cyan-400">y</span> = 2<span class="text-amber-400">x</span> + 3
469
  </div>
470
- <p class="text-slate-300 text-3xl leading-relaxed font-bold">
471
- 每投入 <span class="text-amber-400 font-bold text-6xl mx-2 border-b-2 border-amber-400/50">x</span> 顆晶石,<br>就會產生 <span class="text-cyan-400 font-bold text-6xl mx-2 border-b-2 border-cyan-400/50">y</span> 點電力。
472
  </p>
473
  </div>
474
- <button onclick="startQPhase()" class="w-full py-4 bg-slate-700 hover:bg-slate-600 text-white rounded-xl font-bold text-lg border border-slate-500">
475
  我準備好了 (NEXT)
476
  </button>
477
  `;
@@ -481,30 +583,110 @@
481
  formulaBanner.classList.remove('hidden');
482
  const xVal = checkPoints[qStep];
483
  html = `
484
- <h3 class="text-lg font-bold text-white mb-2">能力檢測 (${qStep + 1}/${checkPoints.length})</h3>
485
- <p class="text-slate-300 mb-6 text-lg">
486
- 若投入 <span class="text-amber-400 font-bold text-2xl drop-shadow">${xVal}</span> 顆晶石 <span class="text-slate-400">(<span class="text-amber-400 font-bold">x=${xVal}</span>)</span>...
487
- </p>
488
 
489
- <div class="flex items-center justify-center gap-4 mb-2">
490
- <span class="font-tech text-3xl text-cyan-400">y = </span>
491
- <div class="relative w-32">
492
- <input id="ans-input" type="number" class="tech-input w-full p-2 text-3xl rounded-lg" placeholder="?" autofocus>
493
- <div class="absolute inset-0 border-2 border-slate-500/50 rounded-lg pointer-events-none"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
494
  </div>
495
  </div>
496
 
497
- ${errorMsg ? `<div class="text-red-400 text-center font-bold mt-2 animate-bounce">${errorMsg}</div>` : ''}
 
 
498
 
499
- <button onclick="checkQAnswer()" class="mt-6 w-full py-3 bg-cyan-600 hover:bg-cyan-500 text-white rounded font-bold text-lg">
500
- 確認電力
501
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
502
  `;
503
  break;
504
 
505
  case STATE.PHASE2_PATTERN:
 
506
  html = `
507
- <h3 class="text-lg font-bold text-white mb-4">規律分析</h3>
508
  <p class="text-slate-300 mb-6">觀察圖表上的這些點,它們呈現什麼樣的排列規律?</p>
509
  <div class="grid grid-cols-3 gap-4">
510
  <button onclick="checkPattern('A')" class="p-4 bg-slate-800 hover:bg-slate-700 rounded-xl border border-slate-600 font-bold text-slate-300 transition-all hover:border-cyan-400 hover:text-white">
@@ -521,12 +703,13 @@
521
  break;
522
 
523
  case STATE.PHASE2_PREDICTION_INTRO:
 
524
  html = `
525
  <h3 class="text-xl font-bold text-green-400 mb-4">分析完成</h3>
526
  <div class="bg-slate-800/80 p-6 rounded-xl border border-green-500/30 mb-6">
527
  <p class="text-slate-200 text-lg leading-relaxed font-bold">
528
  沒錯,所有點都連成一條直線...<br>
529
- <span class="text-cyan-400 block mt-2 text-2xl">也就是我們能預測未來!</span>
530
  </p>
531
  </div>
532
  <button onclick="enterPhase(STATE.PHASE3_CRISIS)" class="w-full py-4 bg-red-600 hover:bg-red-500 text-white rounded-xl font-bold text-xl shadow-lg animate-pulse">
@@ -536,22 +719,23 @@
536
  break;
537
 
538
  case STATE.PHASE3_CRISIS:
 
539
  html = `
540
  <div class="border-l-4 border-red-500 pl-6">
541
- <h3 class="text-2xl font-bold text-red-500 mb-2 blink-red">狀況:工業用電暴增!</h3>
542
- <p class="text-slate-200 mb-6 text-lg">
543
- 因為工業區需求激增,系統現在需要供給 <span class="text-cyan-400 font-bold text-3xl">203</span> 點電力 <span class="text-slate-400 text-base">(<span class="text-cyan-400 font-bold">y=203</span>)</span>。
544
  <br>根據 y=2x+3,請問要投入多少晶石 <span class="text-amber-400 font-bold">(x)</span> 才足夠?
545
  </p>
546
  <div class="flex items-center gap-2 mt-4 bg-slate-800/50 p-4 rounded-xl flex-wrap">
547
- <span class="font-tech text-amber-400 text-3xl whitespace-nowrap">x = </span>
548
- <input id="ans-input" type="number" class="tech-input flex-1 min-w-[100px] p-2 rounded text-3xl text-amber-400" placeholder="?" autofocus>
549
  <button onclick="checkFinalAnswer()" class="px-6 py-3 bg-red-600 hover:bg-red-500 text-white rounded font-bold shadow-[0_0_15px_rgba(239,68,68,0.5)] whitespace-nowrap flex-shrink-0">
550
  投入晶石
551
  </button>
552
  </div>
553
 
554
- ${errorMsg ? `<div class="text-amber-300 text-center font-bold mt-2 text-xl animate-bounce">${errorMsg}</div>` : ''}
555
 
556
  </div>
557
  `;
@@ -560,18 +744,18 @@
560
  case STATE.SUCCESS:
561
  html = `
562
  <div class="text-center">
563
- <h2 class="text-3xl font-bold text-green-400 mb-2 font-tech">SYSTEM STABILIZED</h2>
564
- <p class="text-slate-300 mb-6 text-lg">預測成功!核心運作恢復正常。</p>
565
 
566
  <!-- Teaching Content -->
567
  <div class="bg-slate-800/80 p-4 rounded-xl border border-cyan-500/30 mb-6 text-left">
568
- <h3 class="text-xl font-bold text-white mb-2 text-center">任務結:什麼是「函數」?</h3>
569
- <p class="text-slate-200 text-base leading-relaxed mb-3">
570
- 剛剛我們使用的「能量轉換法則」,在數學上就叫做<span class="text-amber-400 font-bold text-lg mx-1">「函數」(Function)</span>。
571
  </p>
572
- <p class="text-slate-300 text-sm leading-relaxed">
573
  函數就像一座<span class="text-cyan-400 font-bold">工廠</span>:<br>
574
- 它會把原料 <span class="font-bold text-amber-400">x (晶石)</span>,藉由固定的規則,轉換成產品 <span class="font-bold text-cyan-400">y (電力)</span>。
575
  </p>
576
  </div>
577
 
@@ -594,13 +778,13 @@
594
  formulaBanner.classList.remove('hidden'); // Ensure banner is visible
595
  html = `
596
  <div class="border-l-4 border-amber-500 pl-6">
597
- <h3 class="text-2xl font-bold text-amber-500 mb-2 blink-red">警告:公式異變!</h3>
598
- <p class="text-slate-200 mb-6 text-lg">
599
  檢測到晶石純度下降,能量轉換公式已改變!<br>
600
  系統無法自動運作,你必須重新推導公式。
601
  </p>
602
  <div class="bg-slate-800/50 p-4 rounded-xl mb-4 border border-slate-600">
603
- <span class="font-tech text-3xl block text-center tracking-widest">
604
  y = <span class="text-amber-400 animate-pulse">?</span>x + <span class="text-cyan-400 animate-pulse">?</span>
605
  </span>
606
  </div>
@@ -614,34 +798,39 @@
614
  case STATE.PHASE4_TEST_B:
615
  formulaBanner.classList.remove('hidden'); // Ensure banner is visible
616
  html = `
617
- <h3 class="text-xl font-bold text-white mb-2">步驟 1:數據校準</h3>
618
  <p class="text-slate-300 mb-4">
619
  你需要測試不同的晶石投入量,來推導出新的公式。
620
- <br><span class="text-sm text-slate-400">提示:什麼時候 <span class="text-red-400">a</span> 會消失不見? (試試投入 0 顆)</span>
621
  </p>
622
  <div class="flex items-center gap-2 mt-4 bg-slate-800/50 p-4 rounded-xl justify-center flex-wrap">
623
- <span class="font-tech text-amber-400 text-2xl whitespace-nowrap">投入 x = </span>
624
- <input id="test-input" type="number" class="tech-input w-24 p-2 rounded text-2xl text-amber-400 text-center" placeholder="0" value="0">
625
  <button onclick="runPhase4Test()" class="px-6 py-2 bg-amber-600 hover:bg-amber-500 text-white rounded font-bold shadow-lg whitespace-nowrap">
626
  啟動運作
627
  </button>
628
- </div>
629
- `;
 
 
630
  break;
631
 
632
  case STATE.PHASE4_FIND_B:
 
633
  html = `
634
- <h3 class="text-xl font-bold text-white mb-2">步驟 1:尋找初始值 (b)</h3>
635
- <p class="text-slate-300 mb-4">
636
  當投入 <span class="text-amber-400 font-bold">0</span> 顆時,
637
  電力顯示為 <span class="text-cyan-400 font-bold">5</span>。
 
 
638
  </p>
639
- <div class="flex items-center justify-center gap-2 text-2xl font-tech mb-6 bg-slate-800 p-4 rounded-lg">
640
  <span>b = </span>
641
  <input id="ans-input" type="number" class="w-24 bg-slate-700 text-cyan-400 text-center rounded border border-slate-500 focus:border-cyan-400 outline-none p-1" placeholder="?" autofocus>
642
  </div>
643
 
644
- ${errorMsg ? `<div class="text-red-400 text-center font-bold mb-4 animate-bounce bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
645
 
646
  <button onclick="checkPhase4B()" class="w-full py-3 bg-cyan-600 hover:bg-cyan-500 text-white rounded-xl font-bold">
647
  確認校準 Check
@@ -650,42 +839,43 @@
650
  break;
651
 
652
  case STATE.PHASE4_TEST_A:
 
653
  html = `
654
- <h3 class="text-xl font-bold text-white mb-2">步驟 2:數據校準 (a)</h3>
655
  <p class="text-slate-300 mb-4">
656
  現在已知 b=5。接下來需要找出變化率 a。
657
- <br><span class="text-sm text-slate-400">提示:試試看投入 1 顆晶石?</span>
658
  </p>
659
  <div class="flex items-center gap-2 mt-4 bg-slate-800/50 p-4 rounded-xl justify-center flex-wrap">
660
- <span class="font-tech text-amber-400 text-2xl whitespace-nowrap">投入 x = </span>
661
- <input id="test-input-a" type="number" class="tech-input w-24 p-2 rounded text-2xl text-amber-400 text-center" placeholder="1" value="1">
662
  <button onclick="runPhase4TestA()" class="px-6 py-2 bg-amber-600 hover:bg-amber-500 text-white rounded font-bold shadow-lg whitespace-nowrap">
663
  啟動運作
664
  </button>
665
  </div>
 
666
  `;
667
  break;
668
 
669
  case STATE.PHASE4_FIND_A:
670
  formulaBanner.classList.remove('hidden');
671
  html = `
672
- <h3 class="text-xl font-bold text-white mb-2">步驟 2:解出 a (Solve for a)</h3>
673
  <p class="text-slate-300 mb-4">
674
- 測試投入 <span class="text-amber-400 font-bold">1</span> 顆晶石,
675
- 電力變為 <span class="text-cyan-400 font-bold">8</span>。
676
  </p>
677
  <div class="bg-slate-800/50 p-4 rounded-xl mb-4 text-center border border-slate-700">
678
- <p class="text-sm text-slate-400"> x 增加 1,y 增加了 3 (5 → 8)</p>
679
- <span class="text-2xl font-tech block mt-2 text-white"><span class="text-cyan-400">8</span> = a <span class="text-amber-400">× 1</span> + 5</span>
680
  </div>
681
  <p class="text-slate-300 mb-4 text-center">請問 <span class="text-amber-400 font-bold">a</span> 是多少?</p>
682
- <div class="flex flex-wrap gap-2 mb-2 items-center justify-center text-2xl font-tech text-amber-400">
683
  <span>a =</span>
684
- <input id="ans-input" type="number" class="tech-input w-24 p-2 rounded text-2xl text-center text-amber-400" placeholder="?" autofocus>
685
  <button onclick="checkPhase4A()" class="w-full md:w-auto px-8 py-2 bg-amber-600 hover:bg-amber-500 text-white rounded-xl font-bold shadow-lg transition-transform hover:scale-105 whitespace-nowrap text-lg font-sans">確認</button>
686
  </div>
687
 
688
- ${errorMsg ? `<div class="text-red-400 text-center font-bold mb-2 animate-bounce bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
689
  `;
690
  break;
691
 
@@ -700,18 +890,18 @@
700
  case STATE.PHASE4_VERIFY:
701
  formulaBanner.classList.remove('hidden');
702
  html = `
703
- <h3 class="text-xl font-bold text-white mb-2">步驟 3:最終驗算</h3>
704
  <p class="text-slate-300 mb-4">
705
  假設公式為 <span class="font-bold text-white">y = 3x + 5</span>。
706
  <br>若投入 <span class="text-amber-400 font-bold">10</span> 顆晶石,電力應為多少?
707
  </p>
708
- <div class="flex flex-wrap gap-2 mt-6 items-center justify-center text-2xl font-tech text-white">
709
  <span>y =</span>
710
- <input id="ans-input" type="number" class="tech-input w-24 p-2 rounded text-2xl text-center text-white" placeholder="?" autofocus>
711
  <button onclick="checkPhase4Verify()" class="w-full md:w-auto px-8 py-2 bg-green-600 hover:bg-green-500 text-white rounded-xl font-bold shadow-lg transition-transform hover:scale-105 whitespace-nowrap text-lg font-sans">驗證</button>
712
  </div>
713
 
714
- ${errorMsg ? `<div class="text-red-400 text-center font-bold mt-4 animate-bounce bg-slate-900/50 p-2 rounded border border-red-500/30">${errorMsg}</div>` : ''}
715
  `;
716
  break;
717
 
@@ -719,8 +909,8 @@
719
  formulaBanner.classList.remove('hidden'); // Ensure banner is visible
720
  html = `
721
  <div class="text-center w-full">
722
- <h3 class="text-2xl font-bold text-amber-400 mb-6 drop-shadow-lg">輸入核心密碼 (Core Override)</h3>
723
- <div class="flex items-center justify-center gap-2 text-3xl md:text-4xl font-tech font-bold mb-8 bg-slate-900/80 p-6 rounded-2xl border-2 border-slate-700 w-full max-w-lg mx-auto">
724
  <span class="text-cyan-400">y</span>
725
  <span class="text-slate-500">=</span>
726
  <input id="final-a" type="number" class="w-20 bg-slate-800 text-amber-400 text-center rounded border border-slate-600 focus:border-amber-400 outline-none p-2" placeholder="?">
@@ -731,6 +921,7 @@
731
  <button onclick="unlockCore()" class="w-full py-4 bg-gradient-to-r from-amber-600 to-red-600 text-white rounded-xl font-bold text-xl shadow-lg hover:shadow-amber-500/50 transition-all border border-amber-500/30">
732
  解鎖大門 (UNLOCK)
733
  </button>
 
734
  </div>
735
  `;
736
  break;
@@ -738,14 +929,14 @@
738
  case STATE.PHASE6_INTRO:
739
  formulaBanner.classList.add('hidden');
740
  html = `
741
- <h2 class="text-2xl font-bold text-cyan-400 mb-4">核心同步程序 (Core Synchronization)</h2>
742
- <p class="text-slate-200 text-lg leading-relaxed mb-6">
743
  核心已解鎖,但能量極不穩定!<br>
744
  我們需要透過<span class="text-amber-400 font-bold">手動脈衝</span>來穩定核心頻率。
745
  </p>
746
  <div class="bg-slate-800/80 p-4 rounded-xl border border-cyan-500/30 mb-6 text-center">
747
  <p class="text-slate-300 font-bold mb-2">操作說明</p>
748
- <p class="text-sm text-slate-400">
749
  當光圈縮小至<span class="text-cyan-400">中心圓環</span>時,<br>
750
  點擊滑鼠、螢幕或按下空白鍵。
751
  </p>
@@ -764,66 +955,67 @@
764
  break;
765
 
766
  case STATE.PHASE7_SUMMARY:
767
- box.classList.remove('opacity-0', 'translate-y-20', 'translate-y-full');
768
- // Make sure it's centered and large
769
- box.className = "pointer-events-auto w-full md:w-[800px] glass-panel rounded-2xl p-8 transform transition-all duration-500 min-h-[400px] flex flex-col justify-start shadow-[0_0_50px_rgba(0,0,0,0.5)] bg-slate-900/95 frame-border absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2";
770
 
771
  const currentScore = rhythmState.score;
772
  const highScore = localStorage.getItem('math_city_score_function') || 0;
773
 
774
  html = `
775
  <div class="text-center w-full h-full flex flex-col items-center">
776
- <h2 class="text-3xl md:text-4xl font-black text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-green-400 mb-2 drop-shadow-[0_0_10px_rgba(34,211,238,0.5)] tracking-widest">
777
  任務完成 SYSTEM RESTORED
778
  </h2>
779
- <div class="text-amber-400 font-tech text-xl mb-4 tracking-[0.5em]">MISSION ACCOMPLISHED</div>
780
 
781
  <!-- Score Board -->
782
- <div class="flex gap-8 mb-6 bg-slate-800/80 px-8 py-4 rounded-xl border border-slate-600">
783
  <div class="text-center">
784
- <div class="text-xs text-slate-400 uppercase tracking-widest">本次得分</div>
785
  <div class="text-4xl font-black text-cyan-400 font-tech shimmer">${currentScore}</div>
786
  </div>
787
- <div class="w-px bg-slate-600"></div>
788
  <div class="text-center">
789
- <div class="text-xs text-slate-400 uppercase tracking-widest">歷史最高</div>
790
  <div class="text-4xl font-black text-amber-400 font-tech">${highScore}</div>
791
  </div>
792
  </div>
793
 
794
- <div class="grid grid-cols-1 md:grid-cols-2 gap-8 w-full text-left mb-8">
 
795
  <!-- Insight 1 -->
796
- <div class="bg-slate-800/50 p-6 rounded-xl border border-cyan-500/20 hover:border-cyan-500/50 transition-colors">
797
- <h3 class="flex items-center gap-2 text-xl font-bold text-cyan-400 mb-3">
798
- <span class="text-2xl">🔮</span> 預測未來 (Prediction)
799
  </h3>
800
- <p class="text-slate-300 leading-relaxed mb-3">
801
- 「當我們知道規則 <span class="bg-slate-900 px-1 rounded text-cyan-300 font-tech">y=ax+b</span> 時,就能精準預測未來的結果。」
802
  </p>
803
- <div class="text-sm text-slate-500 border-t border-slate-700 pt-2">
804
  回顧:就像你在階段三,算出需要投入多少晶石才能救急。
805
  </div>
806
  </div>
807
 
808
  <!-- Insight 2 -->
809
- <div class="bg-slate-800/50 p-6 rounded-xl border border-amber-500/20 hover:border-amber-500/50 transition-colors">
810
- <h3 class="flex items-center gap-2 text-xl font-bold text-amber-400 mb-3">
811
- <span class="text-2xl">📐</span> 數學建模 (Modeling)
812
  </h3>
813
- <p class="text-slate-300 leading-relaxed mb-3">
814
  「當規則改變或未知時,我們也能透過觀察數據的變化,反推回公式本身。」
815
  </p>
816
- <div class="text-sm text-slate-500 border-t border-slate-700 pt-2">
817
  回顧:就像你在階段四,透過測試 x=0 和 x=1,重新找回消失的 a 與 b。
818
  </div>
819
  </div>
820
  </div>
821
 
822
- <p class="text-slate-400 italic mb-8">
823
  「這就是科學家與工程師的超能力:觀察數據 <span class="text-white">→</span> 找出規則 <span class="text-white">→</span> 預測未來。」
824
  </p>
825
 
826
- <a href="index.html" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 px-12 rounded-full text-xl shadow-[0_0_20px_rgba(79,70,229,0.5)] transition-all transform hover:scale-105 hover:rotate-1">
827
  回到 Math City
828
  </a>
829
  </div>
@@ -832,7 +1024,7 @@
832
 
833
  case STATE.PHASE4_VERIFY_TEST:
834
  html = `
835
- <h3 class="text-xl font-bold text-amber-400 mb-2 font-tech tracking-widest border-b border-amber-500/30 pb-2">
836
  <span class="mr-2">⚡</span> 最終驗證 FINAL VERIFICATION
837
  </h3>
838
  <p class="text-slate-300 leading-relaxed mb-6">
@@ -841,9 +1033,9 @@
841
  </p>
842
 
843
  <div class="flex items-center gap-4 mb-2">
844
- <span class="text-2xl font-tech text-amber-500">x =</span>
845
  <div class="relative flex-1">
846
- <input type="number" id="test-input-verify" class="tech-input w-full p-4 rounded-xl text-2xl font-bold" placeholder="輸入 10" autofocus onkeydown="if(event.key==='Enter') runPhase4VerifyManual()">
847
  <div class="absolute right-4 top-1/2 -translate-y-1/2 text-slate-500 text-sm">晶石</div>
848
  </div>
849
  </div>
@@ -884,16 +1076,9 @@
884
 
885
  if (val === correctY) {
886
  playSound('success');
887
- addPoint(targetX, correctY);
888
-
889
- qStep++;
890
- if (qStep < checkPoints.length) {
891
- // Next question
892
- updateUI();
893
- } else {
894
- // All questions done
895
- setTimeout(() => enterPhase(STATE.PHASE2_PATTERN), 500);
896
- }
897
  } else {
898
  playSound('alarm');
899
  input.classList.add('animate-shake');
@@ -903,6 +1088,38 @@
903
  }
904
  }
905
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906
  function checkFinalAnswer() {
907
  const input = document.getElementById('ans-input');
908
  const val = parseInt(input.value);
@@ -952,18 +1169,26 @@
952
 
953
  function runPhase4Test() {
954
  const input = document.getElementById('test-input');
955
- const x = parseInt(input.value);
956
 
957
  if (isNaN(x)) return;
958
 
959
- const y = 3 * x + 5;
960
- addPoint(x, y);
961
- playSound('tick');
962
-
963
  if (x === 0) {
 
 
 
964
  setTimeout(() => {
965
  enterPhase(STATE.PHASE4_FIND_B);
966
  }, 1000);
 
 
 
 
 
 
 
 
 
967
  }
968
  }
969
 
@@ -985,21 +1210,32 @@
985
  }
986
  }
987
 
 
 
 
 
988
  function runPhase4TestA() {
989
  const input = document.getElementById('test-input-a');
990
  const x = parseInt(input.value);
991
 
992
- if (isNaN(x)) return;
 
 
 
 
 
993
 
994
  const y = 3 * x + 5;
995
  addPoint(x, y);
996
  playSound('tick');
997
 
998
- if (x === 1) {
999
- setTimeout(() => {
1000
- enterPhase(STATE.PHASE4_FIND_A);
1001
- }, 1000);
1002
- }
 
 
1003
  }
1004
 
1005
  function checkPhase4A() {
@@ -1013,7 +1249,7 @@
1013
  enterPhase(STATE.PHASE4_VERIFY);
1014
  } else {
1015
  playSound('alarm');
1016
- updateUI("錯誤!y 從 5 變成了 8增加了 3。每增加 1 x,就增加 a。所以 a 是...?");
1017
  }
1018
  }
1019
 
@@ -1023,10 +1259,28 @@
1023
  playSound('success');
1024
  // Auto verification visual
1025
  addPoint(10, 35);
1026
- updateUI("預測正確!投入 10 顆晶石,電力確實為 35!");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1027
 
1028
- // Delay for user to see the result before unlocking
1029
- setTimeout(() => enterPhase(STATE.PHASE5_UNLOCK), 2500);
 
1030
  } else {
1031
  playSound('alarm');
1032
  // Use on-screen hint instead of alert
@@ -1044,9 +1298,17 @@
1044
  enterPhase(STATE.PHASE6_INTRO);
1045
  } else {
1046
  playSound('alarm');
1047
- document.getElementById('start-overlay').classList.remove('hidden'); // Joke: reset? No just shake
1048
- // Simple shake logic via CSS usually, here just alert
1049
- alert("密碼錯誤!警報!");
 
 
 
 
 
 
 
 
1050
  }
1051
  }
1052
 
@@ -1225,6 +1487,9 @@
1225
  function handleMiss() {
1226
  rhythmState.energy = Math.max(0, rhythmState.energy - 2);
1227
  rhythmState.combo = 0;
 
 
 
1228
  spawnFloatingText("MISS", '#ef4444');
1229
  playSound('alarm');
1230
  }
@@ -1409,12 +1674,12 @@
1409
  // Desktop: Graph takes up right 55% of screen. Mobile: Centered.
1410
  const isDesktop = width > 768;
1411
 
1412
- const gw = isDesktop ? width * 0.5 : Math.min(600, width - 40);
1413
  const gh = isDesktop ? height * 0.6 : Math.min(400, height * 0.5);
1414
 
1415
- // On Desktop, position graph on the right side (Start at 45% width)
1416
  // On Mobile, center it
1417
- const gx = isDesktop ? (width * 0.45) : (width - gw) / 2;
1418
  const gy = isDesktop ? height * 0.2 : height * 0.1;
1419
 
1420
  // BG - Darker background for graph area to make it pop against the global image
@@ -1440,7 +1705,7 @@
1440
 
1441
  // Grid
1442
  ctx.textAlign = 'center';
1443
- ctx.font = 'bold 12px monospace';
1444
 
1445
  const jumpX = graphScale.xMax > 20 ? 10 : 1;
1446
  for (let i = 0; i <= graphScale.xMax; i += jumpX) {
@@ -1484,6 +1749,20 @@
1484
  ctx.stroke();
1485
  }
1486
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1487
  collectedPoints.forEach(p => {
1488
  const px = ox + p.x * unitX;
1489
  const py = oy - p.y * unitY;
@@ -1495,8 +1774,8 @@
1495
  ctx.stroke();
1496
 
1497
  // Color coded coordinates: (x, y)
1498
- ctx.font = 'bold 14px monospace';
1499
- const textY = py - 12; // Position above point
1500
 
1501
  // Measure text to center it
1502
  const strX = `${p.x}`;
@@ -1516,6 +1795,11 @@
1516
  ctx.fillStyle = '#22d3ee'; ctx.fillText(strY, cursorX + wY / 2, textY); cursorX += wY; // Cyan Y
1517
  ctx.fillStyle = '#cbd5e1'; ctx.fillText(")", cursorX + w2 / 2, textY);
1518
  });
 
 
 
 
 
1519
  }
1520
 
1521
  function updateParticles() {
@@ -1559,8 +1843,8 @@
1559
  drawRhythmBackground();
1560
  drawRhythmGame();
1561
  drawCore();
1562
- } else if (currentState === STATE.PHASE7_SUMMARY) {
1563
- // Summary Mode
1564
  drawRhythmBackground();
1565
  drawCore();
1566
  } else {
 
30
  background-image: url('Assets/index/functionbg.png');
31
  background-size: cover;
32
  background-position: center;
33
+ background-repeat: no-repeat;
34
+ background-attachment: fixed;
35
  }
36
 
37
  /* Dark overlay for the whole page */
 
56
  z-index: 0;
57
  }
58
 
59
+ /* Removed legacy .dialogue-default and #formula-banner positioning */
60
+ /* Layout is now handled by #dialogue-container utility classes in HTML */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  #dialogue-box.translate-y-full {
63
  transform: translateY(150%);
64
  }
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  /* Tech UI Styling */
67
  .glass-panel {
68
  background: rgba(15, 23, 42, 0.95);
 
113
  <canvas id="gameCanvas"></canvas>
114
 
115
  <!-- Main UI Container -->
116
+ <div id="ui-container" class="fixed inset-0 z-10 pointer-events-none">
117
+ <!--
118
+ Dialogue Container:
119
+ - Anchored Vertically Centered Left (Desktop/Tablet)
120
+ - Flex Column to stack Banner on top of Box
121
+ -->
122
+ <div id="dialogue-container"
123
+ class="fixed top-1/2 left-4 right-4 md:left-12 md:right-auto md:w-auto -translate-y-1/2 flex flex-col gap-4 items-center md:items-start pointer-events-none z-40">
124
+
125
+ <!-- Formula Banner -->
126
+ <div id="formula-banner" class="hidden pointer-events-auto w-full transition-opacity duration-500">
127
+ <div
128
+ class="glass-panel px-6 py-4 md:px-8 md:py-5 rounded-3xl border-2 border-cyan-500/30 flex items-center justify-center gap-6 shadow-[0_0_20px_rgba(34,211,238,0.2)]">
129
+ <span
130
+ class="text-base md:text-xl text-slate-400 font-bold tracking-widest whitespace-nowrap">核心運作法則</span>
131
+ <div class="flex items-center gap-1 md:gap-2 text-3xl md:text-4xl font-bold font-tech">
132
+ <span class="text-cyan-400">y</span>
133
+ <span class="text-slate-500">=</span>
134
+ <span class="text-slate-200">2</span>
135
+ <span class="text-amber-400">x</span>
136
+ <span class="text-slate-500">+</span>
137
+ <span class="text-slate-200">3</span>
138
+ </div>
139
  </div>
140
  </div>
 
141
 
142
+ <!-- Interactive Dialogue Box -->
143
+ <div id="dialogue-box"
144
+ class="pointer-events-auto w-full glass-panel rounded-3xl p-6 md:p-8 transform transition-all duration-500 translate-y-20 opacity-0 min-h-[200px] flex flex-col justify-center shadow-[0_0_50px_rgba(0,0,0,0.5)] bg-slate-900/90 frame-border">
145
+ <!-- Dynamic Content Injected Here -->
146
+ </div>
147
  </div>
 
148
  </div>
149
 
150
  <!-- Start Overlay -->
 
156
  能源核心
157
  </h1>
158
  <h2
159
+ class="text-3xl md:text-4xl font-bold font-tech text-cyan-400 mb-8 tracking-[0.5em] uppercase opacity-80">
160
  Energy Core
161
  </h2>
162
  <button onclick="startGame()"
163
+ class="group relative px-10 py-4 bg-cyan-600/80 hover:bg-cyan-500 text-white font-bold rounded-xl transition-all shadow-[0_0_20px_rgba(6,182,212,0.4)] text-3xl tracking-widest border border-cyan-400/50 backdrop-blur-sm overflow-hidden">
164
  <span class="relative z-10">進入核心</span>
165
  <div
166
  class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover:animate-shimmer">
 
189
  STORY_BRIEF: 1,
190
  PHASE1_INTRO: 2,
191
  PHASE2_Q: 3, // General Question State
192
+ PHASE2_COORDINATE: 3.5, // New Step: Coordinate Confirmation
193
  PHASE2_PATTERN: 4,
194
  PHASE2_PREDICTION_INTRO: 4.5,
195
  PHASE3_CRISIS: 5,
 
234
  let qStep = 0;
235
  let currentState = STATE.INIT;
236
 
237
+ // Guide Arrow State
238
+ let guideArrowState = {
239
+ active: false,
240
+ startTime: 0,
241
+ targetX: 0,
242
+ targetY: 0
243
+ };
244
+
245
  // --- Audio ---
246
  const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
247
  function playSound(type) {
 
410
  ctx.scale(dpr, dpr);
411
  }
412
 
413
+ function drawGuideArrows() {
414
+ const now = Date.now();
415
+ const elapsed = (now - guideArrowState.startTime) / 1000;
416
+ const duration = 2.0; // Show for 2 seconds
417
+
418
+ if (elapsed > duration) {
419
+ guideArrowState.active = false;
420
+ return;
421
+ }
422
+
423
+ const tx = guideArrowState.targetX;
424
+ const ty = guideArrowState.targetY;
425
+
426
+ const isDesktop = width > 768;
427
+
428
+ // Convert graph coords to canvas coords
429
+ // Logic sync with drawGraph
430
+ const padding = 60;
431
+ const gw = isDesktop ? width * 0.53 : Math.min(600, width - 40);
432
+ const gh = isDesktop ? height * 0.6 : Math.min(400, height * 0.5);
433
+
434
+ // On Desktop, position graph on the right side (Start at 44% width)
435
+ // On Mobile, center it
436
+ const gx = isDesktop ? (width * 0.44) : (width - gw) / 2;
437
+ const gy = isDesktop ? height * 0.2 : height * 0.1;
438
+
439
+ const ox = gx + padding;
440
+ const oy = gy + gh - padding;
441
+ const drawW = gw - padding * 2;
442
+ const drawH = gh - padding * 2;
443
+
444
+ const unitX = drawW / graphScale.xMax;
445
+ const unitY = drawH / graphScale.yMax;
446
+
447
+ const canvasX = ox + tx * unitX;
448
+ const canvasY = oy - ty * unitY;
449
+
450
+ const progress = Math.min(elapsed * 2, 1); // Animate in quickly
451
+
452
+ ctx.save();
453
+ ctx.lineWidth = 4;
454
+ ctx.lineCap = 'round';
455
+
456
+ // Draw X-axis Arrow (Amber, Up)
457
+ ctx.strokeStyle = '#F59E0B'; // Amber-500
458
+ ctx.fillStyle = '#F59E0B';
459
+ ctx.beginPath();
460
+ ctx.moveTo(canvasX, oy); // Start from X-axis
461
+ ctx.lineTo(canvasX, oy - (oy - canvasY) * progress);
462
+ ctx.stroke();
463
+
464
+ // Draw Arrowhead if fully extended
465
+ if (progress > 0.8) {
466
+ ctx.beginPath();
467
+ ctx.moveTo(canvasX, canvasY + 10);
468
+ ctx.lineTo(canvasX - 5, canvasY + 20);
469
+ ctx.lineTo(canvasX + 5, canvasY + 20);
470
+ ctx.fill();
471
+ }
472
+
473
+ // Draw Y-axis Arrow (Cyan, Right)
474
+ ctx.strokeStyle = '#22D3EE'; // Cyan-400
475
+ ctx.fillStyle = '#22D3EE';
476
+ ctx.beginPath();
477
+ ctx.moveTo(ox, canvasY); // Start from Y-axis
478
+ ctx.lineTo(ox + (canvasX - ox) * progress, canvasY);
479
+ ctx.stroke();
480
+
481
+ // Draw Arrowhead
482
+ if (progress > 0.8) {
483
+ ctx.beginPath();
484
+ ctx.moveTo(canvasX - 10, canvasY);
485
+ ctx.lineTo(canvasX - 20, canvasY - 5);
486
+ ctx.lineTo(canvasX - 20, canvasY + 5);
487
+ ctx.fill();
488
+ }
489
+
490
+ // Highlight Point
491
+ if (progress > 0.9) {
492
+ ctx.beginPath();
493
+ ctx.arc(canvasX, canvasY, 8, 0, Math.PI * 2);
494
+ ctx.fillStyle = '#FFFFFF';
495
+ ctx.fill();
496
+ ctx.strokeStyle = '#F59E0B';
497
+ ctx.stroke();
498
+ }
499
+
500
+ ctx.restore();
501
+ }
502
+
503
  // --- State Management ---
504
  function enterPhase(phase) {
505
  currentState = phase;
 
508
 
509
  function updateUI(errorMsg = null) {
510
  const box = document.getElementById('dialogue-box');
511
+ const container = document.getElementById('dialogue-container');
512
  const formulaBanner = document.getElementById('formula-banner');
513
 
514
+ // Default Container State:
515
+ // Desktop: Fixed Left Side (Start 2%, Width 46%). Vertically Centered.
516
+ // Reduced gap: Left ends at 40%, Right starts at 48%.
517
+ container.className = "fixed z-40 left-4 right-4 top-1/2 -translate-y-1/2 md:translate-y-0 md:top-0 md:bottom-0 md:left-[5%] md:right-auto md:w-[35%] flex flex-col gap-6 justify-center items-center pointer-events-none";
518
+
519
+ // Default Box State
520
+ box.className = "pointer-events-auto w-full glass-panel rounded-3xl p-6 md:p-8 transform transition-all duration-500 min-h-[200px] flex flex-col justify-center shadow-[0_0_50px_rgba(0,0,0,0.5)] bg-slate-900/90 frame-border";
521
+
522
+ // Specific overrides
523
+ if (currentState === STATE.PHASE7_SUMMARY) {
524
+ // Summary: Center Screen
525
+ container.className = "fixed inset-0 flex items-center justify-center pointer-events-auto z-50";
526
+ box.className = "w-[95%] md:w-[800px] glass-panel rounded-3xl p-8 transition-all duration-500 min-h-[400px] flex flex-col justify-start shadow-[0_0_80px_rgba(0,0,0,0.8)] bg-slate-900/95 frame-border relative";
527
+ } else if (currentState === STATE.PHASE6_GAME) {
528
+ box.classList.add('hidden');
529
+ container.classList.add('hidden');
530
+ } else if (currentState === STATE.STORY_BRIEF) {
531
+ // Story Brief: Remove fixed width, use full container width
532
+ // box.classList.add('md:w-[600px]'); // REMOVED
533
+ box.classList.remove('hidden');
534
+ container.classList.remove('hidden');
535
+ } else {
536
+ // Normal Phase: Allow full width (controlled by container)
537
+ // box.classList.add('md:w-[480px]'); // REMOVED
538
+ formulaBanner.className = "hidden pointer-events-auto w-full transition-opacity duration-500";
539
+
540
+ box.classList.remove('hidden');
541
+ container.classList.remove('hidden');
542
+ }
543
+
544
+ box.classList.remove('translate-y-full', 'opacity-0');
545
+
546
  let html = '';
547
 
548
  switch (currentState) {
549
  case STATE.STORY_BRIEF:
550
  formulaBanner.classList.add('hidden');
551
+
552
  html = `
553
+ <h2 class="text-4xl font-bold text-amber-400 mb-6">MATH CITY 電力危機</h2>
554
+ <p class="text-slate-200 text-3xl leading-relaxed mb-8">
555
  能源核心是整個城市的電力來源,但現在供電系統出了一些問題,需要你來協助修復。<br><br>
556
  但在正式維修前,我們需要先檢測你的運算能力。
557
  </p>
558
+ <button onclick="enterPhase(STATE.PHASE1_INTRO)" class="w-full py-5 bg-cyan-600 hover:bg-cyan-500 text-white rounded-xl font-bold text-3xl shadow-lg">
559
  開始檢測能力
560
  </button>
561
  `;
 
564
  case STATE.PHASE1_INTRO:
565
  formulaBanner.classList.remove('hidden');
566
  html = `
567
+ <h2 class="text-3xl font-bold text-cyan-400 mb-4">核心運作法則確認</h2>
568
+ <div class="text-center bg-slate-800/80 p-6 rounded-xl border border-cyan-500/30 mb-8">
569
+ <div class="font-tech text-6xl mb-6">
570
  <span class="text-cyan-400">y</span> = 2<span class="text-amber-400">x</span> + 3
571
  </div>
572
+ <p class="text-slate-300 text-4xl leading-relaxed font-bold">
573
+ 每投入 <span class="text-amber-400 font-bold text-7xl mx-2 border-b-2 border-amber-400/50">x</span> 顆晶石,<br>就會產生 <span class="text-cyan-400 font-bold text-7xl mx-2 border-b-2 border-cyan-400/50">y</span> 點電力。
574
  </p>
575
  </div>
576
+ <button onclick="startQPhase()" class="w-full py-5 bg-slate-700 hover:bg-slate-600 text-white rounded-xl font-bold text-xl border border-slate-500">
577
  我準備好了 (NEXT)
578
  </button>
579
  `;
 
583
  formulaBanner.classList.remove('hidden');
584
  const xVal = checkPoints[qStep];
585
  html = `
586
+ <h3 class="text-3xl font-bold text-white mb-2 text-center">能力檢測 (${qStep + 1}/${checkPoints.length})</h3>
 
 
 
587
 
588
+ <div class="grid grid-cols-2 gap-6 mb-2 mt-4">
589
+ <!-- Left: Question -->
590
+ <div class="flex flex-col gap-4">
591
+ <div class="text-cyan-400 font-bold text-xl border-b border-cyan-500/30 pb-2">1. 算出電力 (y)</div>
592
+ <p class="text-slate-300 text-lg">
593
+ 若投入 <span class="text-amber-400 font-bold text-2xl">${xVal}</span> <span class="text-slate-400 text-base">(<span class="text-amber-400">x=${xVal}</span>)</span> 顆晶石...
594
+ </p>
595
+ <div class="flex items-center justify-center gap-2 flex-grow">
596
+ <span class="font-tech text-3xl text-cyan-400">y = </span>
597
+ <div class="relative w-32">
598
+ <input id="ans-input" type="number" class="tech-input w-full p-2 text-3xl rounded-lg" placeholder="?" autofocus>
599
+ <div class="absolute inset-0 border-2 border-slate-500/50 rounded-lg pointer-events-none"></div>
600
+ </div>
601
+ </div>
602
+ <button onclick="checkQAnswer()" class="w-full h-16 bg-cyan-600 hover:bg-cyan-500 text-white rounded-xl font-bold text-xl shadow-lg flex items-center justify-center">
603
+ 確認電力
604
+ </button>
605
+ </div>
606
+
607
+ <!-- Right: Placeholder (Visible but Inactive) -->
608
+ <div class="flex flex-col gap-4 opacity-50 grayscale">
609
+ <div class="text-slate-400 font-bold text-xl border-b border-slate-500/30 pb-2">2. 坐標描點</div>
610
+ <div class="bg-slate-800/30 p-4 rounded-xl border border-slate-700/30 flex flex-col items-center justify-center flex-grow">
611
+ <div class="text-sm text-slate-500 mb-1">對應座標</div>
612
+ <div class="font-tech text-3xl font-bold text-slate-500">
613
+ (<span class="text-amber-400">${xVal}</span>, <span class="text-cyan-400">y</span>)
614
+ </div>
615
+ </div>
616
+ <button class="w-full h-16 bg-slate-700 text-slate-500 rounded-xl font-bold text-xl border border-slate-600 cursor-not-allowed flex items-center justify-center">
617
+ 描繪點 (PLOT)
618
+ </button>
619
  </div>
620
  </div>
621
 
622
+ ${errorMsg ? `<div class="text-red-400 text-center font-bold mt-2 border-2 border-red-400 border-dashed animate-pulse bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
623
+ `;
624
+ break;
625
 
626
+ case STATE.PHASE2_COORDINATE:
627
+ formulaBanner.classList.remove('hidden');
628
+ const xValC = checkPoints[qStep];
629
+ const yValC = 2 * xValC + 3;
630
+ html = `
631
+ <h3 class="text-3xl font-bold text-white mb-2 text-center">能力檢測 (${qStep + 1}/${checkPoints.length})</h3>
632
+
633
+ <div class="grid grid-cols-2 gap-6 mb-2 mt-4">
634
+ <!-- Left: Math Result (Confirmed) -->
635
+ <div class="flex flex-col gap-4 opacity-60 grayscale-[0.5] pointer-events-none">
636
+ <div class="text-cyan-400 font-bold text-xl border-b border-cyan-500/30 pb-2">1. 算出電力 (y)</div>
637
+ <p class="text-slate-300 text-lg">
638
+ 若投入 <span class="text-amber-400 font-bold text-2xl">${xValC}</span> <span class="text-slate-400 text-base">(<span class="text-amber-400">x=${xValC}</span>)</span> 顆晶石...
639
+ </p>
640
+ <div class="flex items-center justify-center gap-2 flex-grow">
641
+ <span class="font-tech text-3xl text-cyan-400">y = </span>
642
+ <div class="relative w-32">
643
+ <input type="text" class="tech-input w-full p-2 text-3xl rounded-lg bg-slate-800 text-cyan-400 border-cyan-500" value="${yValC}" disabled>
644
+ </div>
645
+ </div>
646
+ <button class="w-full h-16 bg-slate-700 text-slate-400 rounded-xl font-bold text-xl border border-slate-600 flex items-center justify-center">
647
+ 已確認
648
+ </button>
649
+ </div>
650
+
651
+ <!-- Right: Coordinate (Two States) -->
652
+ <div class="flex flex-col gap-4">
653
+ <div class="text-amber-400 font-bold text-xl border-b border-amber-500/30 pb-2">2. 坐標描點</div>
654
+
655
+ <!-- Step 1: Pre-substitution -->
656
+ <div id="coord-step-1" class="flex flex-col gap-4 h-full">
657
+ <div class="bg-slate-800/80 p-4 rounded-xl border border-cyan-500/50 flex flex-col items-center justify-center flex-grow">
658
+ <div class="text-sm text-cyan-300 font-bold mb-2 tracking-widest">對應座標</div>
659
+ <div class="font-tech text-4xl font-bold z-10 mb-2">
660
+ (<span class="text-amber-400">${xValC}</span>, <span class="text-cyan-400">y</span>)
661
+ </div>
662
+ </div>
663
+ <button onclick="substituteY()" class="w-full h-16 bg-indigo-600 hover:bg-indigo-500 text-white rounded-xl font-bold text-xl shadow-lg transition-colors flex items-center justify-center">
664
+ 將 y 代入
665
+ </button>
666
+ </div>
667
+
668
+ <!-- Step 2: Post-substitution (Hidden initially) -->
669
+ <div id="coord-step-2" class="hidden flex flex-col gap-4 h-full">
670
+ <div class="bg-slate-800/80 p-4 rounded-xl border border-cyan-500 shadow-[0_0_20px_rgba(34,211,238,0.2)] flex flex-col items-center justify-center relative overflow-hidden transform transition-all flex-grow animate-pulse-once">
671
+ <div class="text-sm text-cyan-300 font-bold mb-2 tracking-widest">對應座標</div>
672
+ <div class="font-tech text-4xl font-bold z-10 mb-2">
673
+ (<span class="text-amber-400">${xValC}</span>, <span class="text-cyan-400">${yValC}</span>)
674
+ </div>
675
+ <div class="absolute inset-0 bg-cyan-500/10 animate-pulse"></div>
676
+ </div>
677
+ <button onclick="plotCoordinate()" class="w-full h-16 bg-amber-500 hover:bg-amber-400 text-white rounded-xl font-bold text-xl shadow-lg animate-pulse whitespace-nowrap flex items-center justify-center">
678
+ 描繪點 (PLOT)
679
+ </button>
680
+ </div>
681
+ </div>
682
+ </div>
683
  `;
684
  break;
685
 
686
  case STATE.PHASE2_PATTERN:
687
+ formulaBanner.classList.remove('hidden'); // Ensure banner is visible
688
  html = `
689
+ <h3 class="text-3xl font-bold text-white mb-4">規律分析</h3>
690
  <p class="text-slate-300 mb-6">觀察圖表上的這些點,它們呈現什麼樣的排列規律?</p>
691
  <div class="grid grid-cols-3 gap-4">
692
  <button onclick="checkPattern('A')" class="p-4 bg-slate-800 hover:bg-slate-700 rounded-xl border border-slate-600 font-bold text-slate-300 transition-all hover:border-cyan-400 hover:text-white">
 
703
  break;
704
 
705
  case STATE.PHASE2_PREDICTION_INTRO:
706
+ formulaBanner.classList.remove('hidden'); // Keep banner visible
707
  html = `
708
  <h3 class="text-xl font-bold text-green-400 mb-4">分析完成</h3>
709
  <div class="bg-slate-800/80 p-6 rounded-xl border border-green-500/30 mb-6">
710
  <p class="text-slate-200 text-lg leading-relaxed font-bold">
711
  沒錯,所有點都連成一條直線...<br>
712
+ <span class="text-cyan-400 block mt-2 text-3xl">也就是我們能預測未來!</span>
713
  </p>
714
  </div>
715
  <button onclick="enterPhase(STATE.PHASE3_CRISIS)" class="w-full py-4 bg-red-600 hover:bg-red-500 text-white rounded-xl font-bold text-xl shadow-lg animate-pulse">
 
719
  break;
720
 
721
  case STATE.PHASE3_CRISIS:
722
+ formulaBanner.classList.remove('hidden');
723
  html = `
724
  <div class="border-l-4 border-red-500 pl-6">
725
+ <h3 class="text-3xl font-bold text-red-500 mb-2 blink-red">狀況:工業用電暴增!</h3>
726
+ <p class="text-slate-200 mb-6 text-xl">
727
+ 因為工業區需求激增,系統現在需要供給 <span class="text-cyan-400 font-bold text-4xl">203</span> 點電力 <span class="text-slate-400 text-base">(<span class="text-cyan-400 font-bold">y=203</span>)</span>。
728
  <br>根據 y=2x+3,請問要投入多少晶石 <span class="text-amber-400 font-bold">(x)</span> 才足夠?
729
  </p>
730
  <div class="flex items-center gap-2 mt-4 bg-slate-800/50 p-4 rounded-xl flex-wrap">
731
+ <span class="font-tech text-amber-400 text-4xl whitespace-nowrap">x = </span>
732
+ <input id="ans-input" type="number" class="tech-input flex-1 min-w-[100px] p-2 rounded text-4xl text-amber-400" placeholder="?" autofocus>
733
  <button onclick="checkFinalAnswer()" class="px-6 py-3 bg-red-600 hover:bg-red-500 text-white rounded font-bold shadow-[0_0_15px_rgba(239,68,68,0.5)] whitespace-nowrap flex-shrink-0">
734
  投入晶石
735
  </button>
736
  </div>
737
 
738
+ ${errorMsg ? `<div class="text-amber-300 text-center font-bold mt-2 text-xl border-2 border-amber-300 border-dashed animate-pulse bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
739
 
740
  </div>
741
  `;
 
744
  case STATE.SUCCESS:
745
  html = `
746
  <div class="text-center">
747
+ <h2 class="text-4xl font-bold text-green-400 mb-2 font-tech">SYSTEM STABILIZED</h2>
748
+ <p class="text-slate-300 mb-6 text-xl">預測成功!核心運作恢復正常。</p>
749
 
750
  <!-- Teaching Content -->
751
  <div class="bg-slate-800/80 p-4 rounded-xl border border-cyan-500/30 mb-6 text-left">
752
+ <h3 class="text-3xl font-bold text-white mb-2 text-center">任務結:什麼是「函數」?</h3>
753
+ <p class="text-slate-200 text-2xl leading-relaxed mb-3">
754
+ 剛剛我們使用的「能量轉換法則」,在數學上就叫做<span class="text-amber-400 font-bold text-3xl mx-1">「函數」(Function)</span>。
755
  </p>
756
+ <p class="text-slate-300 text-2xl leading-relaxed">
757
  函數就像一座<span class="text-cyan-400 font-bold">工廠</span>:<br>
758
+ 它會把原料 <span class="font-bold text-amber-400">x (晶石)</span>,藉由固定的規則,<br>轉換成產品 <span class="font-bold text-cyan-400">y (電力)</span>。
759
  </p>
760
  </div>
761
 
 
778
  formulaBanner.classList.remove('hidden'); // Ensure banner is visible
779
  html = `
780
  <div class="border-l-4 border-amber-500 pl-6">
781
+ <h3 class="text-3xl font-bold text-amber-500 mb-2 blink-red">警告:公式異變!</h3>
782
+ <p class="text-slate-200 mb-6 text-xl">
783
  檢測到晶石純度下降,能量轉換公式已改變!<br>
784
  系統無法自動運作,你必須重新推導公式。
785
  </p>
786
  <div class="bg-slate-800/50 p-4 rounded-xl mb-4 border border-slate-600">
787
+ <span class="font-tech text-4xl block text-center tracking-widest">
788
  y = <span class="text-amber-400 animate-pulse">?</span>x + <span class="text-cyan-400 animate-pulse">?</span>
789
  </span>
790
  </div>
 
798
  case STATE.PHASE4_TEST_B:
799
  formulaBanner.classList.remove('hidden'); // Ensure banner is visible
800
  html = `
801
+ <h3 class="text-3xl font-bold text-white mb-2">步驟 1:數據校準 B</h3>
802
  <p class="text-slate-300 mb-4">
803
  你需要測試不同的晶石投入量,來推導出新的公式。
804
+ <br><span class="text-base text-slate-400">提示:什麼時候 <span class="text-red-400">a</span> 會消失不見? (試試投入 0 顆)</span>
805
  </p>
806
  <div class="flex items-center gap-2 mt-4 bg-slate-800/50 p-4 rounded-xl justify-center flex-wrap">
807
+ <span class="font-tech text-amber-400 text-3xl whitespace-nowrap">投入 x = </span>
808
+ <input id="test-input" type="number" class="tech-input w-24 p-2 rounded text-3xl text-amber-400 text-center" placeholder="0" value="0">
809
  <button onclick="runPhase4Test()" class="px-6 py-2 bg-amber-600 hover:bg-amber-500 text-white rounded font-bold shadow-lg whitespace-nowrap">
810
  啟動運作
811
  </button>
812
+ ${errorMsg ? `<div class="text-amber-400 text-center font-bold mt-2 text-xl border-2 border-amber-400 border-dashed animate-pulse p-2 rounded">${errorMsg}</div>` : ''}
813
+
814
+ </div>
815
+ `;
816
  break;
817
 
818
  case STATE.PHASE4_FIND_B:
819
+ formulaBanner.classList.remove('hidden');
820
  html = `
821
+ <h3 class="text-3xl font-bold text-white mb-2">步驟 1:尋找初始值 (b)</h3>
822
+ <p class="text-slate-300 mb-4 text-2xl">
823
  當投入 <span class="text-amber-400 font-bold">0</span> 顆時,
824
  電力顯示為 <span class="text-cyan-400 font-bold">5</span>。
825
+ <br><span class="font-tech text-3xl block mt-2"><span class="text-cyan-400">5</span> = a × <span class="text-amber-400">0</span> + b</span>
826
+ <span class="font-tech text-3xl block"><span class="text-cyan-400">5</span> = b</span>
827
  </p>
828
+ <div class="flex items-center justify-center gap-2 text-3xl font-tech mb-6 bg-slate-800 p-4 rounded-lg">
829
  <span>b = </span>
830
  <input id="ans-input" type="number" class="w-24 bg-slate-700 text-cyan-400 text-center rounded border border-slate-500 focus:border-cyan-400 outline-none p-1" placeholder="?" autofocus>
831
  </div>
832
 
833
+ ${errorMsg ? `<div class="text-red-400 text-center font-bold mb-4 border-2 border-red-400 border-dashed animate-pulse bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
834
 
835
  <button onclick="checkPhase4B()" class="w-full py-3 bg-cyan-600 hover:bg-cyan-500 text-white rounded-xl font-bold">
836
  確認校準 Check
 
839
  break;
840
 
841
  case STATE.PHASE4_TEST_A:
842
+ formulaBanner.classList.remove('hidden');
843
  html = `
844
+ <h3 class="text-3xl font-bold text-white mb-2">步驟 2:數據校準 A</h3>
845
  <p class="text-slate-300 mb-4">
846
  現在已知 b=5。接下來需要找出變化率 a。
847
+ <br><span class="text-base text-slate-400">提示:試試看投入 1 顆晶石?</span>
848
  </p>
849
  <div class="flex items-center gap-2 mt-4 bg-slate-800/50 p-4 rounded-xl justify-center flex-wrap">
850
+ <span class="font-tech text-amber-400 text-3xl whitespace-nowrap">投入 x = </span>
851
+ <input id="test-input-a" type="number" class="tech-input w-24 p-2 rounded text-3xl text-amber-400 text-center" placeholder="1" value="1">
852
  <button onclick="runPhase4TestA()" class="px-6 py-2 bg-amber-600 hover:bg-amber-500 text-white rounded font-bold shadow-lg whitespace-nowrap">
853
  啟動運作
854
  </button>
855
  </div>
856
+ ${errorMsg ? `<div class="text-amber-400 text-center font-bold mt-2 text-xl border-2 border-amber-400 border-dashed animate-pulse p-2 rounded">${errorMsg}</div>` : ''}
857
  `;
858
  break;
859
 
860
  case STATE.PHASE4_FIND_A:
861
  formulaBanner.classList.remove('hidden');
862
  html = `
863
+ <h3 class="text-3xl font-bold text-white mb-2">步驟 2:解出 a (Solve for a)</h3>
864
  <p class="text-slate-300 mb-4">
865
+ 測試投入 <span class="text-amber-400 font-bold">${lastTestAX}</span> 顆晶石,
866
+ 電力變為 <span class="text-cyan-400 font-bold">${lastTestAY}</span>。
867
  </p>
868
  <div class="bg-slate-800/50 p-4 rounded-xl mb-4 text-center border border-slate-700">
869
+ <span class="text-3xl font-tech block mt-2 text-white"><span class="text-cyan-400">${lastTestAY}</span> = a <span class="text-amber-400">× ${lastTestAX}</span> + 5</span>
 
870
  </div>
871
  <p class="text-slate-300 mb-4 text-center">請問 <span class="text-amber-400 font-bold">a</span> 是多少?</p>
872
+ <div class="flex flex-wrap gap-2 mb-2 items-center justify-center text-3xl font-tech text-amber-400">
873
  <span>a =</span>
874
+ <input id="ans-input" type="number" class="tech-input w-24 p-2 rounded text-3xl text-center text-amber-400" placeholder="?" autofocus>
875
  <button onclick="checkPhase4A()" class="w-full md:w-auto px-8 py-2 bg-amber-600 hover:bg-amber-500 text-white rounded-xl font-bold shadow-lg transition-transform hover:scale-105 whitespace-nowrap text-lg font-sans">確認</button>
876
  </div>
877
 
878
+ ${errorMsg ? `<div class="text-red-400 text-center font-bold mb-2 border-2 border-red-400 border-dashed animate-pulse bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
879
  `;
880
  break;
881
 
 
890
  case STATE.PHASE4_VERIFY:
891
  formulaBanner.classList.remove('hidden');
892
  html = `
893
+ <h3 class="text-3xl font-bold text-white mb-2">步驟 3:最終驗算</h3>
894
  <p class="text-slate-300 mb-4">
895
  假設公式為 <span class="font-bold text-white">y = 3x + 5</span>。
896
  <br>若投入 <span class="text-amber-400 font-bold">10</span> 顆晶石,電力應為多少?
897
  </p>
898
+ <div class="flex flex-wrap gap-2 mt-6 items-center justify-center text-3xl font-tech text-white">
899
  <span>y =</span>
900
+ <input id="ans-input" type="number" class="tech-input w-24 p-2 rounded text-3xl text-center text-white" placeholder="?" autofocus>
901
  <button onclick="checkPhase4Verify()" class="w-full md:w-auto px-8 py-2 bg-green-600 hover:bg-green-500 text-white rounded-xl font-bold shadow-lg transition-transform hover:scale-105 whitespace-nowrap text-lg font-sans">驗證</button>
902
  </div>
903
 
904
+ ${errorMsg ? `<div class="text-red-400 text-center font-bold mt-4 border-2 border-red-400 border-dashed animate-pulse bg-slate-900/50 p-2 rounded">${errorMsg}</div>` : ''}
905
  `;
906
  break;
907
 
 
909
  formulaBanner.classList.remove('hidden'); // Ensure banner is visible
910
  html = `
911
  <div class="text-center w-full">
912
+ <h3 class="text-3xl font-bold text-amber-400 mb-6 drop-shadow-lg">輸入核心密碼 (Core Override)</h3>
913
+ <div class="flex items-center justify-center gap-2 text-4xl md:text-4xl font-tech font-bold mb-8 bg-slate-900/80 p-6 rounded-3xl border-2 border-slate-700 w-full max-w-lg mx-auto">
914
  <span class="text-cyan-400">y</span>
915
  <span class="text-slate-500">=</span>
916
  <input id="final-a" type="number" class="w-20 bg-slate-800 text-amber-400 text-center rounded border border-slate-600 focus:border-amber-400 outline-none p-2" placeholder="?">
 
921
  <button onclick="unlockCore()" class="w-full py-4 bg-gradient-to-r from-amber-600 to-red-600 text-white rounded-xl font-bold text-xl shadow-lg hover:shadow-amber-500/50 transition-all border border-amber-500/30">
922
  解鎖大門 (UNLOCK)
923
  </button>
924
+ ${errorMsg ? `<div class="text-red-400 text-center font-bold mt-4 border-2 border-red-400 border-dashed animate-pulse bg-slate-900/50 p-2 rounded text-xl">${errorMsg}</div>` : ''}
925
  </div>
926
  `;
927
  break;
 
929
  case STATE.PHASE6_INTRO:
930
  formulaBanner.classList.add('hidden');
931
  html = `
932
+ <h2 class="text-3xl font-bold text-cyan-400 mb-4">核心同步程序 (Core Synchronization)</h2>
933
+ <p class="text-slate-200 text-xl leading-relaxed mb-6">
934
  核心已解鎖,但能量極不穩定!<br>
935
  我們需要透過<span class="text-amber-400 font-bold">手動脈衝</span>來穩定核心頻率。
936
  </p>
937
  <div class="bg-slate-800/80 p-4 rounded-xl border border-cyan-500/30 mb-6 text-center">
938
  <p class="text-slate-300 font-bold mb-2">操作說明</p>
939
+ <p class="text-base text-slate-400">
940
  當光圈縮小至<span class="text-cyan-400">中心圓環</span>時,<br>
941
  點擊滑鼠、螢幕或按下空白鍵。
942
  </p>
 
955
  break;
956
 
957
  case STATE.PHASE7_SUMMARY:
958
+ // Break out of the container layout for Full Screen Center
959
+ // We modify the box styling directly to be fixed center
960
+ box.className = "fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-[95%] md:w-[70%] glass-panel rounded-3xl p-8 transition-all duration-500 min-h-[400px] max-h-[90vh] overflow-y-auto flex flex-col justify-start shadow-[0_0_80px_rgba(0,0,0,0.8)] bg-slate-900/95 frame-border z-50 pointer-events-auto";
961
 
962
  const currentScore = rhythmState.score;
963
  const highScore = localStorage.getItem('math_city_score_function') || 0;
964
 
965
  html = `
966
  <div class="text-center w-full h-full flex flex-col items-center">
967
+ <h2 class="text-5xl md:text-6xl font-black text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-green-400 mb-6 drop-shadow-[0_0_15px_rgba(34,211,238,0.6)] tracking-widest">
968
  任務完成 SYSTEM RESTORED
969
  </h2>
970
+ <div class="text-amber-400 font-tech text-4xl mb-10 tracking-[0.5em]">MISSION ACCOMPLISHED</div>
971
 
972
  <!-- Score Board -->
973
+ <div class="flex gap-16 mb-10 bg-slate-800/80 px-16 py-8 rounded-3xl border-2 border-slate-600 transform scale-110">
974
  <div class="text-center">
975
+ <div class="text-xl text-slate-400 uppercase tracking-widest mb-2">本次得分</div>
976
  <div class="text-4xl font-black text-cyan-400 font-tech shimmer">${currentScore}</div>
977
  </div>
978
+ <div class="w-px bg-slate-500"></div>
979
  <div class="text-center">
980
+ <div class="text-xl text-slate-400 uppercase tracking-widest mb-2">歷史最高</div>
981
  <div class="text-4xl font-black text-amber-400 font-tech">${highScore}</div>
982
  </div>
983
  </div>
984
 
985
+
986
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-12 w-full text-left mb-12 max-w-6xl">
987
  <!-- Insight 1 -->
988
+ <div class="bg-slate-800/50 p-10 rounded-3xl border border-cyan-500/20 hover:border-cyan-500/50 transition-colors">
989
+ <h3 class="flex items-center gap-4 text-4xl font-bold text-cyan-400 mb-6">
990
+ <span class="text-5xl">🔮</span> 預測未來 (Prediction)
991
  </h3>
992
+ <p class="text-slate-300 text-3xl leading-relaxed mb-4">
993
+ 「當我們知道規則 <span class="bg-slate-900 px-3 rounded text-cyan-300 font-tech">y=ax+b</span> 時,就能精準預測未來的結果。」
994
  </p>
995
+ <div class="text-xl text-slate-500 border-t border-slate-700 pt-4">
996
  回顧:就像你在階段三,算出需要投入多少晶石才能救急。
997
  </div>
998
  </div>
999
 
1000
  <!-- Insight 2 -->
1001
+ <div class="bg-slate-800/50 p-10 rounded-3xl border border-amber-500/20 hover:border-amber-500/50 transition-colors">
1002
+ <h3 class="flex items-center gap-4 text-4xl font-bold text-amber-400 mb-6">
1003
+ <span class="text-5xl">📐</span> 數學建模 (Modeling)
1004
  </h3>
1005
+ <p class="text-slate-300 text-3xl leading-relaxed mb-4">
1006
  「當規則改變或未知時,我們也能透過觀察數據的變化,反推回公式本身。」
1007
  </p>
1008
+ <div class="text-xl text-slate-500 border-t border-slate-700 pt-4">
1009
  回顧:就像你在階段四,透過測試 x=0 和 x=1,重新找回消失的 a 與 b。
1010
  </div>
1011
  </div>
1012
  </div>
1013
 
1014
+ <p class="text-slate-400 italic mb-12 text-3xl max-w-4xl whitespace-nowrap">
1015
  「這就是科學家與工程師的超能力:觀察數據 <span class="text-white">→</span> 找出規則 <span class="text-white">→</span> 預測未來。」
1016
  </p>
1017
 
1018
+ <a href="index.html" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-6 px-20 rounded-full text-4xl shadow-[0_0_40px_rgba(79,70,229,0.6)] transition-all transform hover:scale-105 hover:rotate-1">
1019
  回到 Math City
1020
  </a>
1021
  </div>
 
1024
 
1025
  case STATE.PHASE4_VERIFY_TEST:
1026
  html = `
1027
+ <h3 class="text-3xl font-bold text-amber-400 mb-2 font-tech tracking-widest border-b border-amber-500/30 pb-2">
1028
  <span class="mr-2">⚡</span> 最終驗證 FINAL VERIFICATION
1029
  </h3>
1030
  <p class="text-slate-300 leading-relaxed mb-6">
 
1033
  </p>
1034
 
1035
  <div class="flex items-center gap-4 mb-2">
1036
+ <span class="text-3xl font-tech text-amber-500">x =</span>
1037
  <div class="relative flex-1">
1038
+ <input type="number" id="test-input-verify" class="tech-input w-full p-4 rounded-xl text-3xl font-bold" placeholder="輸入 10" autofocus onkeydown="if(event.key==='Enter') runPhase4VerifyManual()">
1039
  <div class="absolute right-4 top-1/2 -translate-y-1/2 text-slate-500 text-sm">晶石</div>
1040
  </div>
1041
  </div>
 
1076
 
1077
  if (val === correctY) {
1078
  playSound('success');
1079
+ // Old: addPoint(targetX, correctY);
1080
+ // New: Go to Coordinate Confirmation Step
1081
+ enterPhase(STATE.PHASE2_COORDINATE);
 
 
 
 
 
 
 
1082
  } else {
1083
  playSound('alarm');
1084
  input.classList.add('animate-shake');
 
1088
  }
1089
  }
1090
 
1091
+ function substituteY() {
1092
+ playSound('swish'); // Optional sound
1093
+ document.getElementById('coord-step-1').classList.add('hidden');
1094
+ const step2 = document.getElementById('coord-step-2');
1095
+ step2.classList.remove('hidden');
1096
+ // Add a small pop animation or effect if desired
1097
+ }
1098
+
1099
+ function plotCoordinate() {
1100
+ const targetX = checkPoints[qStep];
1101
+ const correctY = 2 * targetX + 3;
1102
+
1103
+ addPoint(targetX, correctY);
1104
+
1105
+ // Trigger Animation
1106
+ guideArrowState.active = true;
1107
+ guideArrowState.startTime = Date.now();
1108
+ guideArrowState.targetX = targetX;
1109
+ guideArrowState.targetY = correctY;
1110
+
1111
+ playSound('tick');
1112
+
1113
+ qStep++;
1114
+ if (qStep < checkPoints.length) {
1115
+ // Next question
1116
+ enterPhase(STATE.PHASE2_Q);
1117
+ } else {
1118
+ // All questions done
1119
+ setTimeout(() => enterPhase(STATE.PHASE2_PATTERN), 500);
1120
+ }
1121
+ }
1122
+
1123
  function checkFinalAnswer() {
1124
  const input = document.getElementById('ans-input');
1125
  const val = parseInt(input.value);
 
1169
 
1170
  function runPhase4Test() {
1171
  const input = document.getElementById('test-input');
1172
+ const x = parseFloat(input.value);
1173
 
1174
  if (isNaN(x)) return;
1175
 
 
 
 
 
1176
  if (x === 0) {
1177
+ const y = 3 * x + 5;
1178
+ addPoint(x, y);
1179
+ playSound('tick');
1180
  setTimeout(() => {
1181
  enterPhase(STATE.PHASE4_FIND_B);
1182
  }, 1000);
1183
+ } else {
1184
+ playSound('alarm');
1185
+ const y = 3 * x + 5;
1186
+ // Don't plot point to avoid confusing them? Or plot it but don't proceed?
1187
+ // User said "don't plot point... show hint".
1188
+ // Hint display logic
1189
+ const hintY = 14;
1190
+ // Wait, if x=3, y=14. Formula y=ax+b becomes 14 = 3a + b.
1191
+ updateUI(`錯誤!若投入 ${x} 顆,電力為 ${y}。<br>公式變為 <span class="text-amber-400 font-tech">${y} = ${x}a + b</span>。<br>a 沒有消失,無法算出 b。只有什麼數字才能讓 a 消失?`);
1192
  }
1193
  }
1194
 
 
1210
  }
1211
  }
1212
 
1213
+ // Global var to store X logic for Phase 4 Step 2
1214
+ let lastTestAX = 1;
1215
+ let lastTestAY = 8;
1216
+
1217
  function runPhase4TestA() {
1218
  const input = document.getElementById('test-input-a');
1219
  const x = parseInt(input.value);
1220
 
1221
+ if (isNaN(x) || x < 1 || x > 9) {
1222
+ playSound('alarm');
1223
+ updateUI("為了方便觀察,請輸入 1 到 9 之間的整數。");
1224
+ input.value = '';
1225
+ return;
1226
+ }
1227
 
1228
  const y = 3 * x + 5;
1229
  addPoint(x, y);
1230
  playSound('tick');
1231
 
1232
+ // Store for next step text
1233
+ lastTestAX = x;
1234
+ lastTestAY = y;
1235
+
1236
+ setTimeout(() => {
1237
+ enterPhase(STATE.PHASE4_FIND_A);
1238
+ }, 1000);
1239
  }
1240
 
1241
  function checkPhase4A() {
 
1249
  enterPhase(STATE.PHASE4_VERIFY);
1250
  } else {
1251
  playSound('alarm');
1252
+ updateUI(`錯誤,${lastTestAY} = ${lastTestAX}a + 5 a = ?`);
1253
  }
1254
  }
1255
 
 
1259
  playSound('success');
1260
  // Auto verification visual
1261
  addPoint(10, 35);
1262
+ updateUI("預測正確!投入 10 顆晶石,電力確實為 35!<br><span class='text-amber-400 text-base'>(確認點連成一線...)</span>");
1263
+
1264
+ // Disable button
1265
+ const btn = document.querySelector('#dialogue-box button');
1266
+ if (btn) {
1267
+ btn.disabled = true;
1268
+ btn.classList.add('opacity-50', 'cursor-not-allowed', 'pointer-events-none');
1269
+ btn.innerHTML = '驗證成功 Verified';
1270
+ }
1271
+
1272
+ // Add guide text to look at the graph
1273
+ const inputContainer = document.querySelector('#test-input-verify')?.parentElement?.parentElement;
1274
+ if (inputContainer) {
1275
+ const guide = document.createElement('div');
1276
+ guide.className = "text-center text-green-400 font-bold mt-2 text-xl animate-pulse";
1277
+ guide.innerHTML = "請觀察圖表上的點 (10,35)...";
1278
+ inputContainer.insertAdjacentElement('afterend', guide);
1279
+ }
1280
 
1281
+ // Delay for user to see the result matches the line before unlocking
1282
+ // Extended delay to 3.5s to ensure they see the point on the line
1283
+ setTimeout(() => enterPhase(STATE.PHASE5_UNLOCK), 3500);
1284
  } else {
1285
  playSound('alarm');
1286
  // Use on-screen hint instead of alert
 
1298
  enterPhase(STATE.PHASE6_INTRO);
1299
  } else {
1300
  playSound('alarm');
1301
+ const box = document.querySelector('.bg-slate-900\\/80'); // Target container
1302
+ if (box) box.classList.add('animate-shake');
1303
+ setTimeout(() => box && box.classList.remove('animate-shake'), 500);
1304
+
1305
+ // Show hint on screen (simulated by updateUI call inside loop, but here distinct)
1306
+ // Just let user retry, maybe add text
1307
+ // Since updateUI overwrites everything, we just re-render PHASE5_UNLOCK with Error?
1308
+ // But unlockCore is called from button. We can append error.
1309
+ const inputs = document.querySelectorAll('#final-a, #final-b');
1310
+ inputs.forEach(i => i.value = '');
1311
+ updateUI("密碼錯誤!提示:y = 3x + 5 (a=3, b=5)");
1312
  }
1313
  }
1314
 
 
1487
  function handleMiss() {
1488
  rhythmState.energy = Math.max(0, rhythmState.energy - 2);
1489
  rhythmState.combo = 0;
1490
+ // Penalize score to prevent farming (losing energy to re-gain points)
1491
+ // Deduction = Potential gain of a Perfect Hit (500)
1492
+ rhythmState.score = Math.max(0, rhythmState.score - 500);
1493
  spawnFloatingText("MISS", '#ef4444');
1494
  playSound('alarm');
1495
  }
 
1674
  // Desktop: Graph takes up right 55% of screen. Mobile: Centered.
1675
  const isDesktop = width > 768;
1676
 
1677
+ const gw = isDesktop ? width * 0.53 : Math.min(600, width - 40);
1678
  const gh = isDesktop ? height * 0.6 : Math.min(400, height * 0.5);
1679
 
1680
+ // On Desktop, position graph on the right side (Start at 44% width)
1681
  // On Mobile, center it
1682
+ const gx = isDesktop ? (width * 0.44) : (width - gw) / 2;
1683
  const gy = isDesktop ? height * 0.2 : height * 0.1;
1684
 
1685
  // BG - Darker background for graph area to make it pop against the global image
 
1705
 
1706
  // Grid
1707
  ctx.textAlign = 'center';
1708
+ ctx.font = 'bold 20px "Orbitron", sans-serif';
1709
 
1710
  const jumpX = graphScale.xMax > 20 ? 10 : 1;
1711
  for (let i = 0; i <= graphScale.xMax; i += jumpX) {
 
1749
  ctx.stroke();
1750
  }
1751
 
1752
+ // Phase 4: Draw line y = 3x + 5 (After finding a)
1753
+ if (currentState >= STATE.PHASE4_VERIFY) {
1754
+ ctx.beginPath();
1755
+ ctx.strokeStyle = '#fbbf24'; // Amber Line for new formula
1756
+ ctx.lineWidth = 2;
1757
+ ctx.setLineDash([5, 5]); // Dashed line to show it's a prediction/new model
1758
+ const endX = graphScale.xMax;
1759
+ const endY = 3 * endX + 5;
1760
+ ctx.moveTo(ox, oy - 5 * unitY); // Start at (0, 5)
1761
+ ctx.lineTo(ox + endX * unitX, oy - endY * unitY);
1762
+ ctx.stroke();
1763
+ ctx.setLineDash([]); // Reset dash
1764
+ }
1765
+
1766
  collectedPoints.forEach(p => {
1767
  const px = ox + p.x * unitX;
1768
  const py = oy - p.y * unitY;
 
1774
  ctx.stroke();
1775
 
1776
  // Color coded coordinates: (x, y)
1777
+ ctx.font = 'bold 24px Orbitron'; // Point Numbers Increased
1778
+ const textY = py - 24; // Position higher up for larger font
1779
 
1780
  // Measure text to center it
1781
  const strX = `${p.x}`;
 
1795
  ctx.fillStyle = '#22d3ee'; ctx.fillText(strY, cursorX + wY / 2, textY); cursorX += wY; // Cyan Y
1796
  ctx.fillStyle = '#cbd5e1'; ctx.fillText(")", cursorX + w2 / 2, textY);
1797
  });
1798
+
1799
+ // Draw Guide Arrows if active (Coordinate Animation)
1800
+ if (guideArrowState.active && (currentState === STATE.PHASE2_COORDINATE || currentState === STATE.PHASE2_Q)) {
1801
+ drawGuideArrows();
1802
+ }
1803
  }
1804
 
1805
  function updateParticles() {
 
1843
  drawRhythmBackground();
1844
  drawRhythmGame();
1845
  drawCore();
1846
+ } else if (currentState >= STATE.PHASE6_INTRO) {
1847
+ // Intro & Summary Mode (Use Rhythm BG)
1848
  drawRhythmBackground();
1849
  drawCore();
1850
  } else {
sequence.html CHANGED
@@ -1010,6 +1010,11 @@
1010
  if (isStairsVanished) return;
1011
  isStairsVanished = true;
1012
 
 
 
 
 
 
1013
  platforms = platforms.filter(p => p.type !== 'temp_stair');
1014
 
1015
  const hint = document.getElementById('story-hint');
 
1010
  if (isStairsVanished) return;
1011
  isStairsVanished = true;
1012
 
1013
+ // Fix: Disable jumping (including coyote time) to force fall
1014
+ player.isGrounded = false;
1015
+ player.coyoteTimer = 0;
1016
+ player.jumpCount = MAX_JUMPS;
1017
+
1018
  platforms = platforms.filter(p => p.type !== 'temp_stair');
1019
 
1020
  const hint = document.getElementById('story-hint');