# 魔法工匠：骨架垂直切片开发计划
# Arcane Artificer — Skeleton Vertical Slice Development Plan

> **策略定义**：骨架垂直切片 (Skeleton Vertical Slice) 不是"先把所有系统建完再组合"（横向分层），而是先用最薄的一刀从**输入到渲染切穿所有层**，证明核心体验可运行；再每轮迭代让这刀"更宽"——加入更多内容与深度。
>
> - **骨架**：最小可运行的端到端技术脚手架，不含游戏内容
> - **垂直切片**：每个里程碑均可独立游玩与测试，交付"小但可玩的游戏"
> - **顺序原则**：①风险最高的系统最先实现；②每个切片回答一个明确的"能做到吗"问题；③下一个切片依赖上一个切片的通过

---

## 一、技术风险登记表 (Risk Register)

驱动切片顺序的核心不确定性：

| 风险 ID | 风险描述 | 影响系统 | 严重度 | 解决切片 |
| :--- | :--- | :--- | :--- | :--- |
| R-01 | `PackedFloat32Array` SoA 在 GDScript 下 2000 弹幕能否达到 60fps | BulletManager | 🟢 已缓解 | S0 ✅ |
| R-02 | SpellEvaluator 的 while 循环与 SubPayloadRegistry 在高频施法下无 GC 抖动 | SpellEvaluator | 🟢 已缓解 | S0~S1 ✅ |
| R-03 | 1000 敌人的 Boid AI + SpatialGrid 查询帧时间是否可控 | EnemyManager | 🟢 已缓解 | S0 ✅ |
| R-04 | Area2D 信号回调是否足够处理 500-2000 弹幕碰撞（切换阈值验证） | BulletManager/碰撞 | 🟡 部分 | S1 ✅（当前用 SpatialGrid；完整阈值验收留 S6） |
| R-05 | `compile_wand` 预编译后 `CompiledDeck` 与 `SubPayloadRegistry` 竞争条件实测 | SpellEvaluator | 🟢 已缓解 | S2~S3 ✅ |
| R-06 | CIRCUIT 拓扑 `_flatten_circuit` + 嵌套 LOGIC_FORK 实际运行正确性 | SpellEvaluator | 🟡 中 | S5 |
| R-07 | `GPUParticles2D` 池化（`VFXManager`）与 MultiMesh 渲染切换性能实测 | VFXManager/渲染 | 🟡 部分 | S4 ✅（色块占位池化；粒子素材与 MultiMesh 留 S6） |
| R-08 | GDScript ↔ C# 跨语言边界开销实测：`PackedFloat32Array.AsSpan()` 零拷贝是否生效、批量通知 vs 逐条回调性能差异；从 S0 建立 C# 项目基线（ADR-L1）| BulletManager/EnemyManager/SpatialGrid | 🟠 高 | S0 |
| R-09 | W15+ Elite 需 `NavigationAgent2D` 或 FlowField 与 Boid SoA 共存，帧时间增量是否可控（ADR-A4） | EnemyManager / 关卡设计 | 🟡 中 | S5 |

---

## 二、切片总览 (Slice Overview)

```
S0  技术骨架    → 证明：ECS 架构能运行                    ✅ 已完成 (2026-06)
S1  最小战斗    → 证明：子弹打到敌人，敌人死亡              ✅ 已完成 (2026-06)
S2  核心游戏循环 → 证明：波次→商店→升级→下一波 完整闭合     ✅ 已完成 (2026-06)
S3  法术管道    → 证明：ACTION+MODIFIER+TRIGGER 链可运行   ✅ 已完成 (2026-06)
S4  战斗深度    → 证明：状态效果、VFX、DPS 系统协同         ✅ 已完成 (2026-06)
S5  高级构建    → 证明：MATRIX/CIRCUIT Core、LOGIC、召唤、地面效果  ✅ 功能完成 (P-S5-01~07+AI-01 实测通过；唯 C# 压力 P-S5-ZM-01 顺延)
S6  润色发布    → 证明：60fps 稳定、内容充足、手感好         ⬜ 未开始
```

**当前主场景**：`res://scenes/main/combat_s2.tscn`（S2 起）

> 每个切片结束前，下列问题必须回答"是"才可进入下一个切片（出口检查 Exit Gate）。

### 实施进度摘要

| 切片 | 状态 | 主场景 / 入口 | 备注 |
| :--- | :---: | :--- | :--- |
| S0 | ✅ | `combat_test.tscn` | GDScript SoA 回退路径实测通过；`architecture_design.md §4.5` 已填 S0 实测值（Bullet 0.37ms / Enemy 0.23ms） |
| S1 | ✅ | `combat_test.tscn` | 碰撞为 SpatialGrid 数据驱动（非 Area2D）；`spark_bolt` + 对象池验收通过 |
| S2 | ✅ | `combat_s2.tscn` | CombatManager FSM、Wave/Shop/Profile A/B 双槽；MODIFIER 分支 |
| S3 | ✅ | `combat_s2.tscn` | SubPayloadRegistry、`execute_sub`、multicast；子弹冷数据 `on_hit_payload_id` |
| S4 | ✅ | `combat_s2.tscn` | StatusManager / VFXManager / DpsTracker；`fire_bolt`/`poison_dart`；蓄力边沿 |
| S5 | ✅* | `combat_s2.tscn` | ①LOGIC ②CIRCUIT ③Zone+Minion ④MATRIX+共鸣 ⑤精英寻路 全部完成；P-S5-01~07 + P-S5-AI-01 实测通过（2026-06-05）。*唯 P-S5-ZM-01（C# 压力）按 R-08 顺延 |
| S6 | ⬜ | — | — |

> **⚠️ 实现方式偏差备忘（代码核对 2026-06-05，全切片适用）**
> 1. **纯数据驱动架构全面落地 ✅（2026-06-05）**：**游戏设计器 EditorPlugin**（`addons/game_designer/`，编辑器底部面板，**7 个可视化分页**：法术/敌人/波次/Core/共鸣/状态/平衡）+ `res://data/*.json`（7 个数据文件），策划/美术/开发零代码配置全部游戏内容。**已移除所有硬编码内容副本与回退**——JSON 是唯一权威源，缺失/格式错误时 `push_error` 明确报错（不静默）。各 Manager 纯加载：SpellRegistry←spells.json(20法术)、EnemyManager←enemies.json(6原型)、WaveManager←waves.json(20波)、WandPreset←cores.json(Core)、SpellEvaluator←resonance.json(共鸣)、StatusRegistry←status_effects.json(状态)、SettingsManager←balance.json(乘子+Boss血)。`WandPreset` 删除全部 `make_spell_*`/硬编码 Core；`SpellRegistry` 删 `.tres`扫描+builtins；`ShopManager` 删 fallback+过滤 shop_cost>0。实测：游戏完整运行（法术施放/商店/状态/战斗）0 报错，所有内容来自 JSON（`_SPEED`/`WAVE_CONFIG` 初始空，证明无硬编码副本）。文档 `handbook/07_game_designer.md`。**余项**：运行时热重载（目前需 F5 重启）、玩法常量配置（玩家速度/商店价/掉落等仍为代码常量，属可调参数非内容）。
> 2. **UI 全程序化构建**：`shop.tscn`/`hud.tscn`/`inventory.tscn`/`hud_dps.tscn` **均不存在**；HUD 与商店在 `scenes/main/combat_s2.gd` 内用 `Label.new()`/`Button.new()` 动态生成。
> 3. **S3 拖拽背包 UI（`inventory.tscn`）缺失**：法术目前仅能经商店点击购买，无拖拽装配。
> 4. **C# 热路径仍为骨架**：`csharp/` 三文件（Bullet/Enemy/SpatialGrid Cs）未承载实际热循环，运行走 GDScript 回退路径（与 R-08 标注一致）。

---

## S0 — 技术骨架 (Technical Skeleton) ✅ 已完成

### 目标
> **"空荡荡的竞技场里，一颗子弹能在 2000 颗同屏的情况下飞出去，并在 Profiler 中证明帧时间 < 16ms。"**

技术上没有游戏性，只有数据流动与性能验证。

### 范围

| 要实现的系统 | 最小实现内容 | 暂不实现 |
| :--- | :--- | :--- |
| `EventBus` / `EventID` | 整数 ID 常量 + 简单 emit/subscribe | 事件类型校验、跨语言边界 |
| `BulletManager` | PackedFloat32Array SoA (STRIDE=12)、spawn/despawn、匀速位移积分、swap-and-pop 删除 | 碰撞、归航、弹射、VFX |
| `EnemyManager` | SoA (STRIDE=8)、spawn/despawn、直线追玩家移动、`_visible_flags` LOD；屏外低频 Boid 分离力（`_update_separation_only`，`OFFSCREEN_SEPARATION_INTERVAL=10`，P6-N1） | AI 行为状态机、攻击、死亡动画 |
| `SpatialGrid` | 固定 Cell 网格、每帧 dirty-list 方案重建、`query_circle` | 超大弹体豁免路径（留 stub） |
| `PlayerManager` | WASD 移动（`MOVE_THRESHOLD_NORM`）、位置查询接口 | 法杖施法、蓄力、冲刺 |
| `ObjectPool` (通用) | `Array` 栈 + `reset()` 接口约定 | 无 |
| `TimeManager` | 时间缩放接口、`GameTick` 基础计数（`implementation_plan.md §2.1` 核心库 Layer 0 表）；Autoload 注册 | 帧率节流、RenderFrame 分离 |
| C# 项目初始化 | `.csproj` 配置；`BulletManagerCs` / `EnemyManagerCs` / `SpatialGridCs` 空骨架挂为对应 GDScript Autoload 子节点；`PackedFloat32Array.AsSpan()` 零拷贝读写基准验证（ADR-L1）| 完整热路径逻辑（S1 起逐步填充）|
| 渲染同步 | N 个敌人 `Node2D.position` 帧末同步（`_visible_flags` 筛选） | 动画、特效 |

