Files
spellforge/docs_dev/specs/2026-07-21-meta-progression-mvp-design.md

148 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 元进展 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 用明文 JSONEndlessRecords 的 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 = [] # 已解锁法术 idString
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.jsonST-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/20W12→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);图鉴/成就/核心解锁待后续」。