# 元进展 MVP（碎片 + 解锁树 · 法术only）设计规范

> **日期**：2026-07-21 · **状态**：设计已确认，待写实现计划
> **背景**：`game_design §7` 的元进展（以太碎片 / 解锁树 / 图鉴 / 成就）代码 0% 实现。本规范定义元进展的**第一个子项目**：碎片货币 + 法术解锁树的完整赚→花→门控闭环。图鉴、成就、核心解锁、挑战模式为后续子项目。

---

## 1. 目标与范围

**目标**：给游戏加入 Roguelite 长期粘性闭环——每局结束按波次获得**以太碎片**（全局持久、死亡不失），在主菜单花碎片**解锁进阶法术**，解锁后的法术进入商店掉落池。即使失败也有收获感。

> **设计原则（数据驱动）**：本系统所有**可调数值**（解锁费用、碎片奖励曲线）**必须落在 `data/*.json` 且能在游戏设计器里可视化调整**，不得硬编码。解锁费用走 `spells.json` 的 `meta.unlock_cost`（法术编辑器 meta 框可调，同 `shop_cost`/`mana_cost`）；碎片奖励曲线走 `balance.json` 的阈值表（平衡页签可调）。

**本次 MVP 包含**：
- 全局持久的碎片货币（`user://meta.json`）。
- 每局结束（死亡）按到达波次发放碎片。
- 主菜单「🔓 解锁」面板：显示碎片、花碎片解锁锁定法术。
- 内容门控：~6 张进阶法术**开局锁定**，其余开局可用；商店池只纳入"开局解锁 + 已解锁"的法术。
- ST-03「清除所有本地数据」一并清空 meta.json。

**明确不做（后续子项目/迭代）**：
- 核心(Core)解锁（依赖未实现的商店货架 B）、挑战模式解锁。
- 图鉴 Codex、成就 / 炼金日志。
- 通关首胜额外奖励（通关流程本身未实现）。
- 存档防篡改（单机本地存档，MVP 用明文 JSON；EndlessRecords 的 HMAC 不引入此处）。

---

## 2. 架构：组件与职责

### 2.1 `MetaProgress`（新 Autoload）—— 全局元进展状态
`scripts/autoloads/meta_progress.gd`，注册进 `project.godot`。
```
const PATH := "user://meta.json"
var aether: int = 0
var unlocked: Array = []      # 已解锁法术 id（String）

func _ready() -> void: load_meta()
func add_aether(n: int) -> void          # 累加并存档（n<=0 直接返回）
func is_unlocked(id: String) -> bool     # id 在 unlocked 中
func unlock(id: String, cost: int) -> bool  # 若已解锁 或 aether<cost → 返回 false（不扣费）；否则扣费+加入 unlocked+存档→true
func load_meta() -> void                 # 读 meta.json；缺失/损坏→aether=0, unlocked=[]
func save_meta() -> void                 # 写 { "aether": int, "unlocked": [...] }
func clear() -> void                     # aether=0, unlocked=[], 删 meta.json（ST-03 调用）
```
- 职责边界：只管"有多少碎片、解锁了哪些、怎么存读"。不知道法术费用（费用来自 spells.json，调用方传入）、不碰商店/UI。

### 2.2 `spells.json` 门控字段（可在法术编辑器调）
给 6 张进阶法术的 `meta` 加 `"unlock_cost": N`（放 **meta 内**：法术编辑器把 meta 当自由 JSON 保留，不会像顶层字段那样被 rebuild 丢弃，且策划可在 meta 框直接改费用）。无 `unlock_cost` 或 =0 → 开局解锁。**锁定法术集 = `meta.unlock_cost>0` 的法术**（即"改哪张法术的 unlock_cost 就等于改解锁树"，纯数据驱动）。