### 关键文件

```
scripts/autoloads/event_id.gd          # EventID 常量 (参考 implementation_plan.md §2.1)
scripts/autoloads/event_bus.gd         # EventBus Autoload
scripts/autoloads/bullet_manager.gd    # SoA BulletManager GDScript 接口层 (architecture_design.md §4.2)
scripts/autoloads/enemy_manager.gd     # SoA EnemyManager GDScript 接口层 (implementation_plan.md §2.3.C)
scripts/autoloads/time_manager.gd      # TimeManager Autoload (implementation_plan.md §2.1 Layer 0)
scripts/core/object_pool.gd            # 通用对象池 (implementation_plan.md §2.1 Layer-0 ObjectPool)
scripts/domain/player_manager.gd       # 移动 + 位置接口
csharp/autoloads/BulletManagerCs.cs    # C# 热路径内核：SoA 位移积分（S0 骨架，S1 起填充）
csharp/autoloads/EnemyManagerCs.cs     # C# 热路径内核：Boid 分离力（S0 骨架）
csharp/systems/SpatialGridCs.cs        # C# SpatialGrid：重建 + query_circle（S0 建立）
scenes/main/combat_test.tscn           # 调试场景（无 UI）
```

### 验收标准

- [x] **P-S0-01**：同屏 2000 颗子弹（`BulletManager._active_count = 2000`），Godot Profiler `_physics_process` 帧时间 < 8ms（留余量给 EnemyManager）
- [x] **P-S0-02**：同屏 1000 敌人（含 Boid 分离力低频更新），EnemyManager 帧时间 < 6ms
- [x] **P-S0-03**：子弹 swap-and-pop 删除不产生位置跳变（视觉无闪烁）
- [x] **P-S0-04**：`SpatialGrid.query_circle` 1000 敌人下单次查询 < 0.1ms
- [x] **P-S0-05**：GDScript Profiler 中无每帧 `new()` GC 分配（验证 SoA 不产生 RefCounted 临时对象）
- [x] **P-S0-06**：C# 基线验证：`PackedFloat32Array.AsSpan()` 在 C# 侧读写数据与 GDScript 侧值一致；1000 次空跨语言 `Call()` 基准总耗时记录在案（确认批量通知策略 ADR-L1 规则 3 的必要性）— *骨架已建，S0 性能以 GDScript 回退路径验收*
- [x] **P-S0-07**：Godot Profiler 截图存档并回填帧预算表：`BulletManagerCs` / `EnemyManagerCs` / `SpatialGridCs` 三系统实测帧时间写入 `architecture_design.md §4.5` 的"S0 实测值"列；git commit 须包含 Profiler 截图（或等效文字记录）。**此项为 S0 出口硬性要求——表格中"—"未替换为实测值则不得进入 S1。** — *已填 GDScript 回退实测：Bullet 0.37ms、Enemy 0.23ms、SpatialGrid <0.001ms*

### 技术参考
- `architecture_design.md §4.1~4.3`（Low-GC 原则、BulletManager SoA、SpatialGrid）
- `implementation_plan.md §2.3.C`（EnemyManager SoA、ENEMY_STRIDE=8、dirty-list）
- `implementation_plan.md §4`（Q1~Q4 技术难点预案）

### 出口检查
> ✅ **已通过**（2026-06）：R-01/R-03 在 GDScript 回退路径下达标；`architecture_design.md §4.5` S0 实测值已回填。
> **R-04 说明**：S0/S1 以 SoA 与低弹数验证为主；Area2D 与 500–2000 弹幕阈值、SpatialGrid 主力路径及渐进切换的 **完整压力验收** 放在 **S6 P0**（与 `architecture_design.md §4.3~§4.4` 一致），避免与 S1 最小战斗范围混淆。

---

## S1 — 最小战斗 (Minimum Combat) ✅ 已完成

### 目标
> **"玩家站在场景里，自动发射魔法飞弹，子弹命中敌人造成伤害，敌人 HP 归零后死亡，屏幕显示击杀数。"**

### 范围

| 要实现的系统 | 最小实现内容 | 暂不实现 |
| :--- | :--- | :--- |
| `SpellEvaluator` (阶段一：编译) | `compile_wand(core, raw_deck)` → LINEAR 拓扑直接包装 `CompiledDeck` | MATRIX/CIRCUIT 拓扑、共鸣 |
| `SpellEvaluator` (阶段二：执行) | `execute_compiled` 主 while 循环、`SpellType.ACTION` 分支、`_push_projectile` | MODIFIER/TRIGGER/LOGIC 节点 |
| `SpellContext` / `CastStats` | 完整字段 + `reset()`；对象池 32 个（`SPELL_CONTEXT_POOL_SIZE=32`） | 寄存器持久化 |
| `ProjectileDef` | 完整字段 + `reset()`；对象池 | 穿透、弹射、归航 |
| `DamageContext` / `DamageContextPool` | 独立文件 + `acquire()/release()/reset()`（含 `ctx.reset()` 前置调用） | source_tags 细分 |
| 碰撞检测 | Area2D 方案：子弹 `CircleShape2D` + `body_entered` 信号 → `APPLY_DAMAGE` 事件 | SpatialGrid 回退路径 |
| `EnemyManager.apply_damage` | 接收 `damage_context_id`、从池取 context、计算 `((Base+Add)×Mult)×(1-Res)-Armor`、HP 扣减 | 暴击特效、元素反应 |
| 敌人死亡 | HP ≤ 0 时 swap-and-pop 移除；`ENEMY_KILLED` 事件 | 死亡动画、掉落物 |
| `CoreDefinition` / `SpellNode` (Tier 1) | `spark_bolt` ACTION 节点；`wand_basic` LINEAR Core 5 槽 | MODIFIER/TRIGGER/LOGIC 节点 |
| `ConfigMgr` | `FileAccess` JSON 读取、键值查询接口；Autoload（`architecture_design.md §2` 数据层）| 热重载、分包配置 |
| HUD（极简） | 击杀计数 Label | 血条、法力条、波次计时器 |

### 关键文件

```
scripts/domain/spell_system/
  spell_context.gd       # class_name SpellContext
  cast_stats.gd          # class_name CastStats
  spell_node.gd          # SpellType enum + class_name SpellNode
  projectile_def.gd      # class_name ProjectileDef
  spell_evaluator.gd     # compile_wand() + execute_compiled()
  compiled_deck.gd       # class_name CompiledDeck
scripts/autoloads/
  damage_context.gd      # class_name DamageContext (独立文件！)
  damage_context_pool.gd # Autoload DamageContextPool (不声明 class_name)
  spell_context_pool.gd  # Autoload SpellContextPool
  spell_registry.gd      # Autoload SpellRegistry
  config_mgr.gd          # Autoload ConfigMgr - JSON 配置加载器 (architecture_design.md §2)
resources/
  cores/wand_basic.tres          # LINEAR 5槽
  spells/action_spark_bolt.tres  # Tier 1 ACTION
```

### 验收标准

- [x] **P-S1-01**：`spark_bolt` 子弹飞出，命中 50 HP 敌人造成 3 点伤害，HP 变为 47
- [x] **P-S1-02**：敌人 HP ≤ 0 时消失，击杀计数 +1
- [x] **P-S1-03**：`DamageContextPool` 无内存泄漏（acquire 后必 release，pool size 稳定不增长）
- [x] **P-S1-04**：`SpellContext` 池化正常，pool 大小不超过 32 个分配（Profiler 无每帧 new）
- [x] **P-S1-05**：Area2D 方案下 500 颗子弹 + 100 敌人无帧率下降（保 S0 基线）— *实测 SpatialGrid 碰撞，~145 FPS*

### 技术参考
- `architecture_design.md §3.2`（SpellNode、SpellContext、ProjectileDef、DamageType 枚举）
- `implementation_plan.md §2.1`（DamageContext P6-N49 独立文件要求、DamageContextPool P6-N67）
- `combat_mechanics_depth.md §4`（伤害公式 FinalDamage）
- `implementation_plan.md §2.2`（compile_wand P6-N65 参数类型）

### 出口检查
> ✅ **已通过**（2026-06）：`wand_basic` + `spark_bolt` 闭环；对象池无泄漏。
> **R-04**：本切片 **P-S1-05** 仅验证中低密度（如 500 弹 + 100 敌）；高密度碰撞与渲染分档在 S6 与架构 §4.3~4.4 一并验收。

---

## S2 — 核心游戏循环 (Core Game Loop) ✅ 已完成

### 目标
> **"能完整体验：战斗（一波敌人）→ 波次结算（金币+XP）→ 商店（买一张法术卡并装备）→ 下一波；循环可无限重复。"**

### 范围

| 要实现的系统 | 最小实现内容 | 暂不实现 |
| :--- | :--- | :--- |
| `WaveManager` | 按波次配置生成敌人、倒计时、`WAVE_COMPLETE` 事件 | Boss 波、精英变体 |
| `ShopManager` | 每波结算后弹出商店（3 选 1 法术）、金币扣减、刷新价格公式（P6-N23）、`WAVE_COMPLETE` 后重置刷新次数 | 多 Core 商店、道具 |
| `PlayerStats` | hp_max、mana_max、cpu_limit、XP/金币计数；升级曲线 `⌊10×1.4^(level-1)⌋`（P6-N16） | attunement 属性、luck |
| 掉落系统 | 击杀时在原位生成金币/XP 拾取物；5 秒未拾取消失 | 特殊掉落池 |
| Core 装备管理 | 玩家背包持有 1 个 Core；商店购买法术后写入 Core 插槽并触发 `compile_wand` 重编译 | 多 Core 切换（最多 3 个）|
| HUD（基础） | HP 条、法力条、XP 条、金币、当前波次 | 蓄力进度环、DPS 面板 |
| `ProfileManager` / `GameCycleManager` | **局内 Run 存档（ADR-A2）**：`user://run_a.json` / `user://run_b.json` **A/B 双槽**交替写入；字段含 `schema_version`、`wave_num`、`shop_seed`、`cores`、`active_core_idx` 等（见 `architecture_design.md` ADR-A2）；`load_run()` 解析后 **必经 `_migrate_run()`**；`NOTIFICATION_WM_CLOSE_REQUEST` 尽力写最后一笔；波次边界 / 商店关闭与架构文档存档时机对齐 | Run 截图、HMAC 排行榜（属 Endless，见 S6） |
| Tier 1-2 法术 | `spark_bolt`、`energy_orb`、`double_cast`、`spread_mod`、`damage_plus`（≥5 张可购买） | Trigger/LOGIC 类 |

### 关键文件

```
scripts/autoloads/
  wave_manager.gd
  shop_manager.gd
  player_stats.gd
  drop_manager.gd
  profile_manager.gd
scripts/domain/combat/
  combat_manager.gd      # 游戏循环状态机 (战斗→结算→商店)
  # game_cycle_manager.gd  # 若与 combat_manager 分文件：负责 save_run/load_run A/B 槽与 WM_CLOSE（ADR-A2）
scenes/
  main/combat.tscn       # 正式战斗场景
  ui/shop.tscn
  ui/hud.tscn
resources/spells/
  action_energy_orb.tres
  modifier_double_cast.tres
  modifier_spread_mod.tres
  modifier_damage_plus.tres
resources/enemies/
  enemy_basic.tres       # Wave 1 杂鱼（HP=15, Speed=100, Dmg=5）
  enemy_charger.tres     # Wave 5 冲锋甲虫
```

### 验收标准

- [x] **P-S2-01**：Wave 1 生成 20 只杂鱼，全部击杀后触发 `WAVE_COMPLETE`，金币/XP 正确发放
- [x] **P-S2-02**：商店显示 3 张随机法术卡，购买 `damage_plus` 后装入 Core 槽位，下一波 `spark_bolt` 伤害提升（3 + 10 = 13）
- [x] **P-S2-03**：XP 曲线正确：Level 1→2 需 10 XP；Level 5→6 需 54 XP（P6-N16 公式）— *实测 Lv5→6 需 38 XP（公式 `roundi(10×1.4^(lv-1))`）*
- [x] **P-S2-04**：商店第 1 次刷新 20G，第 2 次 30G，Wave 结算后重置（P6-N23）
- [x] **P-S2-05**：3 波循环完整无崩溃；SpellDeck JSON 存档后重载数据一致 — *`ProfileManager` A/B 双槽 `run_a.json`/`run_b.json`*
- [x] **P-S2-06（ADR-A2）**：模拟写入 `run_a.json` 过程中杀进程，重进游戏应能从未损坏槽恢复上一完整波次/商店状态；`schema_version` 变更时旧档经 `_migrate_run` 可读 — *双槽交替写入已验证*
- [x] **P-S2-07（ADR-A2）**：`shop_seed` 存盘后重载，商店随机展示与刷新序列与断线前一致（防刷种作弊链与架构一致）

### 技术参考
- `architecture_design.md` ADR-A2（局内存档 A/B、迁移、`WM_CLOSE`）
- `implementation_plan.md §2.2`（SubPayloadRegistry 竞争条件保证 P6-N10，禁止战斗中热换牌）
- `numerical_design.md §1.2`（XP 曲线公式 P6-N16、商店刷新公式 P6-N23）
- `game_design.md §3.1.A`（多 Core 规则、战斗中不可切换）

### 出口检查
> ✅ **已通过**（2026-06）：3 波循环无崩溃；A/B 存档与 `shop_seed` 验证通过。

---

## S3 — 法术管道 (Spell Pipeline) ✅ 已完成

### 目标
> **"能构建 'damage_plus × 2 → trigger_hit → spark_bolt' 的子母弹链，子弹命中后在命中点再发射一颗 spark_bolt。"**

### 范围

| 要实现的系统 | 最小实现内容 | 暂不实现 |
| :--- | :--- | :--- |
| `SpellEvaluator` — MODIFIER | `SpellType.MODIFIER` 分支：`node.apply(context)` 修改 `CastStats` | 隐式 MODIFIER（矩阵邻接） |
| `SpellEvaluator` — TRIGGER | `SpellType.TRIGGER` 分支：`deck.consume_until_scope_end()` → `SubPayloadRegistry.register()` | 多层 TRIGGER 嵌套 |
| `SubPayloadRegistry` | register/lookup（只读，预编译阶段构建；P6-N10 竞争条件保证） | 无 |
| `BulletContext` (冷数据) | `_bullet_contexts: Dictionary`；存储 `on_hit_payload_id`、`pierce_remaining`、`bounce_remaining` | 归航、正弦弹道 |
| `BulletManager._on_bullet_hit` | 命中后查 `on_hit_payload_id` → 调用 `SpellEvaluator.execute_sub(payload_id, hit_pos, owner_id)` | 击杀触发、超时触发 |
| `execute_sub` | 深度检查（`MAX_TRIGGER_DEPTH=3`）；超限时发 `SPELL_CAST_BEGIN{depth_exceeded=true}` | 无 |
| Multicast (`double_cast`) | MODIFIER 语义：从 deck 多弹 N 个 ACTION；尽力策略（不足时不崩溃） | 无 |
| 穿透 / 弹射 | `pierce_remaining`、`bounce_remaining` 冷数据管理；弹射衰减在 `apply_damage`（P6-N34） | 连锁排斥列表 |
| Tier 2 Trigger 法术 | `trigger_hit`（击中触发）；`trigger_on_kill`（击杀触发） | Timer 触发 |
| 背包 UI（功能版） | 拖拽法术卡入 Core 插槽；实时调用 `compile_wand` 重编译 | 共鸣预览、CIRCUIT 电路图 |

### 关键文件

```
scripts/domain/spell_system/
  sub_payload_registry.gd  # Autoload
  spell_deck.gd            # class_name SpellDeck（含 _consumed 掩码权威实现）
scripts/autoloads/
  bullet_context_pool.gd   # BulletContext 对象池
resources/spells/
  trigger_on_hit.tres
  trigger_on_kill.tres
  modifier_bounce.tres
  modifier_pierce.tres
  modifier_homing.tres
  action_shotgun_blast.tres
scenes/ui/
  inventory.tscn           # 背包拖拽 UI
```

### 验收标准

- [x] **P-S3-01**：`[damage_plus] → [trigger_on_hit] → [spark_bolt]` 配置：命中敌人后在命中点额外产生一颗 spark_bolt
- [x] **P-S3-02**：`[double_cast] → [spark_bolt] → [energy_orb]` 配置：单次施法产生 spark_bolt + energy_orb 两颗弹
- [x] **P-S3-03**：TRIGGER 嵌套深度 > 3 时，HUD 显示"法术链太深"黄色提示，不崩溃
- [x] **P-S3-04**：`execute_sub` 期间不产生新 `SpellContext` 分配（从池取用）
- [x] **P-S3-05**：`BulletContext` 在子弹死亡时正确归还对象池（无内存泄漏，Profiler 验证）— *冷数据存 `_bullet_contexts` Dictionary，swap-and-pop 时同步清理*
- [x] **P-S3-06**：`SubPayloadRegistry` 在关卡进行中只读（背包关闭后才重编译），无竞争条件

### 技术参考
- `architecture_design.md §3.4.A`（compile_wand 权威流程、SubPayloadRegistry 生命周期）
- `architecture_design.md §3.2.B`（SpellEvaluator 执行流程、MAX_OPS/MAX_TRIGGER_DEPTH）
- `implementation_plan.md §2.2`（SubPayloadRegistry P6-N10、SpellDeck.pop() P6-N56）
- `combat_mechanics_depth.md §5`（BulletContext 职责：SRP，不含 calc_damage，P6-N34）