### 2.3 商店池门控（`shop_manager.gd`）
`_refresh_slots` 现有过滤 `shop_cost>0`，改为：
```
shop_cost>0 且 (unlock_cost==0 或 MetaProgress.is_unlocked(id))
```
锁定且未解锁的法术不进商店池。

### 2.4 碎片结算（数据驱动曲线，`balance.json` + `SettingsManager` + `combat_manager.gd`）
- `data/balance.json` 增字段 `"aether_thresholds": [[1,0],[5,3],[10,8],[15,14],[20,20]]`（`[到达波次, 发放碎片]` 阶梯，升序）。
- `SettingsManager`（已负责加载 balance.json）加 `aether_for_wave(wave) -> int`：返回**波次 ≤ 到达波次 的最高阶梯**对应的碎片（阶梯查表；缺字段则回退内置默认表并 `push_warning`）。超过最高阶梯按最高值封顶（Endless 亦然）。
- `combat_manager._on_player_died`（已在重置前捕获到达波次）新增：`MetaProgress.add_aether(SettingsManager.aether_for_wave(wave))`。
- **平衡页签（`balance_tab.gd`）**支持编辑 `aether_thresholds`（JSON 文本框，比照 core_tab 的 edges 编辑法），并在保存时保留该字段（不被 rebuild 丢弃）。

### 2.5 解锁界面（`main_menu.gd`）
主菜单新增「🔓 解锁」按钮 → 程序化 CanvasLayer 面板（比照现有 settings 面板写法）：
- 顶部显示 `以太碎片: N`。
- 遍历 `SpellRegistry` 中 `meta.unlock_cost>0` 的法术，逐行：`显示名 — 费用 X 碎片 — [解锁]按钮`。
- 按钮态：已解锁→显示"已解锁"禁用；未解锁且碎片不足→禁用；足够→可点，点击 `MetaProgress.unlock(id, cost)` 成功后刷新面板。
- 关闭按钮返回菜单。文案走 i18n（新增 `META_*` 键，4 语言）。

---

## 3. 数据流
```
开始→游玩→死亡
   │  combat_manager._on_player_died 捕获 wave
   ▼  MetaProgress.add_aether( SettingsManager.aether_for_wave(wave) )  → 存 meta.json（全局持久）
主菜单「🔓解锁」面板 → 花碎片 MetaProgress.unlock(id,cost) → unlocked[]+存档
   ▼
下一局商店 _refresh_slots：池 = shop_cost>0 且 (unlock_cost==0 或 已解锁)
```

---

## 4. 数值

**默认锁定的 6 张法术 + 费用**（写入各自 `meta.unlock_cost`）：
| 法术 id | unlock_cost | 说明 |
| :--- | :--- | :--- |
| action_energy_orb | 4 | 慢速高伤 |
| action_frost_bolt | 5 | 冻结 |
| action_poison_pool | 6 | 地面毒池(zone) |
| logic_loop | 6 | 循环(强力) |
| action_chain_bolt | 8 | 连锁闪电 |
| action_summon_turret | 10 | 召唤炮台 |

其余 ~13 张（spark_bolt / fire_bolt / poison_dart / laser_beam / water_wave / damage_plus / spread_mod / double_cast / pierce_plus / trigger_on_hit / every_n_shots / if_hp_below / if_enemy_nearby）开局可用。解锁全部 = 39 碎片 ≈ 5 局（W10 档）。以上费用均写在各法术 `meta.unlock_cost`，**法术编辑器可调**。

**碎片奖励阈值表**（`balance.json.aether_thresholds`，**平衡页签可调**）：`[[1,0],[5,3],[10,8],[15,14],[20,20]]`——`[波次,碎片]` 阶梯，到达波次取 ≤ 它的最高阶梯值（如 W12→8、W20→20、Endless W25→20 封顶）。贴合 §7.1 的 0/3/8/20。

> `action_plasma_storm` 是共鸣产物、`shop_cost=0`，本就不在商店池，无需门控。

---