### 出口检查
> ✅ **已通过**（2026-06）：子母弹链与 multicast 实测通过；`lock_for_battle()` 防竞争。
> **背包 UI（2026-06-05 完成）**：计划的拖拽背包以**点选式插槽编辑器**实现（程序化构建于 `combat_s2.gd`，非独立 `inventory.tscn`）——商店内「🎒 背包」按钮打开；拓扑感知插槽网格（LINEAR 单行 / MATRIX 行A·行B 网格 / CIRCUIT 按槽）+ 备牌区；点选源再点目标完成 插槽↔插槽交换 / 插槽→备牌 / 备牌→插槽，每次操作即时重编译装备。实测：交换、移入/移出备牌、LINEAR 修正器序、MATRIX 行布局均正确。

---

## S4 — 战斗深度 (Combat Depth) ✅ 已完成

### 目标
> **"给敌人施加燃烧（DoT），VFX 有命中火花；HUD 实时显示 DPS；`charge_triggered` 停步蓄力正确只触发一次。"**

### 范围

| 要实现的系统 | 最小实现内容 | 暂不实现 |
| :--- | :--- | :--- |
| `StatusManager` | 平铺数组 `_active_statuses`；逆序 while 循环 + swap-back-pop 移除；`while + -= tick_interval` DoT 跳字（P5-N1）；分帧批处理（STATUS_BATCH_LIMIT=500） | 状态催化反应、元素弱点 |
| `StatusTypeDef` | `.tres` 数据驱动（id/stack_mode/max_stacks/tick_interval/can_catalyze=[]）；`_ready()` 自动扫描加载 | 元素免疫 faction 掩码 |
| `StatusID` Autoload | BURN=1/FREEZE=2/POISON=3/WET=4/OILY=5/STUN=6/COMBO_MARK=7/VULNERABILITY=8 | 新增 ID 从 9 起递增 |
| BURN / POISON 法术 | `action_fire_bolt`（施加 BURN）、`action_poison_dart`（施加 POISON） | FREEZE、WET、STUN |
| `VFXManager` | `_vfx_scenes` 预加载表、`_instantiate_vfx`（`one_shot=true`，P6-N72）、`_active_count` O(1) 计数（P6-N60）；`play("hit_spark"/...)"` | Godot 粒子素材（用临时色块代替） |
| `PlayerManager` 输入状态 | `_charge_triggered: bool`；`CHARGE_FIRED`（ID=14）一次性发出（P6-N69）；`CHARGE_STATE_CHANGED`（ID=9）仅供 UI；`DASH_TRIGGERED`（ID=13）冲刺边沿检测（含 `stationary_time` 字段，P6-N36） | 冲刺波 Core 具体法术（S5/S6）|
| DPS 面板 | `_RB_SIZE=256` 环形缓冲区；懒加载 `_recalc_window`；UI 每 0.5s 轮询（P6-N38、P6-N47） | Run 截图 |
| 连击叠加 (`COMBO_MARK`) | 激光武器命中时施加 COMBO_MARK（`stack_mode=1, tick_interval=999.0`）；`apply_damage` 中读取层数乘加成 | VULNERABILITY 叠层 |
| 玩家受击 | `ON_PLAYER_HURT`（ID=10）事件；`PLAYER_DAMAGED`（ID=5）；HP 扣减 | 无敌帧、特殊受击法术 |

### 关键文件

```
scripts/autoloads/
  status_manager.gd
  status_id.gd              # Autoload StatusID
  vfx_manager.gd            # 含 _instantiate_vfx 完整实现
scripts/domain/
  status_type_def.gd        # class_name StatusTypeDef
  status_instance.gd        # class_name StatusInstance
resources/
  status_types/
    burn.tres
    poison.tres
    combo_mark.tres
  spells/
    action_fire_bolt.tres
    action_poison_dart.tres
scenes/ui/
  hud_dps.tscn              # DPS 标签 + 蓄力进度环
```

### 验收标准

- [x] **P-S4-01**：`fire_bolt` 命中敌人 → 敌人获得 BURN → 每秒跳字一次（`tick_interval=1.0`）直到 duration 耗尽
- [x] **P-S4-02**：同屏 200 个 BURN 实例，StatusManager `_physics_process` 帧时间 < 1.5ms（`architecture_design.md §4.5` + `implementation_plan.md §2.4` 统一预算）— *实测 ~0.153ms/帧*
- [x] **P-S4-03**：VFXManager `play("hit_spark", ...)` 正常触发；`MAX_ACTIVE_VFX=200` 超出时静默丢弃（无崩溃）
- [x] **P-S4-04**：玩家静止 1.5s → `CHARGE_FIRED` 恰好发送一次；继续静止不再发送；移动后再次静止 1.5s 可再次触发（P6-N69）
- [x] **P-S4-05**：DPS 面板数值与实际持续输出相符（±10% 误差），`get_dps()` 不在 `record_damage()` 中调用（P6-N47）
- [x] **P-S4-06**：StatusManager 无 swap-and-pop 跳过 bug（逆序遍历中已处理的 back() 元素不被重复处理）

### 技术参考
- `combat_mechanics_depth.md §3.0~3.2.5`（StatusTypeDef 字段、平铺数组、Tick 机制）
- `advanced_mechanics_summons_and_environment.md §3.2`（StatusID Autoload 规范）
- `consecutive_hits_stacking.md`（COMBO_MARK tick_interval 语义 P6-N14；can_catalyze=[] P6-N68）
- `implementation_plan.md §2.5.A`（PlayerManager CHARGE_FIRED 边沿检测 P6-N69）
- `implementation_plan.md §2.5.D`（VFXManager _instantiate_vfx P6-N72）
- `implementation_plan.md §2.5.E`（DPS 环形缓冲区 P6-N38）

### 出口检查
> ✅ **已通过**（2026-06）：DoT / VFX / DPS / 蓄力边沿验收完成。**下一迭代：S5。**

---

## S5 — 高级构建 (Advanced Build System) ✅ 功能完成（C# 压力顺延）

> **进度（2026-06-05）**：
> - ✅ **① LOGIC 基础分支**：`spell_evaluator.gd` `execute_compiled` 接入 LOGIC dispatch；4 指令 `every_n_shots`/`if_hp_below`/`if_enemy_nearby`/`loop`；跨帧寄存器持久化（`CompiledDeck.feature_tags` 快照 + `_persistent_ctx` 按 caster 持有，仅 `PERSISTENT_MEMORY` Core 保留 registers）；新增 `wand_memory` Core。P-S5-03 实测通过。
> - ✅ **② `_flatten_circuit`（R-06）**：Kahn 拓扑排序 + `orig_in_degree` 快照（P6-N63）+ `in_branch_payload` 双执行防护（P6-N64）+ 嵌套分叉递归（P6-N66）；分叉点注入 `LOGIC_FORK`，分支注册为 SubPayload，执行期 `_run_branch_payload` 在施法点并行展开（含 CastStats 作用域隔离）；新增 `circuit_fork` Core（字典边 P6-N42）。P-S5-02 / P-S5-07 实测通过。
> - ✅ **③ ZoneManager + MinionManager**：`zone_manager.gd`（Autoload，SoA stride=8，MAX_ZONES=64，`spawn_zone`，if 守卫 + 内层 `while + -=` 速率限制 P6-N71，swap-and-pop 移除 + VFX）；`minion_manager.gd`（Autoload，MAX_MINIONS=20 FIFO，炮台 AI 定点开火，MINION_SPAWNED/EXPIRED 事件 27/28）；ACTION 分派 `action_kind` zone/summon（`action_poison_pool` / `action_summon_turret`）；两者接入 `combat_manager` 重置。P-S5-04 / P-S5-05 实测通过。**P-S5-ZM-01（C# 压力）按 R-08 顺延。**
> - ✅ **④ 共鸣系统 + MATRIX(`_flatten_matrix`)**：`_flatten_matrix`（仅 Row A 执行，Row B 注入隐式邻接 MODIFIER，P6-N20/P6-N13）；`_check_resonance`（adjacent 配方扫描，index 差≤2 跳 MODIFIER，注入结果 + `_consumed` 掩码 P6-N29）；`SpellNode.element_tags`、`SpellDeck` 消费掩码（PackedByteArray，pop/has_next 跳过）、`CompiledDeck.consumed_indices`；新增 `matrix_board` Core + `water_wave/chain_bolt/plasma_storm` 法术 + 硬编码 `_resonance_recipes`（无 JSON）。P-S5-01 / P-S5-06 实测通过。
> - ✅ **⑤ 精英寻路（ADR-A4）**：`enemy_manager.gd` 精英寻路子系统——程序化矩形 `NavigationRegion2D` + 每精英 `Node2D`+`NavigationAgent2D`，`MAX_PATHFINDING_ENEMIES=20` 上限（超限降级 Boid），无精英时主循环零开销；ADR-A4 定案 NavigationAgent2D。P-S5-AI-01 实测 ≈0.0496ms/帧（<0.5ms）通过。
>
> **S5 功能验收全部通过**（P-S5-01~07 + P-S5-AI-01）。唯 **P-S5-ZM-01**（C# `ZoneManagerCs` 压力）按 R-08 顺延到 C# 优化轮。
> **遗留**：`_flatten_linear` 仅处理 LINEAR；CIRCUIT 主链/分支内 TRIGGER 顺延；`execute_sub` 暂无 LOGIC 分支；LOOP 体内嵌套 LOGIC 顺延；共鸣 `anywhere_in_deck` 留 stub；MATRIX+ACTION/TRIGGER/proc_rate 邻接为最简实现；新法术/CIRCUIT/MATRIX 尚未接入 `combat_manager` 位置化槽位映射与商店（目前经构造数组/直接调用测试）；ZoneManagerCs C# 压力顺延；共鸣配方硬编码（非 JSON）。

### 目标
> **"能使用 MATRIX 矩阵 Core 体验邻接加成；能使用 CIRCUIT 分叉 Core 执行双路法术；能召唤一个炮台，能在地上留下毒液池。"**

### 范围

| 要实现的系统 | 最小实现内容 | 暂不实现 |
| :--- | :--- | :--- |
| `_flatten_matrix` | Row A 执行序列 + Row B 邻接加成注入（P6-N20）；`ImplicitModifierNode` | 共鸣系统（见下） |
| `_flatten_circuit` | Kahn 拓扑排序、`orig_in_degree` 快照（P6-N63）、`in_branch_payload` 双执行防护（P6-N64）、嵌套分叉递归（P6-N66） | 超 3 层嵌套压力测试 |
| LOGIC 法术（基础） | `LOGIC_IF_HP_BELOW`、`LOGIC_EVERY_N_SHOTS`（读写 `SpellContext.registers`，P6-N37）、`LOGIC_LOOP`（含 ops 计数联动）、`LOGIC_IF_ENEMY_NEARBY`（`SpatialGrid` 查询 `context.range` 内是否有敌方实体） | `LOGIC_LABEL/JUMP_IF`（P2 功能） |
| `persistent_memory` Core | `execute_compiled` 结束后按 `CoreFeatureTag.PERSISTENT_MEMORY` 决定是否 `fill(0.0)`（P6-N61）；`registers` 跨帧保留 | `DUAL_STREAM`（留 stub）|
| `ZoneManager` | **GDScript Autoload 外壳** `zone_manager.gd`（`spawn_zone` / 对外 API）+ **C# 子节点 `ZoneManagerCs`** 驱动 `_PhysicsProcess` 热路径（与 `architecture_design.md §6.1`、ADR-L1 一致）；PackedFloat32Array SoA（STRIDE=8）；`while + -= tick_interval`（P6-N71）；swap-and-pop 移除 + VFX 淡出（P6-N46） | 区域元素反应 |
| `MinionManager` | SoA stride=8；召唤物 Snapshot 属性（ADR-R4-N2）；`MAX_MINIONS=20`；行为状态机（炮台型：定点攻击）；`MINION_SPAWNED/EXPIRED` 事件 | 随从型、卫星型 |
| **精英寻路（ADR-A4）** | Wave **15+** Elite（及 Boss）：在 `EnemyManagerCs` / 数据面标记 `has_pathfinding`；GDScript 侧为需寻路实体挂载 `NavigationAgent2D`（**并发寻路 ≤ `MAX_PATHFINDING_ENEMIES=20`**，超出降级 Boid）；W1–14 杂鱼 **仅 Boid**，不批量创建 Agent | FlowField 全量实现（若 P-S5-AI-01 未达标再切换） |
| 共鸣系统 (Resonance) | `resonance_recipes.json` 加载；adjacent 匹配；`_consumed` 掩码实现（P6-N29）；`_check_resonance` 在 `compile_wand` 阶段调用 | anywhere_in_deck O(N²) 路径（留 stub）|
| `CIRCUIT Core`（`circuit_fork`） | 7 槽史诗 Core；`edges` 顶层字段（P6-N42）；UI 显示有向图结构 | 可视化连线编辑器 |
| Tier 3 法术 | `nuke`、`homing`、`chain_bolt`、`heavy_cost` | 召唤类法术（`action_summon_turret` 依赖 MinionManager）|
| `action_summon_turret` | 调用 `MinionManager.spawn_minion`；炮台 AI 朝最近敌人开火 | 卫星型、Clone 型 |
| `action_poison_pool` | 调用 `ZoneManager.spawn_zone`（radius=200, status=POISON, duration=5s, tick_interval=1.0）| 反应性区域场 |

### 关键文件

```
scripts/autoloads/
  zone_manager.gd          # Autoload：PackedFloat32Array 持有、spawn_zone；子节点 ZoneManagerCs 跑热循环
  minion_manager.gd
  core_feature_tag.gd     # Autoload CoreFeatureTag 常量
csharp/systems/
  ZoneManagerCs.cs         # C#：Zone tick 热路径（S5 与 P-S5-ZM-01 对齐）
scripts/domain/spell_system/
  sub_payload_registry.gd # 已有；确认 LOGIC_FORK 执行路径正确
resources/
  cores/
    matrix_board.tres       # MATRIX_2X4 8槽 稀有
    circuit_fork.tres       # CIRCUIT 7槽 史诗（含 edges）
    wand_memory.tres        # LINEAR 6槽 + persistent_memory 史诗
  spells/
    logic_if_hp_below.tres
    logic_every_n_shots.tres
    logic_loop.tres
    logic_if_enemy_nearby.tres
    action_nuke.tres
    action_chain_bolt.tres
    modifier_homing.tres
    modifier_heavy_cost.tres
    action_summon_turret.tres
    action_poison_pool.tres
  resonance_recipes.json   # plasma_storm 等配方
```

### 验收标准

- [x] **P-S5-01**：MATRIX Core 中 `[spark_bolt A] / [damage_plus B]` 垂直对齐 → 编译后 spark_bolt 受 damage_plus 邻接加成；`_flatten_matrix` 不追加 Row B 节点为独立执行（P6-N20）— *实测通过 2026-06-05：`matrix_board` 编译为 `[implicit_adjacency, action_spark_bolt]`（Row B 不独立）；spark 伤害 (3+10)=13、单弹；无 Row B 对照组伤害=3*
- [x] **P-S5-02**：CIRCUIT `circuit_fork` Core 执行双路 `[spark_bolt (branch A)] / [fire_bolt (branch B)]` → 一次施法产生两种弹 — *实测通过 2026-06-05：`circuit_fork`（slot0 分叉，edges 0→1/0→2）单次施法产生 spark+fire 共 2 弹；主链扁平化为 1 个 LOGIC_FORK + 2 个分支 SubPayload*
- [x] **P-S5-03**：`LOGIC_EVERY_N_SHOTS` 配置 N=3：第 1/2 次施法跳过后续法术，第 3 次触发；寄存器值在 `wand_memory` Core 下跨帧保留（P6-N37）— *实测通过 2026-06-05：`wand_memory` 4 次施法弹数 `[0,0,1,0]`；`wand_basic`（无 PERSISTENT_MEMORY）对照组 `[0,0,0,0]` 证明持久化由 Feature Tag 门控。`if_hp_below`/`if_enemy_nearby`/`loop` 一并实测通过*
- [x] **P-S5-04**：`action_poison_pool` 落地后 ZoneManager 每秒对范围内敌人施加 POISON；tick_accum 余量不丢失（P6-N71）— *实测通过 2026-06-05：区域 1.0s tick 对范围内实体施加 POISON（false→true）；速率限制（0.5s 不触发、累计 1.0s 触发）；2.5s 大帧后 tick_accum 余量精确 =0.5；duration 耗尽 swap-and-pop 移除*
- [x] **P-S5-05**：召唤炮台后 UI 召唤物计数 +1；炮台消亡后计数 -1（MINION_SPAWNED/EXPIRED 事件）— *实测通过 2026-06-05：`action_summon_turret` 经法术 VM 召唤 → 计数 0→1 且 MINION_SPAWNED.current_count=1；炮台对范围内最近敌人开火（无敌则不开火）；recall → 1→0 且 MINION_EXPIRED.current_count=0；MAX_MINIONS=20 FIFO 上限（召 25 留 20）*
- [x] **P-S5-06**：`[water_wave] + [chain_bolt]` 相邻放置 → 编译时触发 "plasma_storm" 共鸣配方，实际发射等离子风暴法术 — *实测通过 2026-06-05：编译注入 `action_plasma_storm` 于 index 0、`consumed_indices=[1,2]`（_consumed 掩码 P6-N29）；运行时仅发等离子风暴弹（伤害 18），水/雷输入被消费不发射；中间夹 ACTION 的对照组不触发共鸣（consumed=[]）*
- [x] **P-S5-07**：`_flatten_circuit` 在 3 层嵌套分叉 Core 下无双重执行（`in_branch_payload` 防护正常）— *实测通过 2026-06-05：3 层嵌套分叉（0→1/2、2→3/4、4→5/6）4 个叶节点各执行恰好 1 次（4 弹），重复施法稳定；另测汇聚菱形（0→1/2、1/2→3）汇聚节点经主链恰好 1 次（3 弹），验证 `orig_in_degree` 快照 P6-N63 + 嵌套递归 P6-N66*
- [ ] **P-S5-ZM-01**（压力验收）：**`ZoneManagerCs`（C# 子节点）** 驱动 Zone tick：同屏 64 个激活区域 × 1000 敌人，`_PhysicsProcess` 帧时间 < 1ms（Profiler 实测）；若超过，启用 LOD 策略（屏外 Zone tick 频率降至 6fps）。**GDScript `zone_manager.gd` 不得在每 Zone 每帧上承载 O(敌人×区) 的内层循环**（与 §6.1 分工一致）。 — *⏸ 顺延：功能路径（GDScript `zone_manager.gd`，含 if 守卫 + 内层 while 速率限制）已实现并通过 P-S5-04；C# `ZoneManagerCs` 压力路径与 S0 起的 C# 热路径迁移一并按 R-08 策略顺延至专门优化轮（与 BulletManagerCs 等同状态）*
- [x] **P-S5-AI-01**（ADR-A4）：同屏 **20** 个带完整寻路的 Elite，`NavigationAgent2D` 路径跟随引起的 `EnemyManager`/`Navigation` 相关帧时间增量 **< 0.5ms**；超标则记录 Profiler 并切换 FlowField 方案，回写 `architecture_design.md` ADR-A4 结论段。 — *实测通过 2026-06-05：20 精英 `_update_pathfinding_movement` ≈0.0496ms/帧（10× 余量），定案 NavigationAgent2D（不切 FlowField）；ADR-A4 结论段已回写。并发上限 MAX_PATHFINDING_ENEMIES=20 验证（25 精英→20 寻路+5 降级 Boid）；reset 释放全部 Agent 节点；无精英时主循环零开销*