## 5. 边界与默认
- `meta.json` 缺失/损坏 → 全新档（aether 0、unlocked 空），不崩溃。
- 已解锁法术再点解锁 → `is_unlocked` 提前拦截 / 按钮禁用，不重复扣费。
- 碎片不入局内存档（ProfileManager A/B）——它是全局的，只在 meta.json。
- ST-03 清除数据：`SettingsManager.clear_all_local_data` 增调 `MetaProgress.clear()`。
- 门控只影响**商店掉落池**；玩家的初始预设卡组（action_spark_bolt）不受影响。

---

## 6. 测试计划（Godot MCP 运行时实测）
1. **持久往返**：`MetaProgress.add_aether(10)` → `save_meta` → 新实例 `load_meta` → aether=10、unlocked 保留。
2. **解锁扣费**：aether=5，`unlock("action_frost_bolt", 5)`→true、aether=0、is_unlocked=true；再 `unlock("action_chain_bolt", 8)`→false（不足）、状态不变。
3. **商店池门控**：unlocked 为空时 `_refresh_slots` 抽样多次，锁定法术（如 chain_bolt）**不出现**；解锁后再抽，可出现。
4. **碎片阈值表**：`SettingsManager.aether_for_wave` 对 1/5/10/20 返回 0/3/8/20；W12→8、W25→20（封顶）；改 `balance.json.aether_thresholds` 后值随之变（数据驱动）。
5. **清除数据**：`MetaProgress.clear()` 后 aether=0/unlocked 空/文件删除。
6. **解锁面板**：主菜单点「解锁」渲染碎片数 + 6 行锁定法术；截图确认；点解锁按钮态正确变化。
7. **编辑器可调**：`balance_tab` 编辑 `aether_thresholds` 保存后不丢字段（`execute_editor_script` 走 core-tab 同款 fresh-load 往返验证）；法术编辑器改 `meta.unlock_cost` 保留。

---

## 7. 涉及文件清单
| 文件 | 改动 |
| :--- | :--- |
| `scripts/autoloads/meta_progress.gd` | **新建** MetaProgress autoload |
| `project.godot` | 注册 MetaProgress autoload |
| `data/spells.json` | 6 张法术 meta 加 `unlock_cost`（费用，法术编辑器可调） |
| `data/balance.json` | 加 `aether_thresholds` 阈值表（碎片曲线，平衡页签可调） |
| `scripts/autoloads/shop_manager.gd` | `_refresh_slots` 过滤加门控条件 |
| `scripts/domain/combat/combat_manager.gd` | `_on_player_died` 调 `MetaProgress.add_aether(SettingsManager.aether_for_wave(wave))` |
| `scripts/autoloads/settings_manager.gd` | 加载 `aether_thresholds` + `aether_for_wave()`；`clear_all_local_data` 增调 `MetaProgress.clear()` |
| `addons/game_designer/balance_tab.gd` | 平衡页签支持编辑并保留 `aether_thresholds`（**编辑器可调**） |
| `scenes/ui/main_menu.gd` | 「🔓解锁」按钮 + 解锁面板 |
| `translations/*.po` | `META_*` 四语键 |

---

## 8. 完成定义 (DoD)
- 上述 6 项测试在 Godot 4.6 运行时全绿、0 编译错误。
- 死亡后碎片按波次增加并持久（重启游戏仍在）。
- 主菜单解锁面板可花碎片解锁法术；解锁后该法术出现在商店池、未解锁前不出现。
- ST-03 清除数据把碎片/解锁清空。
- **所有可调数值（解锁费用 / 碎片曲线）均在数据文件且可在游戏设计器里改**：法术编辑器改 `unlock_cost`、平衡页签改 `aether_thresholds`，保存后运行时生效、不丢字段。
- `game_design §7` 现状 callout 更新为「碎片+解锁树 已实现(MVP)；图鉴/成就/核心解锁待后续」。