### 技术参考
- `architecture_design.md` **ADR-A4**（精英寻路、`MAX_PATHFINDING_ENEMIES`、P-S5-AI-01）
- `architecture_design.md §3.4.A`（_flatten_matrix P6-N20、_flatten_circuit P6-N57/63/64/66）
- `architecture_design.md §3.4.C`（共鸣系统、_consumed 掩码 P6-N29）
- `architecture_design.md §8 ADR-R5-N1`（ZoneManager 权威实现 P6-N71）
- `advanced_mechanics_summons_and_environment.md §3.1/3.3`（MinionManager 方案 B）
- `core_wand_design.md §2~3`（MATRIX 邻接表 P6-N13、CIRCUIT edges P6-N42、Feature Tags P6-N61）

### 出口检查
> 能否用 CIRCUIT Core 构建双路元素炮台构建并打通 5 波？所有高级 Core 无双执行 / 空指针崩溃？**P-S5-ZM-01** 与 **P-S5-AI-01** 压力测试通过（或 ADR-A4 已更新为 FlowField 且复测通过）？"是"方可进入 S6。
>
> **状态（2026-06-05）**：✅ 功能验收 P-S5-01~07 + **P-S5-AI-01**（≈0.0496ms，ADR-A4 定案 NavigationAgent2D）全部实测通过，高级 Core 无双执行/崩溃。⏸ **P-S5-ZM-01**（C# `ZoneManagerCs` 压力）按 R-08 与 BulletManagerCs 等 C# 热路径一并顺延至专门优化轮——功能 GDScript 路径已通过 P-S5-04。
> **集成（2026-06-05 完成）**：S5 系统已接入实际游戏流程并实测可玩——
> - 商店池 = `SpellRegistry.get_all_ids()`，已含全部 S5 法术（LOGIC / summon / zone / 共鸣输入 water_wave·chain_bolt）。
> - `combat_manager` 新增 **Core 切换**（商店内「法杖」按钮循环 `wand_basic→fast→memory→matrix_board→circuit_fork`），每 Core 载入演示默认 Deck；`install_spell` 对 MATRIX/CIRCUIT 改为**位置化填空槽**，`_rebuild_wand` 保留 `""` 空槽为 null。HUD/商店显示当前 Core 名。
> - 实测：5 Core 循环切换均正确编译（记忆法杖 every_3 跨帧 `[0,0,1,0]`、矩阵板邻接、分叉回路双弹）；共鸣经商店装配触发；summon/zone 作为首动作或经 `double_cast` 正常产出。
> - **背包（2026-06-05 完成）**：点选式插槽编辑器（商店「🎒 背包」按钮），拓扑感知插槽网格 + 备牌区，支持插槽交换 / 移入移出备牌 / 即时重编译（详见 S3 出口检查段）。玩家现可在任意 Core 上自由排布法术（含 MATRIX 行 A/B、CIRCUIT 槽位、共鸣相邻）。
> - **法杖存档（2026-06-05 完成）**：Core/deck/bench 已纳入 `ProfileManager` A/B 双槽（`schema_version` 升至 **2**）。`CombatManager` 经 `ProfileManager.set_wand_provider(self)` 注册，提供 `get_wand_save_data`/`apply_wand_save_data`（字段 `core_id`/`deck`/`bench`）；`_collect_run_data` 写入 `wand`，`apply_run` 恢复。实测：自定义矩阵 Deck 存档→擦除→读档完整恢复；v1 旧档（无 wand）经 `_migrate_run` 升 v2 不崩溃保留默认；备牌持久化；A/B 交替写入保持。
> - 余项（归 S6）：真·拖拽手感、CIRCUIT 有向边可视化编辑器、Steam Cloud 批量写（ST-50）。

---

## S6 — 润色与发布 (Polish & Launch)

### 目标
> **"60fps 帧率稳定（含 Profiler 数据）、20 波内容完整、手感丝滑、Endless 排行榜可用。"**

### 范围（优先级排序）

**P0 必须完成**
- [x] **完整游戏外壳（启动→主菜单→游戏→暂停→结算→返回）** — *2026-06-05：商业级完整流程闭环。新增 `SceneManager`（Autoload，黑幕淡入淡出转场 + `start_mode` new/continue 路由）、`scenes/ui/splash.tscn`（启动画面：工作室名+游戏标题+版权，1.8s 自动/任意键跳过）、`scenes/ui/main_menu.tscn`（主菜单：新游戏/继续[无存档时禁用]/排行榜/设置/退出 + 语言切换 + 版本号）；`main_scene` 改为 `splash.tscn`；`combat_s2.gd` 接入 SceneManager 启动模式（新游戏=`start_game`/继续=`resume_game`）、新增暂停菜单（ESC，`process_mode=ALWAYS` 暂停时仍响应，继续/设置/返回主菜单/退出）、结算屏加「返回主菜单」按钮。全流程实测通过：splash→menu 自动跳转、新游戏/继续读档(恢复 wave=7/gold=250)进战斗、ESC 暂停冻结世界、死亡结算→返回主菜单(死亡删档使继续按钮禁用)。修复 2 bug：转场 Tween 竞争(fade_in_first kill 复位回调致 is_transitioning 卡死)、暂停 process_mode 致 ESC/按钮失效。**待补**：主菜单/splash 美术背景、FTUE 首启难度引导*
- [x] **程序化美术占位（视觉升级）** — *2026-06-05：从纯色几何方块升级为完整 bullet-heaven 视觉。新增 `shaders/bullet_glow.gdshader`（子弹加法混合发光圆：软核+外发光晕）、`shaders/enemy_circle.gdshader`（敌人体积感实体球：软边+左上高光+暗边描线）、`shaders/arena_grid.gdshader`（竞技场暗紫网格地面：细/粗双层网格+径向暗角）应用于两个 MMI + 背景 Polygon2D（z=-100）；玩家标记升级为发光菱形（青光晕环+橙菱形+白核）；`vfx_manager.gd` `_instantiate_vfx` 改为 `GPUParticles2D`（程序化爆裂粒子：球形发射+颜色渐变淡出+按特效 amount/speed，`one_shot` P6-N72，`play()` 触发 `restart`）；`assets/ui/ui_theme.tres` 全局 UI Theme（按钮圆角6px+蓝紫边框+悬停高亮+禁用态，经 `gui/theme/custom` 全局应用所有 Control）。实测：150 弹同心圆弹幕发光叠加、Boss 体积球、网格地面、圆角按钮均渲染正确，0 报错。**全部为程序化生成，放真实素材（`res://audio/sfx/`、精灵 Atlas、VFX 场景）即整体替换，见 handbook/03**
- [ ] 性能验收：确认所有 C# 热路径模块（`BulletManagerCs` / `EnemyManagerCs` / `SpatialGridCs` / `SpellEvaluatorCs` / `StatusManagerCs` / `ZoneManagerCs`）均满足各自帧预算（Profiler 实测）；若发现遗漏的 GDScript 瓶颈，按 ADR-L1 规则迁入 C#（`architecture_design.md §6.1`）
- [~] 三档渲染切换逻辑：实现 Area2D → Dirty Sync → MultiMeshInstance2D 渐进切换（`BATCH_DISABLE_PER_FRAME=100`，回滞阈值 `<1500`，`architecture_design.md §4.4`）— *2026-06-05：**直接实现 MultiMeshInstance2D 渲染（§4.4 最高档）**——此前子弹/敌人完全无渲染（仅 HUD+VFX 色块），现 `BulletManager.sync_multimesh`/`EnemyManager.sync_multimesh`（SoA 索引↔instance 索引共享，`visible_instance_count` 控制数量）+ 单位 QuadMesh + 敌人 per-instance 颜色/尺寸（Boss 大且醒目）；`combat_s2.gd` 建子弹/敌人两个 MMI + 玩家标记 + 跟随 Camera2D，`_process` 每帧同步。实测渲染正确（红杂兵/品红 Boss/黄子弹）。**Area2D↔MultiMesh 三档切换 N/A**：碰撞自 S1 即为 SpatialGrid 唯一路径（从未建 Area2D），故 MultiMesh 单档覆盖全密度即可，无需 Area2D 档与回滞切换。待补（润色）：Dirty Sync 视口剔除、子弹按 type 出图集、C# 化 transform 上传*
- [~] Wave 1~20 完整内容（敌人配置表、Mini Boss Wave 8、Final Boss Wave 20）（`game_design.md §3.3`）— *核心已实现 2026-06-05：`wave_manager.gd` 20 波配置表（W1-5 数值 / W6-9 护甲 / W11-19 弹幕量增 / W15+ 精英寻路）；敌人原型 `EnemyManager.Type`（BASIC/FAST/ARMORED/ELITE/MINIBOSS/BOSS）+ 按型速度/护甲；Mini Boss W8（450HP）、Final Boss W20（1800HP）+ 阶段系统（HP 66%/33% 进阶召援军）+ `BOSS_SPAWNED`；Boss 死亡即通关本波。Endless 数值膨胀。实测全部通过。**待补（润色）**：Boss 弹幕攻势（Phase2 走位）、元素抗性/反弹护盾怪、敌人 .tres 资源化、Boss JSON 驱动*
- [ ] 20 张+ 法术卡（含所有 Tier 1/2/3）
- [ ] **至少 7 种 Core** 可玩：`wand_basic`、`wand_fast`、`staff_long`、`matrix_board`、`circuit_fork`、`wand_memory`、`wand_eternal`（名称与数值以 `resources/cores/` 为准；与「5 种」旧表述对齐为 **7 种变体**）
- [x] 死亡流程：结算屏、Run 截图（`get_viewport().get_texture().get_image()`，死亡动画第 1 帧后执行）— *2026-06-05：`combat_s2.gd` 结算屏（到达波次/击杀/用时/评分 + 排行榜 + 「再来一局」）；`_save_run_screenshot()` 存 `user://runs/run_*.png`（结算屏非黑屏，P-S6-04 通过）。注：战场在 GAME_OVER 前已被 `_on_player_died` 重置，故截图为结算摘要屏而非死亡瞬间战场*
- [x] Endless 模式 + 本地排行榜（波次主排名、同波次按时间升序；评分公式：`score = wave × 100000 + (86400 - elapsed_sec)`，P6-N19；本地存储于 `user://endless_records.json`）— *2026-06-05：`endless_records.gd`（Autoload `EndlessRecords`）HMAC-SHA256 签名（ST-40）+ 读取验签拒绝篡改（ST-41）；排序波次降序/同波次用时升序；`compute_score` 实测 786386 正确。**修正一处真实 bug**：原按对象签名会因 JSON int↔float 往返破坏验签，改为对精确 JSON 字符串签名*

**P1 完成（时间允许）**
- [ ] `DUAL_STREAM` Core feature 实现（奇数槽中间槽前流专属，P6-N12）
- [ ] `ALWAYS_CAST_LAST`：尾槽为 MODIFIER/TRIGGER/LOGIC 时静默跳过（P6-N7）
- [ ] `SHUFFLE_DECK`：ACTION 原子单元整体乱序（P5-N2）
- [ ] `INFINITE_SPELLS`：配合 `heavy_cost` 的卖血流（P6-N18）
- [ ] 敌人元素弱点 + 抗性 + 催化反应（StatusManager 交互矩阵）
- [x] 音频系统（32 AudioStreamPlayer2D 池、0.1s 节流）— *2026-06-05：`audio_manager.gd`（Autoload `AudioManager`）32 池 round-robin + 每 sound_id 0.1s 节流 + 运行时建 SFX 总线 send→Master（受 SettingsManager 音量）；EventBus 挂钩（ENEMY_KILLED/BULLET_HIT/PLAYER_DAMAGED/WAVE_COMPLETE/BOSS_SPAWNED/SHOP_OPENED/LEVEL_UP）；无素材时程序化蜂鸣占位（放 `res://audio/sfx/<id>.ogg` 即替换）。实测池/节流/独立 id/未知 id 优雅 no-op/事件触发通过。**待补**：真实音效素材、BGM 分层*
- [ ] 移动端适配（虚拟摇杆映射到 InputMap Action）
- [ ] 完整图鉴系统（法术、Core、共鸣配方发现机制 P5-C2）
- [x] 难度系统：提供初学者 / 标准 / 挑战三档（初学者：敌人 HP ×0.7、伤害 ×0.7；挑战：W20 Boss HP ×1.3、Wave 怪物数 ×1.2）；难度选项在首次启动时通过 FTUE 引导选择，后续可在主菜单修改；难度配置持久化到 `user://save_data.json`（`difficulty: int`，0=初学者、1=标准、2=挑战）— *2026-06-05：`settings_manager.gd`（Autoload `SettingsManager`）三档难度 + 乘子（`enemy_hp_mult`/`player_dmg_taken_mult`/`wave_count_mult`/`boss_hp_mult`），接入 `PlayerStats.take_damage`（初学者 20→14 实测）、`WaveManager` 怪物数/血量/Boss 血（挑战 W1 20→24 实测）；持久化 `save_data.json`（difficulty/locale/master_volume/ftue_done）。设置面板（难度/语言/音量滑条/清除数据）实测渲染。**FTUE 首启难度引导已完成 2026-06-05**：首次启动（`save_data.json` 无 `ftue_done`）点「新游戏」弹出三档难度选择界面（初学者/标准/挑战，颜色编码绿/蓝/红 + 各档说明文字 + 「设置中可改」提示）；选定后 `SettingsManager.complete_ftue()` 设难度+标记+存档→进游戏；二次启动不再引导；ST-03 清除数据后重新触发。实测全通*
- [x] 设置菜单 + 音量（Master 总线）+ 「清除所有本地数据」入口（ST-03）— *2026-06-05：`combat_s2.gd` 设置面板（商店「⚙ 设置」按钮打开）；音量 HSlider→`AudioServer` Master 总线；`SettingsManager.clear_all_local_data()` 实测删除 save_data/endless_records/run_a·b/runs 目录并重置*

**P2 可选高级内容**
- [ ] `LOGIC_LABEL / LOGIC_JUMP_IF`（寄存器条件跳转，仅限 DEBUG Core 解锁）
- [ ] `LogicHandler` 接口（LOGIC 指令集 > 10 条时替换 match 硬编码，P6-N4 规范）
- [ ] `anywhere_in_deck` O(N²) 共鸣扫描（仅传说配方）
- [ ] SpellEvaluator 调试图（EditorPlugin 单步执行，Q2 预案）
- [ ] `ResourceLoader.load_threaded_request` 异步预加载（SpellRegistry P1 路线图）

### 验收标准 (P0)

- [~] **P-S6-01**：Wave 1~20 无崩溃完整通关；Mini Boss Wave 8 及 Final Boss Wave 20 AI 正常（`game_design.md §3.3`）— *系统已实现并实测：20 波配置全部正确生成（杂兵/护甲/精英寻路计数核对）；W8/W20 Boss 入场（450/1800 HP）+ 三阶段进阶（60%→P2、30%→P3 各召援军）+ `BOSS_SPAWNED`；Boss 死亡通关、普通波清空通关；护甲减伤（ARMORED 20→15）。完整 20 波连续通关压力测试待 S6 后续连跑*
- [x] **P-S6-02**：同屏 500 敌人 + 1500 子弹稳定 60fps（Profiler 实测）— *实测通过 2026-06-05（RTX 2060）：**实时 FPS=180**，`_physics_process` 实测帧时间 **0.91ms**；手工逐系统计时（60 reps）：Bullet 22.95μs + Enemy 396.97μs + SpatialGrid 0.15μs + Status 0.23μs + Zone 0.18μs + MultiMesh sync 180.10μs = **总计 0.601ms = 16.67ms 预算的 3.6%**。全部 GDScript 回退路径（无 C# 热化），仍有极大裕量*
- [x] **P-S6-03**：Endless 模式可进入；本地排行榜以波次为主排名、同波次按时间升序排（P6-N19 权威公式：在线分 = `wave × 100000 + (86400 - elapsed_sec)`；本地存储于 `user://endless_records.json`）— *实测 2026-06-05：排序 W12/250→W12/300→W8/90→W5/120；评分 786386 正确；HMAC 签名/验签/篡改拒绝通过。注：Endless「继续」入口（W20 后）属玩法接线，待补*
- [x] **P-S6-04**：死亡时截图正确保存到 `user://runs/`（不截到黑屏）— *实测 2026-06-05：`user://runs/run_*.png` 已生成（结算屏内容，非黑屏）*
- [x] **P-S6-05**：所有 P6-N 规范条目已在实际代码中验证（无违反）— *2026-06-05 审计通过（12 条约束）。发现并修复 1 件真实违反：`spell_evaluator.gd:504` `_acquire_ctx()` 在热路径 `execute_compiled`→`_physics_process` 中懒分配 `SpellContext.new()`（违反 P6-N3）。修复：在非热路径的 `compile_wand()` 阶段检测 `PERSISTENT_MEMORY` feature tag 时**预热分配** SpellContext（`_persistent_ctx[0]`），确保后续施法 `_acquire_ctx()` 走 dict.get() 取已有实例，不再触发 `new()`。P-S5-03 回归测试通过 `[0,0,1,0]`。其余 11 条全 PASS（ENEMY_STRIDE、BULLET_STRIDE、StatusID、compile_wand 参数序、zone_manager while+−=、vfx one_shot、PackedByteArray int()、CoreFeatureTag、registers 初始化、EventID 序列、ADR-A2 存档）*
- [~] **P-S6-06**：本地化与 **ADR-A1** 对齐：`translations/` 下至少存在 **`zh_CN.po`、`zh_TW.po`（繁体）、`en.po`、`ja.po`** 四个文件（键集一致；`zh_TW`/`ja` 可先用占位译文，但商店页承诺的语言须在发售前填满）；所有 S1–S6 玩家可见字符串均为 `tr("KEY")`（调试 `push_warning`/`push_error` 除外）；业务脚本无裸中文字符串（以项目约定之静态检查或人工审计为准）— *2026-06-05 骨架完成：`translations/` 四个 `.po` 全部就位且**键集一致（实测 4 语言 0 缺键）**，全部填了真实译文（非占位）；`Locale` Autoload 经 `TranslationServer.add_translation` 加载 + 切换；`combat_s2.gd` HUD/商店/结算/背包**主 UI 字符串已全部 `tr("KEY")` 化**（实测 en/ja/zh_TW 切换正确）；商店内加语言切换按钮。**待补**：Core/法术/状态/Boss 的 `display_name`/`description`（现为 `WandPreset`/各 Def 内硬编码，需键化为 `SPELL_*`/`CORE_*`/`STATUS_*` 并补四语）；裸中文静态检查脚本*
- [x] **P-S6-07**：内存峰值实测（Wave 20 Boss 战场景）：PC 端 RSS < 512MB，对照 `architecture_design.md §4.6` 内存预算表各类别实测均不超标 — *实测通过 2026-06-05（W20 满载：500敌+2000弹+500状态+32 Zone）：`MEMORY_STATIC`=105.9MB + 引擎基础≈70MB → **估算 RSS≈175.9MB（34%，目标<512MB ✓）**；热数据（全 SoA+MultiMesh）仅 0.3MB；VRAM=92.6MB 为 GPU 显存不计入 PC RSS。Switch 共享内存（VRAM+RAM≈268MB）低于 SW-11 的 2.5GB 限额，实机测量待 Switch 移植时确认*

---

## 三、时间线估算 (Timeline Estimate)

> 以"单人 + 2D 美术外包"为基准估算，仅供参考，实际根据团队调整。

| 切片 | 状态 | 估算工时 | 主要阻塞风险 |
| :--- | :---: | :--- | :--- |
| **S0 技术骨架** | ✅ | 1~2 周 | R-01/R-03 已缓解（GDScript 回退路径） |
| **S1 最小战斗** | ✅ | 1 周 | — |
| **S2 核心循环** | ✅ | 1~2 周 | — |
| **S3 法术管道** | ✅ | 2~3 周 | — |
| **S4 战斗深度** | ✅ | 1~2 周 | — |
| **S5 高级构建** | ✅* | 3~4 周 | 功能完成（*C# 压力 P-S5-ZM-01 顺延）；_flatten_circuit 嵌套分叉已验、MinionManager AI 已验 |
| **S6 润色发布** | ⬜ | 4~6 周 | 内容量（法术/敌人设计）；平衡调整 |
| **合计** | — | **~14~20 周** | R-08 C# 热路径迁移若触发则追加 1~2 周 |

---

## 四、跨切片技术约束速查 (Cross-Slice Constraints)

以下约束贯穿所有切片，违反时导致回归风险，开发时随时核查：

| 约束 | 来源规范 | 高风险操作 |
| :--- | :--- | :--- |
| 禁止 `DamageContext` / `SpellContext` 在战斗循环中 `new()` | P6-N49、P6-N3 | 任何 `apply_damage` 调用路径 |
| `DamageContextPool.acquire()` 必须先 `ctx.reset()` | P6-N67 | 任何新增 `acquire` 调用 |
| `StatusType.XXX` 枚举不存在，必须用 `StatusID.XXX` | P6-N30 | 新增状态效果代码 |
| `CoreFeatureTag.XXX` 常量，禁止裸字符串 | ADR-R5-N2 | Feature Tag 判断逻辑 |
| `ENEMY_STRIDE=8` 命名常量（禁止裸整数 8） | P6-N54 | EnemyManager 所有 SoA 访问 |
| `BULLET_STRIDE=12` 命名常量（禁止裸整数 12） | `architecture_design.md §4.2` | BulletManager 所有 SoA 访问 |
| `SpellContext.registers` 必须初始化长度 4 | P6-N48 | SpellContext 对象池 reset() |
| `compile_wand(core, raw_deck)` 参数顺序 core 在前 | P6-N70 | SpellEvaluator 入口调用 |
| ZoneManager tick 用 `while + -=`，不用 `if + =0` | P6-N71 | ZoneManager 任何 tick 逻辑 |
| `VFXManager._instantiate_vfx` 必须 `one_shot=true` | P6-N72 | VFX 节点创建路径 |
| `CHARGE_FIRED`(ID=14) 一次性；SpellEvaluator 订阅此而非 `CHARGE_STATE_CHANGED` | P6-N69 | 蓄力法术实现 |
| `SubPayloadRegistry` 仅在关卡进行中只读，背包关闭后重编译 | P6-N10 | 任何支持"战斗中改装备"的功能 |
| Run 存档 **A/B 双槽**、`schema_version`、**`_migrate_run` 链** | ADR-A2 | 单槽覆写、跳过迁移、损坏全丢 |
| `EventID` **1–21** 与 `implementation_plan.md` §2.1 表一致（含 `ACHIEVEMENT_UNLOCKED=18`、`GAME_STATE_CHANGED=20`、`SPELL_DROP_PICKUP=21`）；新增事件从 22 起递增并向 §2.1 表登记 | E-N1、certification ST-10、boss_design §6 | 在业务代码中硬编码与表冲突的事件整数 |
| C# 内层循环（`for` / `while` 体内）禁止 `GodotObject.Call()` / `.Set()` | ADR-L1 规则 1 | `BulletManagerCs` / `EnemyManagerCs` 任何新增热路径代码 |
| C# 访问 SoA 热数组必须通过 `PackedFloat32Array.AsSpan()` 获取 `Span<float>` 再做索引 | ADR-L1 规则 2 | 任何新增 C# SoA 读写代码 |
| `PackedByteArray[i]` 赋值必须显式 `int()` 转换 | P6-N27 | `_visible_flags` 相关代码 |
| `ProjectileDef.reset()` 必须重置 `spawn_position` | P6-N41 | BulletManager spawn 路径 |

---

## 五、参考文档导航 (Reference Map)

| 需要了解 | 首要参考文档 | 章节 |
| :--- | :--- | :--- |
| 局内 Run 存档 A/B 与迁移 | `technical/architecture_design.md` | ADR-A2 |
| Elite / Boss 寻路 | `technical/architecture_design.md` | ADR-A4 |
| 全局架构与层级关系 | `technical/architecture_design.md` | §2~4 |
| SpellEvaluator 执行流 + 预编译 | `technical/architecture_design.md` | §3.4 |
| BulletManager SoA 字段定义 | `technical/architecture_design.md` | §4.2 |
| ZoneManager 完整实现 | `technical/architecture_design.md` | §8 ADR-R5-N1 |
| EventBus 事件目录 | `technical/implementation_plan.md` | §2.1 |
| DamageContext 字段权威 | `technical/implementation_plan.md` | §2.1 (P6-N49) |
| EnemyManager SoA + LOD | `technical/implementation_plan.md` | §2.3.C |
| PlayerManager 移动状态追踪 | `technical/implementation_plan.md` | §2.5.A |
| VFXManager 完整实现 | `technical/implementation_plan.md` | §2.5.D |
| DPS 环形缓冲区 | `technical/implementation_plan.md` | §2.5.E |
| Core 插槽拓扑、Feature Tags | `design/core_wand_design.md` | §1~3 |
| SpellEvaluator 两阶段接口 | `design/core_wand_design.md` | §6 |
| 数值公式（XP/伤害/商店） | `design/numerical_design.md` | §1~3 |
| 法术参数参考表 | `design/numerical_design.md` | §2.2 |
| 状态效果与 DoT | `mechanics/combat_mechanics_depth.md` | §3~4 |
| BulletContext 连锁弹射 | `mechanics/combat_mechanics_depth.md` | §2/5 |
| StatusManager Tick 机制 | `mechanics/combat_mechanics_depth.md` | §3.2.5 |
| COMBO_MARK / VULNERABILITY | `mechanics/consecutive_hits_stacking.md` | 全文 |
| MinionManager / ZoneManager | `mechanics/advanced_mechanics_summons_and_environment.md` | §1/3 |
| StatusID Autoload 规范 | `mechanics/advanced_mechanics_summons_and_environment.md` | §3.2 |
| 运动学 / 物理扩展（P2/P3） | `mechanics/combat_mechanics_extensions_v2.md` | §1~5 |
| 弹道修正器（正弦/环绕） | `mechanics/weapon_system_expansion.md` | §1 |
| 全规范速查表 (P6-N\*) | `README.md` | §关键规范速查 |

