docs: 整理文档目录并对齐代码现状

- 将开发过程/归档文档迁至 docs_dev/(development_plan、certification_checklist、
  已废弃的 Cocos 架构草案 archived_cocos_architecture_draft),并修正全部跨引用
- 新增根 README.md(项目介绍,暂定名 Spellforge)与 docs_dev/README.md 索引
- 新增 docs_dev/doc_code_audit_2026-07-20.md:文档 vs 代码交叉审计报告(经 6
  路对抗性复核,零证伪),含「代码更优 / 文档更优 / 中性」判定汇总
- 在 docs/ 各设计·技术·机制文档就地加「实现现状 (2026-07-20)」callout:
  追认代码更优实现(纯 JSON 数据驱动、SpatialGrid-only 碰撞、MultiMesh 单档、
  存档选最新槽等),订正陈旧/矛盾内容(.tres→JSON、Boss HP/阈值/波次、EventID、
  StatusManager.apply 签名等),标记未实现功能(C# 热路径、Mana、元进展、
  Boss 阶段/抗性、Tutorial、轨迹/连锁/催化等)与 latent bug(CoreFeatureTag 位运算、
  pierce 空操作、MAX_OPS 不读 cpu_limit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-20 14:35:55 +08:00
co-authored by Claude Opus 4.8
parent 7bcc0026e0
commit ad2f4c0bc6
20 changed files with 446 additions and 67 deletions
+13 -5
View File
@@ -7,22 +7,30 @@
## 目录结构
```
docs/
docs/ 产品文档(设计规范 / 架构参考)
README.md 本文件,文档导航索引
design/ 游戏设计文档
technical/ 技术架构文档
mechanics/ 机制细节设计
plan/ 垂直切片开发计划与平台认证清单
handbook/ 开发者 / 策划手册
docs_dev/ 开发过程 / 归档文档(工程根目录,见下)
development_plan.md 垂直切片开发计划
certification_checklist.md 平台认证清单
archived_cocos_architecture_draft.md 早期 Cocos 架构草案(已废弃)
```
---
## 工程计划 (`plan/`)
## 工程计划与归档 (`../docs_dev/`)
> 开发过程/追踪与归档类文档已移出 `docs/`,统一存放于工程根目录的 [`docs_dev/`](../docs_dev/)。
| 文档 | 内容简述 |
| :--- | :--- |
| [development_plan.md](plan/development_plan.md) | S0–S6 骨架垂直切片、风险登记、验收与出口检查、跨切片约束、参考文档地图(与 `technical/` 架构对齐) |
| [certification_checklist.md](plan/certification_checklist.md) | Steam / Switch / 无障碍 / 发布前检查项,并映射到切片 |
| [development_plan.md](../docs_dev/development_plan.md) | S0–S6 骨架垂直切片、风险登记、验收与出口检查、跨切片约束、参考文档地图(与 `technical/` 架构对齐) |
| [certification_checklist.md](../docs_dev/certification_checklist.md) | Steam / Switch / 无障碍 / 发布前检查项,并映射到切片 |
| [archived_cocos_architecture_draft.md](../docs_dev/archived_cocos_architecture_draft.md) | 早期 Cocos Creator / TypeScript 架构草案(已废弃,权威架构见 `technical/` |
---
-214
View File
@@ -1,214 +0,0 @@
# [已归档] 模块化战术土豆 - 早期架构草案 (Cocos Creator 3.x / TypeScript)
> ⚠️ **此文件为历史草案,已废弃。**
> 项目已迁移至 **Godot 4.6 + GDScript/C#**。
> 当前权威架构请参阅:[docs/technical/architecture_design.md](technical/architecture_design.md)
---
## 1. 概述 (Overview)
本文件记录了项目早期基于 **Cocos Creator 3.x** 的架构探索。核心设计思想(HMWS 法术系统、ECS-Lite、Zero-GC、Strategy 模式)已被 Godot 版本继承并大幅扩展。
核心体验结合了 **Brotato (土豆兄弟)** 的快节奏割草体验与 **Noita** 的深度法术构建系统。
架构设计的首要目标是 **高性能**(支持同屏大量单位与弹幕)与 **极高的可扩展性**(特别是武器系统的模块化)。
### 1.1 设计目标
1. **超模块化武器系统 (Hyper-Modular Weapon System)**:超越 Noita 的线性构建,引入更灵活的管道流与事件钩子机制。
2. **高性能战斗引擎**:支持同屏 500+ 敌人,2000+ 弹幕,60FPS 稳定运行。
3. **数据驱动 (Data-Driven)**:所有游戏内容(法术、属性、波次)完全配表化/JSON化。
---
## 2. 系统分层架构 (Layered Architecture)
采用能够严格分离数据与表现的架构模式。虽然 Cocos 是组件式的,但在核心战斗层我们将采用 **Manager + Data** 的方式来规避组件更新带来的开销。
```mermaid
graph TD
Layer1[表现层 (Presentation Layer)] --> Layer2[逻辑层 (Domain/Logic Layer)]
Layer2 --> Layer3[数据层 (Data Layer)]
Layer2 --> Layer4[核心库 (Core Library)]
subgraph Layer1
ViewComponents[Cocos Components (Sprite, Animation)]
UIManagers[UI System]
Effects[Particle Wrapper]
end
subgraph Layer2
CombatMgr[Combat Manager (Main Loop)]
SpellEvaluator[Spell Interpreter (The "CPU")]
EnemyAI[Boid AI System]
GameCycle[Wave & Shop Cycle]
end
subgraph Layer3
ConfigMgr[JSON Config Loader]
SaveSystem[Persistent Storage]
Inventory[Player State & Inventory]
end
subgraph Layer4
Pool[Object Pool System]
SpatialHash[Spatial Hashing (Collision)]
EventBus[Global Event Bus]
end
```
---
## 3. 核心子系统:超模块化法术系统 (HMWS)
这是本项目的技术核心。我们将 Noita 的“魔杖”概念抽象为 **“法术管道 (Spell Pipeline)”**。
### 3.1 核心概念差异
| 特性 | Noita 原版 | HMWS (本项目) | 改进目的 |
| :--- | :--- | :--- | :--- |
| **执行流** | 线性 (Deck -> Hand -> Discard) | **树状/图状结构 + 事件驱动** | 支持“子母弹”、“条件触发”、“击中后分裂逻辑”的无限嵌套。 |
| **属性计算** | 累加式 (Cast Delay += 0.1) | **管线式 (Pipeline)** | 允许中间件对属性进行乘算、覆写或逻辑重定向。 |
| **载体** | 法杖 (Wand) | **构建核心 (Core)** | 核心决定了插槽拓扑结构(不仅仅是线性数组,可能是矩阵或特定触发槽)。 |
### 3.2 数据结构设计 (TypeScript)
#### A. 基础单元 (ISpellNode)
这是所有“部件”的基类。
```typescript
interface ISpellContext {
caster: Entity; // 施法者
target: Vec2; // 目标点
stats: CastStats; // 当前累积的属性(伤害、速度、扩散等)
payloads: ProjectileDef[]; // 待发射的弹头定义队列
}
abstract class SpellNode {
id: string;
type: SpellType; // ACTION (投射物), MODIFIER (修正), TRIGGER (触发器), LOGIC (逻辑门)
// 核心执行函数:修改 Context 或 产生行为
abstract execute(ctx: ISpellContext, deck: SpellDeck): void;
}
```
#### B. 法术解析器 (The Evaluator)
为了高性能,解析器必须 **零垃圾回收 (Zero-GC)**。在施法计算帧,不应当 `new` 任何对象。
* 使用预分配的 `Context` 对象池。
* 使用 `Stack<SpellNode>` 来模拟递归,防止深层递归爆栈。
#### C. 高级特性:动态插槽与逻辑门
* **Logic Spells (逻辑法术)**:引入 `IfHPBelow`, `OnKillAction`, `EveryNbShot` 等逻辑块,让玩家实现“如果血量低于30%,则发射吸血导弹”的构建。
* **Variable Storage (变量存储)**:允许法术在法杖上写入/读取临时变量(例如:记录连击数)。
### 3.3 扩展性设计
所有法术行为通过 **Strategy Pattern (策略模式)** 实现。
新增一个法术只需:
1. 在 JSON 中定义 ID 和贴图。
2. 实现一个 `SpellAction` 类。
3. 在注册表中注册。
### 3.4 进阶构建机制 (Advanced Mechanics) - 玩法增强
为了超越“线性堆砌”的枯燥感,架构支持以下三种深度玩法机制:
#### A. 拓扑插槽系统 (Topology Slots)
核心(Core)不再仅仅是一个列表,它可以是一个 **2D 网格****电路板**
* **adjacency_bonus (邻接加成)**:某些插槽有物理连接。例如,将 [火元素] 放在 [高压槽] 旁边,会自动获得 +20% 范围。
* **Circuit Logic (电路逻辑)**:法术流不再只是从左到右。核心板可以有分叉路口,玩家需要用 [分流器法术] 将能量流引导到不同的分支。
#### B. 状态寄存器与图灵完备 (State Registers & Turing Completeness)
为了实现真正的“图灵完备”,架构必须支持:**状态存储**、**条件跳转** 和 **循环**
1. **Registers (寄存器)**:
*`ISpellContext` 中引入 `MemoryBank`,提供 4 个 Float 寄存器 (`R1`, `R2`, `R3`, `R4`)。
* 寄存器在同一帧内所有法术间共享,甚至可以跨帧持久化(如果法杖配置了 Persistent Memory 核心)。
2. **Instruction Set (指令集法术)**:
* **OPS**: `Add R1, 1` (加法), `Set R2, HP_Percent` (赋值).
* **JUMP**: `JumpIf R1 > 10, Label_A` (条件跳转到标签A).
* **LABEL**: `Label_A` (标记跳转点).
3. **Recursion Control (递归控制)**:
* 为了防止死循环 (`While(true)`), 解释器引入 `MaxOpLimit` (最大操作数限制,例如 100 ops/frame)。超过限制强制中断并在此帧失效。
4. **实战应用**:
* **计数器**: 每射击 3 次,第 4 次发射强力火球。
* **动态模式切换**: 根据敌人距离 (`R1 = EnemyDistance`),如果近则跳转到 [霰弹逻辑],如果远则跳转到 [狙击逻辑]。
#### C. 共鸣系统 (Resonance System)
在**预编译阶段 (Pre-compile Phase)** 进行模式匹配。
* 如果检测到 `[水]``[电]` 法术在执行链中紧邻,架构自动插入一个隐藏的 `[导电反应]` 中间件。
* 这允许设计隐藏配方(Hidden Recipes),鼓励玩家探索特定组合。
---
## 4. 高性能战斗架构 (High-Performance Combat Architecture)
为了实现“同屏 2000+ 弹幕”和“复杂逻辑构建”的双重目标,本架构采用 **Data-Oriented (面向数据)****Hybrid-ECS** 相结合的策略,最大化 CPU 缓存命中率并消除 GC 压力。
### 4.1 核心原则:零 GC (Zero-GC Principle)
在核心战斗循环 (Game Loop) 中,**绝对禁止**使用 `new` 关键字分配堆内存。
* **Context Pooling**: `ISpellContext` 等高频对象在关卡加载时预分配 2000 个,使用时复用,用完 `Reset`
* **Static Temporaries**: 向量计算使用全局静态临时变量 (`_tempVec2`),避免中间对象产生。
### 4.2 实体管理:ECS-Lite
虽然 Cocos Creator 是基于组件的,但在海量单位管理上,我们将剥离组件的 Update 逻辑。
* **Manager-Based Logic**: 子弹 (`Bullet`) 和 敌人 (`Enemy`) 不挂载 `UpdateComponent`
* 也就是:`cc.Node` 仅仅作为渲染容器。
* **Centralized Loop (中央循环)**:
* `BulletManager` 维护一个紧凑的 `Float32Array` (SoA 布局: `[x, y, vx, vy, type...]`)。
*`update(dt)` 中,直接遍历 Array 进行物理积分,速度比遍历 Node Tree 快一个数量级。
* **Dirty Sync**: 仅当物体在屏幕视口内,且逻辑坐标发生位移时,才去同步 `cc.Node.position`
### 4.3 物理与碰撞机制
* **Builtin Optimization**: 优先使用 Cocos **Builtin-Physics** (非 Box2D) 进行 Geometry Overlap 检测。
* **Spatial Hashing Fallback**: 若 Builtin 仍有压力,回退到定制的 **Spatial Grid** (一维数组网格),只计算临近 Grid 的实体碰撞,确保碰撞检测复杂度维持在 O(N)。
* **Separation Logic**: 怪物挤压不使用刚体求解,而是施加简单的轻量级斥力向量。
### 4.4 渲染优化
* **Node Pooling**: 严格的节点池管理。
* **Throttling (分帧降频)**:
* 伤害数字:每帧最多弹出 10 个,多余的合并或延迟显示。
* AI 索敌:不需要每帧执行 `FindNearest`,可分散到 10~20 帧内轮询一次。
---
## 5. 游戏循环设计 (Game Loop)
结合 Brotato 的经济循环:
1. **准备阶段 (Shop/Inventory)**
* 玩家拖拽法术卡牌组合逻辑。
* **解析预热**:在玩家关闭背包时,预先编译法术链,生成 cached 的指令列表。避免战斗中实时解析带来的开销。
2. **战斗阶段 (Wave)**
* 生成大量敌人。
* Player 自动开火 (执行 Cached 指令列表)。
* 掉落拾取 -> 经验值/金币。
3. **结算阶段**
* 随机 3 选 1 升级(属性成长)。
* 商店刷新法术与道具。
---
## 6. 技术栈选型总结
| 模块 | 方案 | 理由 |
| :--- | :--- | :--- |
| **语言** | TypeScript | 类型安全,便于重构 |
| **ECS框架** | Custom Lite (Manager-based) | 避免第三方 ECS 库的学习成本与 overhead,针对本项目定制最优 |
| **物理** | Custom Spatial Hash | Box2D 性能瓶颈明显 |
| **UI** | Cocos UI + Virtual List | 背包道具可能很多,需要虚拟列表优化 |
| **配置** | JSON + Type Interface | 灵活且易于热更 |
## 7. 目录规范建议
```text
assets/
Scripts/
Core/ # 核心架构 (EventBus, Pool, BaseClasses)
Systems/ # 独立系统 (Physics, Input, Audio)
Domain/ # 游戏业务逻辑
SpellSystem/ # 法术解释器, 定义, 执行栈
Combat/ # 伤害计算, 弹道管理
Enemy/ # AI 行为树
View/ # UI 控制, 特效表现
Config/ # 配置表加载器与类型定义
```
+8 -3
View File
@@ -54,8 +54,9 @@ extends Resource
@export var base_cast_delay: float = 0.25 # 基础施法后摇(乘算基准)
@export var base_recharge_time: float = 0.5 # 完整一轮打完后的充能时间
## Core 特性标签 (Array[String]),驱动特殊执行规则
## 例如: ["persistent_memory", "dual_stream", "shuffle_deck"]
## Core 特性标签,驱动特殊执行规则
## ⚠️ 实现现状(2026-07-20):代码实为 `@export var feature_tags: int = 0`int 位掩码,非 Array[String]);
## CoreFeatureTag 常量为 int 1..5 并用 `&` 判定——值非 2 的幂有位冲突 bug(见审计 D 节)。
@export var feature_tags: Array[String] = []
## 图标路径
@@ -92,7 +93,7 @@ extends Resource
| **ACTION** | **ACTION**(同 ID) | 完全相同的 ACTION 对齐 | 伤害 ×1.5`damage_mult +0.5` | "强化同类弹" |
| **ACTION** | **MODIFIER** | 任意 | 该 MODIFIER 效果额外再施加一次(等效双倍加成) | "增幅器加倍" |
| **MODIFIER** | **MODIFIER**(同 ID | 完全相同的 MODIFIER 对齐 | 修正值 ×2(平直叠加而非乘算) | "共鸣修正" |
| **ACTION** | **TRIGGER** | 任意 | 该 TRIGGER 的 `proc_rate ×1.5`(最大 1.0 | "增压触发" |
| **ACTION** | **TRIGGER** | 任意 | 该 TRIGGER 的 `proc_rate ×1.5`(最大 1.0 | "增压触发" ⚠️**未实现**(代码只实现 ACTION+ACTION / ACTION+MODIFIER / MODIFIER+MODIFIER 三行)|
| **LOGIC** | **任意** | 任意 | 无加成(LOGIC 节点不参与邻接计算) | 设计原则:逻辑层不受物理拓扑影响 |
| **空槽** | **任意** | 行 A 为空 | 无加成 | 空槽不传递邻接效果 |
@@ -150,6 +151,8 @@ extends Resource
| `infinite_spells` | 法力耗尽时不停止执行,但每发增加 1 点 HP 代价(需配合 `heavy_cost` 法术) | 传说 |
| `always_cast_last` | 无论 Deck 如何执行,最后一个插槽的法术在一轮结束时**必定**执行一次 | 稀有 |
> **⚠️ 实现现状 (2026-07-20 审计)**5 个特性标签中**只有 `persistent_memory` 实现**`dual_stream` / `shuffle_deck` / `infinite_spells` / `always_cast_last` 在代码里除常量定义外**无任何逻辑引用**`core_feature_tag.gd:6` 自注"S6 P1 实现")。下文对这 4 个标签的详细行为规范(P5-N2/P6-N7/P6-N12 等)均为目标设计。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
> **实现约束说明(P5-N2**
> - **`shuffle_deck`**:仅随机化**独立 ACTION 节点**的执行顺序;MODIFIER 节点始终与其紧接的 ACTION 视为原子单元整体移动,防止修正器与错误的动作配对,保持构建意图的确定性。UI 上以"卡组"为单位展示被打乱的顺序,而非单张卡牌。
> - **`dual_stream`**:两条执行流**各自独立计算 ops 消耗**,每条流的 MAX_OPS 上限 = `cpu_limit × MAX_OPS_PER_CPU / 2`(平均分配)。双流模式下单帧总指令数与单流相同,不额外增加运算力消耗;实际可用插槽从序列头尾各取一半,两条流不可互相访问对方的槽位。
@@ -220,6 +223,7 @@ extends Resource
# ── 阶段 1:编译(法杖装备/换牌时调用,结果缓存于 CompiledDeck)──────────────────
# 参数类型:raw_deck: SpellDeck(通过 deck.nodes[i] 访问节点序列,禁止传入 Array[SpellNode]
# ⚠️ 实现现状(2026-07-20):真实签名 compile_wand(core, raw_nodes: Array)——第 2 参是 Array 而非 SpellDeck
func compile_wand(core: CoreDefinition, raw_deck: SpellDeck) -> CompiledDeck:
match core.topology:
CoreDefinition.LINEAR:
@@ -234,6 +238,7 @@ func compile_wand(core: CoreDefinition, raw_deck: SpellDeck) -> CompiledDeck:
# ── 阶段 2:执行(每次施法时调用缓存的 CompiledDeck)────────────────────────────
# 必须接受 core: CoreDefinition 参数,用于读取 core.feature_tags 判断寄存器持久策略。
# 禁止将 feature_tags 复制到 SpellContextSpellContext 不持有 Core 引用;ctx.core_feature_tags 不存在)。
# ⚠️ 实现现状(2026-07-20):真实签名 execute_compiled(compiled, caster_id: int, spawn_pos: Vector2)——不传 ctx/corefeature_tags 从 CompiledDeck 编译期快照读取
func execute_compiled(compiled: CompiledDeck, ctx: SpellContext, core: CoreDefinition) -> void:
_run_deck(compiled, ctx) # SpellEvaluator 不感知拓扑,仅执行线性化节点序列
+15 -1
View File
@@ -131,6 +131,9 @@
为了支撑如此复杂的构建,数值系统必须严谨且具备极其宽广的边界。
### 4.1 核心资源:魔力 (Mana)
> **⚠️ 实现现状 (2026-07-20 审计)****Mana 魔力系统完全未实现**——`PlayerStats` 无 mana 变量,法术无蓝耗/回蓝逻辑。作为设计文档声称的核心平衡阀,这是最大的功能缺口之一。当前施法仅受 `MAX_OPS` 与 `Cast Delay` 限制。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
为了防止无限“加特林核弹”,限制资源是 **魔力**
* 强力修正器(如伤害+100)会有巨大的 **Delay (施法后摇)** 和 **Mana Cost (蓝耗)**
* **平衡点**:高射速 = 低单发伤害;高爆发 = 便秘的手感(打一发回蓝3秒)。
@@ -148,6 +151,9 @@ $$ FinalDamage = ((Base + \sum Mod_{flat}) \times \prod Mod_{mult}) \times (1 -
* **Armor**: 敌人护甲平铺扣除值(固定数值,不受百分比影响)。最终伤害最小为 1。
### 4.3 属性词条池 (Stats Pool) — *MVP 层*
> **⚠️ 实现现状 (2026-07-20 审计)**:下表 5 条 MVP 属性中**仅 CPU`cpu_limit`)实现**——且因 `MAX_OPS` 硬编码不读 cpu_limit,连它也对施法预算无效(见架构文档 §3.4 现状注)。Cast Speed / Mana Leech / Accuracy / Attunement 均未实现。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
玩家在升级或商店中购买的属性,直接影响构建的上限。
> ⚠️ **MVP 说明**:以下 5 条属性为 MVP 阶段实现目标。后续扩充方向:
@@ -172,6 +178,8 @@ $$ FinalDamage = ((Base + \sum Mod_{flat}) \times \prod Mod_{mult}) \times (1 -
Boss 出现于特定里程碑波次,强制验证玩家构建的适应性。
> **⚠️ 实现现状 (2026-07-20 审计)**:本节多为设计蓝图,代码差异较大。① **波次**:精英/Mini Boss 实际在 **W8**(非本节标题的 W10),最终 Boss 在 W20`waves.json`)。② **阶段阈值**:代码统一用 **66%/33%**,非下方 JSON schema 的 50%/10%。③ **血量**:代码取 `balance.json` 的 450(mini)/1800(final),与 `numerical_design §3.2` 的 50000 冲突(DPS 门槛测算已失真)。④ **未实现**:元素抗性 `(1-Res)`、镜像法师反射、重甲降甲、`regen_on_physical_hit` 虚空再生、NG+ Phase4——Boss 现状只是会在血线召唤 FAST 援军的厚血怪。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
#### W10 精英 Boss (第 10 波)
* **功能**: 展示某一核心机制的利用方式。
* **典型设计示例**:
@@ -265,7 +273,7 @@ Boss 阶段数据完全由 JSON 驱动(`res://resources/bosses/boss_XX.json`
3. **购物 (The Shop)**:
* 战斗结束,进入商店。
* **货架 A**: 出售新的法术卡片(随机池)。
* **货架 B**: 出售新的”核心”(更牛的主板)。
* **货架 B**: 出售新的”核心”(更牛的主板)。 ⚠️**未实现**(现状商店仅货架 A 法术抽取;货架 C 属性、出售退款 G5 亦未实现)
* **货架 C**: 基础属性提升(生命值、暴击率)。
* **出售与转型规则 (G5)**
* 玩家可在商店界面将背包中的法术卡**出售**,回收该卡购入价格的 **50%**(下限 10G)。
@@ -302,6 +310,8 @@ Roguelite 的"第一把"体验至关重要。系统复杂度必须渐进式解
## 7. 元进展设计 (Meta-Progression)
> **⚠️ 实现现状 (2026-07-20 审计)**:**本章整体未实现**——以太碎片局外货币、局外解锁树、图鉴 Codex、成就系统在代码中均无实现(仅 1 个孤立未接线的 `ACHIEVEMENT_UNLOCKED` 事件常量)。属 Roguelite 长期粘性核心,为后续切片的功能待办。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
元进展是 Roguelite 的长期粘性来源。玩家每次游玩都在为"总体库"做出贡献,即使失败也有收获感。
### 7.1 局外货币:以太碎片 (Aether Fragments)
@@ -345,6 +355,8 @@ Roguelite 的"第一把"体验至关重要。系统复杂度必须渐进式解
### 8.2 通关条件与通关后内容 (G2)
> **⚠️ 实现现状 (2026-07-20 审计)****通关流程整体未实现**。击败 W20 Boss 后照常结算进商店并继续 W21,无通关演出、无 `GAME_CLEARED` 事件、无 NG+ 解锁;游戏目前只能靠死亡结束。Endless 已实现但为**逐波 ×1.12 膨胀**(非"每 5 波 +10%"),且无 Boss Rush、无全局词条。EndlessRecords 本地排行榜(评分 + HMAC)已实现。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
* **通关条件**:击败 W20 终波 Boss(三阶段精英怪),触发通关演出。
* **首通奖励**
* 给予 30 以太碎片(固定)。
@@ -369,6 +381,8 @@ Roguelite 的"第一把"体验至关重要。系统复杂度必须渐进式解
### 8.3 里程碑与 Checkpoint (G3)
> **⚠️ 实现现状 (2026-07-20 审计)**:本节里程碑体系(W5/W10/W15 奖励与练习模式解锁)**未实现**;练习模式、Boss Rush 均无代码。里程碑碎片依赖的元进展系统(以太碎片/解锁树/图鉴)也未落地。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
到达 **W5 / W10 / W15** 触发里程碑事件:
| 里程碑 | 即时奖励 | 解锁内容 |
+4 -2
View File
@@ -21,7 +21,7 @@
| `cast_delay_mod` | 施法延迟修正 | 1.0 (100%) | 0.1 | 0.01 | 越低越快,乘算系数 |
| `recharge_speed_mod` | 充能速度修正 | 1.0 (100%) | 5.0 | - | 越低越快 |
| `luck` | 幸运 | 0 | 100 | - | 影响高阶法术掉率、暴击率 |
| `cpu_limit` | 运算力上限 | 5 | 20 | 50 | `SpellEvaluator` 实际 MAX_OPS = `cpu_limit × 40`(默认 5 → 200 步;软上限 20 → 800 步;硬上限 50 → 2000 步)。升级此属性可执行更复杂的法术链,与 `implementation_plan.md §2.2``MAX_OPS_PER_CPU = 40` 常量对应。 |
| `cpu_limit` | 运算力上限 | 5 | 20 | 50 | `SpellEvaluator` 实际 MAX_OPS = `cpu_limit × 40`(默认 5 → 200 步;软上限 20 → 800 步;硬上限 50 → 2000 步)。升级此属性可执行更复杂的法术链,与 `implementation_plan.md §2.2``MAX_OPS_PER_CPU = 40` 常量对应。 ⚠️**实现现状(2026-07-20)**:代码硬编码 `MAX_OPS = 40×5 = 200`**从不读 cpu_limit**——升级此属性对施法预算无效(待修,本设计更优)。 |
| `attunement_fire` | 火焰精通 | 0 | 50 | 100 | 每点:火焰伤害 +3%(乘算),火焰状态(点燃/爆燃)触发概率 +2%。影响 `damage_type=FIRE` 的所有投射物。 |
| `attunement_ice` | 冰霜精通 | 0 | 50 | 100 | 每点:冰霜伤害 +3%,冰冻/减速触发概率 +2%。 |
| `attunement_lightning` | 雷电精通 | 0 | 50 | 100 | 每点:雷电伤害 +3%,麻痹/连锁导电触发概率 +2%。 |
@@ -135,7 +135,7 @@ Damage = ((Base + Add) × Mult) × (1 - Res) - Armor
| Wave 1 | 杂鱼土豆 | 15 | 100 | 5 | - |
| Wave 5 | 冲锋甲虫 | 80 | 250 | 12 | 击退抗性 50% |
| Wave 10| 坦克巨兽 | 2000 | 50 | 30 | 护甲 5 (免疫机枪) |
| Wave 20| 虚空领主 | 50000| 150 | 100 | 弹幕反射盾 |
| Wave 20| 虚空领主 | 50000 ⚠️| 150 | 100 | 弹幕反射盾 |
### 3.3 波次强度控制 (Pacing)
@@ -191,6 +191,8 @@ Damage = ((Base + Add) × Mult) × (1 - Res) - Armor
### D. W20 Boss Phase 1 DPS 门槛(P6-N17
* **问题**: Phase 1 的 `regen_on_physical_hit = 0.005`(每次物理命中恢复 Boss 最大 HP 的 0.5%)。
Boss 最大 HP = 50,000,即每次物理命中回血 **250 HP**
> **⚠️ 实现现状 (2026-07-20 审计)**:代码 Boss 实际 HP = 450(mini)/1800(final)`balance.json`,疑为占位),**非 50000**`regen_on_physical_hit` 虚空再生、反射盾、元素抗性**均未实现**。本节 DPS 门槛测算基于 50000 已失真,需统一血量口径后重算。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
若玩家攻速 10 发/秒(纯物理构建),Boss 每秒净回血 2,500 HP。
* **DPS 突破阈值(最低要求)**:物理构建需满足以下条件才能有效磨血:
$$DPS_{physical} > \frac{250 \times hit\_rate}{1} \Rightarrow DPS > 2500 \text{10发/秒时)}$$
+4
View File
@@ -7,6 +7,8 @@
---
> **⚠️ 实现现状 (2026-07-20 审计)**:本文档描述的整套 FTUE 架构——`TutorialManager` Autoload、9 步状态机、气泡提示 `tutorial_hint_bubble.tscn``hint_*.tres`、情境提示、渐进解锁(局 1-5)、靶场慢放——**均未实现**。现状"新手引导" = 仅**首次启动的难度三选一弹窗**(`main_menu.gd` 的 FTUE 面板 + `SettingsManager.ftue_done`)。下文保留作目标设计。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
## 一、设计目标
| 目标 | 度量标准 |
@@ -218,6 +220,8 @@ T=Wave 4+ 触发共鸣时 → 最终提示显示
| 18 | `TUTORIAL_STEP_COMPLETED` | `step: int` | TutorialManager | UIManager(显示解锁提示)|
| 19 | `SHOP_OPENED` | — | ShopManager | TutorialManager |
| 20 | `RESONANCE_TRIGGERED` | `recipe_id: String` | SpellEvaluator | TutorialManager, VFXManager |
> **⚠️ 实现现状 (2026-07-20 审计)**:上表 EventID 与实际 `event_ids.gd` **不符**——`TUTORIAL_STEP_COMPLETED``RESONANCE_TRIGGERED` 常量**根本不存在**ID 18/19/20/21 实为 `ACHIEVEMENT_UNLOCKED`/`BOSS_SPAWNED`/`GAME_STATE_CHANGED`/`SPELL_DROP_PICKUP``SHOP_OPENED` 实为 17。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
| 21 | `ACHIEVEMENT_UNLOCKED` | `achievement_id: String` | AchievementManager | Steam 插件层 |
> **注意**ID 0 为 `CRASH_DETECTED`CrashReporter 专用,已定义)。
+13 -43
View File
@@ -2,60 +2,30 @@
> 策划和程序共同维护的内容文件。所有数值修改无需改架构代码,只改指定位置。
> **策划/美术优先用可视化工具**[07 游戏设计器](07_game_designer.md) 提供编辑器内一站式面板(法术/敌人/波次/Core/平衡),表单填数值即可,**无需写代码**。本文的代码方式适合需要复杂自定义逻辑的程序员,或了解底层数据结构
> **⚠️ 实现现状 (2026-07-20 审计)**:本文多个「改硬编码」的步骤已**过期作废**——项目内容早已全面 JSON 数据驱动,法术/波次/敌人/共鸣/Core 均从 `data/*.json` 加载,相关硬编码工厂函数与常量表已从代码中删除。**当前唯一权威的内容创作工作流是 [07 游戏设计器](07_game_designer.md)**编辑器内插件面板,全部经 `data/*.json` + 编辑器,零代码)。本文中标注 ❌**OBSOLETE** 的小节仅保留作历史参考,请勿据其修改代码。详见审计报告 ../../docs_dev/doc_code_audit_2026-07-20.md
> ⭐ **策划/美术优先用可视化工具**[07 游戏设计器](07_game_designer.md) 提供编辑器内一站式面板(法术/敌人/波次/Core/平衡),表单填数值即可,**无需写代码**。本文的代码方式已大部分作废(见上方现状说明)。
---
## A. 添加/修改法术
> 💡 **策划优先用可视化工具**[06 法术编辑器](06_spell_editor.md) 提供编辑器内表单,无需写代码即可新增/编辑法术(保存到 `data/spells.json`)。下面的代码方式适合需要复杂逻辑的程序员
> **⚠️ 实现现状 (2026-07-20 审计)**:法术定义唯一来源是 `res://data/spells.json`,由 `SpellRegistry` 启动时加载。`wand_preset.gd` **已不再定义任何法术**(文件头注:「法术已迁至 SpellRegistry」),下文 `make_frost_bolt` / `make_spell_by_id` / `get_all_spell_ids` 等工厂函数**在代码中已不存在**,请勿添加。详见审计报告 ../../docs_dev/doc_code_audit_2026-07-20.md
> 💡 **策划优先用可视化工具**:在 [07 游戏设计器](07_game_designer.md) 的「法术」页签(或独立的 [06 法术编辑器](06_spell_editor.md))用表单新增/编辑法术,保存到 `data/spells.json`**无需写代码**。
### 法术定义位置
- **数据驱动(推荐)**`res://data/spells.json`(由法术编辑器维护,启动时由 `SpellRegistry` 加载,优先级最高
- **硬编码(程序)**`scripts/autoloads/wand_preset.gd`(向后兼容回退)
- **唯一来源**`res://data/spells.json`(由游戏设计器/法术编辑器维护,启动时由 `SpellRegistry` 加载)
- 手工编辑时,每条法术是一个对象,键与旧工厂函数字段对应:`id` / `type` / `display_name`tr() 键)/ `description` / `element_tags`(如 `["tag:ice"]`/ `meta``base_damage` / `speed` / `lifetime` / `radius` / `damage_type` / `apply_status_id` / `shop_cost` 等)。推荐用编辑器面板生成,避免手写 JSON 出错。
### 步骤:添加一个新 ACTION 法术
### 步骤:添加一个新 ACTION 法术(当前正确流程)
**1. `wand_preset.gd` 添加工厂函数**
1. 打开游戏设计器「法术」页签,新增一条法术,填写上述字段(`data/spells.json` 会自动写入)。
2. 在 `translations/*.po` 四个文件中添加对应翻译键(见本地化指南)。
3. 无需改动任何 `.gd` 代码;`SpellRegistry` 启动即加载,商店会自动抽取新法术。
```gdscript
func make_frost_bolt() -> SpellNode:
var n := SpellNode.new()
n.id = "action_frost_bolt" # 全局唯一 ID
n.type = SpellNode.SpellType.ACTION
n.display_name = "SPELL_FROST_BOLT_NAME" # tr() 键(见本地化指南)
n.description = "SPELL_FROST_BOLT_DESC"
n.element_tags = ["tag:ice"] # 共鸣系统用的元素标签
n.meta = {
"base_damage": 5.0, # 基础伤害
"speed": 380.0, # 弹速 (px/s)
"lifetime": 4.0, # 存活时间 (s)
"radius": 7.0, # 弹体半径 (px)
"damage_type": 0, # 0=通用 (DamageType.NORMAL)
"apply_status_id": StatusID.FREEZE, # 命中施加状态(可选)
"shop_cost": 22, # 商店价格
}
return n
```
**2. 注册到 `make_spell_by_id``get_all_spell_ids`**
```gdscript
func make_spell_by_id(spell_id: String) -> SpellNode:
match spell_id:
# ... 已有法术 ...
"action_frost_bolt": return make_frost_bolt() # 新增
_: push_warning(...)
func get_all_spell_ids() -> Array:
return [
# ... 已有 ...
"action_frost_bolt", # 新增到列表末尾(商店会自动抽取)
]
```
**3. 在 `translations/*.po` 四个文件中添加翻译键**(见本地化指南)
> ❌ **OBSOLETE(历史参考,代码已删除)**:早期版本需在 `wand_preset.gd` 添加 `make_frost_bolt()` 工厂函数并注册到 `make_spell_by_id` / `get_all_spell_ids`。这些函数已随 SpellRegistry 迁移删除,请勿参照。
---
+2 -2
View File
@@ -1,6 +1,6 @@
# 05 发布标准清单 (Release Guide)
> 正式发布前所有 **P0 必须项** 须全部打勾。参考:`docs/plan/certification_checklist.md`(权威完整版)。
> 正式发布前所有 **P0 必须项** 须全部打勾。参考:`docs_dev/certification_checklist.md`(权威完整版)。
---
@@ -109,7 +109,7 @@
| ST-40/41 | HMAC 签名排行榜 | `endless_records.gd` ✅ |
| ST-50 | Steam Cloud 存档 | 待实现(profile_manager.gd 已有位置) |
完整清单见 `docs/plan/certification_checklist.md`
完整清单见 `docs_dev/certification_checklist.md`
---
+6 -6
View File
@@ -34,12 +34,12 @@ daihaoRogue/
├── translations/ ← zh_CN / zh_TW / en / ja .po
├── audio/sfx/ ← 音效放这里(目前占位蜂鸣)
├── assets/sprites/ ← 精灵图放这里(目前几何占位)
── docs/
├── design/ ← 游戏设计文档
├── mechanics/ ← 机制深度文档
├── technical/ ← 架构 / 实现方案
── plan/ 开发计划 / 认证清单
└── handbook/本手册(你在这里)
── docs/
├── design/ ← 游戏设计文档
├── mechanics/ ← 机制深度文档
├── technical/ ← 架构 / 实现方案
── handbook/本手册(你在这里)
└── docs_dev/ 开发计划 / 认证清单 / 归档草案
```
---
@@ -1,5 +1,7 @@
# 进阶机制扩展:召唤与环境蔓延 (Advanced Mechanics: Summons & Propagation)
> **⚠️ 实现现状 (2026-07-20 审计)**:召唤体系**只实现了 Stationary 炮台一种**;随从/卫星/镜像(含 hp / 碰撞体积 / 移动 / `behavior_state`)未实现,施法者死亡联动也无。`MinionManager``Array[Dictionary]`(§5.12,非 SoA),`MAX_MINIONS=20` FIFO 已实现。环境传导 / 反应地表 / 元素蔓延**未实现**。StatusID 常量 FREEZE/WET/OILY/STUN/VULNERABILITY 存在,但 `status_effects.json` 只定义 Burn/Poison/Combo。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
基于现有的 ECS 战斗系统,我们可以进一步拓展 **“召唤体系 (Summoning System)”** 和 **“环境传导 (Environmental Propagation)”** 维度。
## 1. 召唤体系 (Summoning Dimension)
+2
View File
@@ -1,5 +1,7 @@
# 深度战斗机制扩展设计文档 (Advanced Combat Mechanics Design)
> **⚠️ 实现现状 (2026-07-20 审计)**:本文档的多个进阶维度**未实现**:① §2 连锁/弹跳(`visited_targets` 排除表、`pow(0.9, bounce)` 衰减)——`bounce_remaining` 字段从不被消费;② §3.0 StatusType 现从 `data/status_effects.json`JSON)加载而非 `res://resources/status_types/*.tres`,且 `can_catalyze`/`immune_faction_tags` 不解析;③ §3.3 催化/覆盖/免疫状态交互未实现;④ §4 伤害公式的 `(1-Res)` 抗性项虽存在于 `calc_damage`,但实战路径恒传 `resistance=0`(死维度)。已实现:DoT/伤害基础管线、DamageContext(含 `mult`,伤害计算在 EnemyManager/SRP)。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
为了支持 Roguelike 游戏中涌现式的玩法组合(如:闪电链、叠毒流、触发流),现有的简单"伤害+阵营"模型需要升级为多维度的**上下文感知 (Context-Aware)** 系统。
本文档详细定义了扩展战斗深度所需的关键维度和判断逻辑。
@@ -1,5 +1,7 @@
# 连续打击与伤害叠加设计 (Consecutive Hits & Damage Stacking)
> **⚠️ 实现现状 (2026-07-20 审计)**`VULNERABILITY`(易伤,+5%/层)状态**未实现**——无 JSON 条目、`apply_damage` 不读取。现状代码改用 **`COMBO_MARK` 每层 +2%**`enemy_manager.gd`)。文中标注为 P2 长期方案的 `is_combo_tracker` **已提前实现**并正确守卫 DoT tickCOMBO_MARK 不误触发 DoT)。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
## 需求分析
用户希望实现“对同一个目标,同一种伤害在一定间隔内认定为持续多次伤害,从而可以制作伤害叠加”。
@@ -1,5 +1,7 @@
# 武器系统扩展设计方案 (Weapon System Expansion Design)
> **⚠️ 实现现状 (2026-07-20 审计)**:本文档为**扩展蓝图,大部分未实现**。§1 轨迹修正器(正弦 / 回旋镖 / 环绕,含 `is_orbiting`/`wave_frequency` 等字段)**完全未落地**;文中引用的 `ProjectileDef` 字段属**死类**(代码 `spawn_bullet` 走扁平参数 + cold_data,从不实例化 ProjectileDef)。已实现的触发器/子母弹见 `spell_evaluator.gd`。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
本文档旨在描述基于现有的 **HMWS (Hackable Modular Weapon System)****ECS BulletManager** 架构,可以进一步扩展的玩法机制。
当前的系统已经支持了基础的投射物、霰弹、激光,以及修正器(多重施法、弹射、追踪、穿透、连锁、冰冻)。在此基础上,我们可以引入更高级的策略组合和“涌现式”玩法。
-184
View File
@@ -1,184 +0,0 @@
# 平台发行认证清单 (Platform Certification Checklist)
> **用途**:列出 Steam / Nintendo Switch 两个目标平台的强制通过项,并映射到对应开发切片。
> 每项完成后在 `[ ]` 处打勾,并记录验证者与日期。
> **规则**:所有 P0 项必须在 S6 发布前全部完成,P1 项视平台要求决定是否阻断发布。
---
## 一、Steam (PC) 必须项
### 1.1 隐私与数据合规(EU GDPR / Steam 要求)
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-01 | 游戏不收集任何可识别玩家身份的数据(无账号系统、无上报遥测) | S2 | ⬜ |
| ST-02 | 崩溃日志(`user://crash_log.txt`)不包含 Steam ID / 机器码等 PII | S2 | ⬜ |
| ST-03 | 设置菜单提供「清除所有本地数据」选项(删除 `user://` 下所有文件) | S6 | ✅ 2026-06-05`SettingsManager.clear_all_local_data`,二次确认;实测删除 save_data/endless_records/run_a·b/runs|
| ST-04 | Steam 商店页包含隐私政策链接(即使只写"本游戏不收集任何数据" | S6 前置 | ⬜ |
| ST-05 | 如启用 opt-in 崩溃上报,玩家首次启动须明确同意 | S6(可选)| ⬜ |
> **ST-03 实现指引**
> `SettingsManager.clear_all_local_data()` 遍历删除:
> `user://crash_log.txt``user://crash_log_prev.txt``user://run_a.json`
> `user://run_b.json``user://endless_records.json``user://save_data.json`
> `user://runs/`(截图目录)。调用后重置 `ProfileManager` 单例状态并返回主菜单。
### 1.2 Steam 成就 (Achievements)
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-10 | Steam API 初始化成功(`Steam.init()` 返回 OK,或使用 GodotSteam 插件) | S6 | ⬜ |
| ST-11 | 所有成就在 Steamworks 后台已定义并与 EventBus 钩子对应 | S6 | ⬜ |
| ST-12 | 离线模式下成就解锁不崩溃(本地缓存后联网再同步) | S6 | ⬜ |
| ST-13 | 成就解锁调用幂等(重复触发不报错、不重复计数) | S6 | ⬜ |
> **EventBus 成就钩子**`EventID.ACHIEVEMENT_UNLOCKED` **固定为 ID `18`**(负载:`{ "achievement_id": String }`),权威定义见 **`technical/implementation_plan.md` §2.1** 事件目录与 `event_ids.gd` 常量。
> `AchievementManager` 订阅此事件,调用 `Steam.set_achievement(achievement_id)` + `Steam.store_stats()`(须幂等,见 ST-13)。
### 1.3 Steam 输入与手柄
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-20 | 手柄(Xbox / PS / Switch Pro)基本功能可用 | S1 | ⬜ |
| ST-21 | 手柄连接/断开时游戏不崩溃,自动切换输入方案 | S4 | ⬜ |
| ST-22 | 按键提示图标根据当前设备自动切换(Xbox A / PS × / 键盘 Space | S6 | ⬜ |
### 1.4 Steam Deck 兼容性
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-30 | 游戏在 Steam Deck 默认分辨率(1280×800)下可正常显示 | S6 | ⬜ |
| ST-31 | 所有 UI 文字在 Steam Deck 屏幕上可读(最小字号 ≥ 16px)| S6 | ⬜ |
| ST-32 | Steam Deck 触摸屏操作不引起误触崩溃 | S6 | ⬜ |
| ST-33 | Proton 兼容性验证(在 SteamOS 下通过 Proton 层运行测试) | S6 | ⬜ |
### 1.5 排行榜数据完整性
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-40 | 本地 `endless_records.json` 写入时附加 HMAC-SHA256 签名 | S5 | ✅ 2026-06-05`endless_records.gd`,对精确 JSON 字符串签名)|
| ST-41 | 读取时验证签名,签名失败则标记分数为「未验证」并拒绝提交 Steam 排行榜 | S5 | ✅ 2026-06-05(验签失败 `push_warning`+返回空,实测篡改拒绝)|
| ST-42 | Steam 排行榜提交时服务端异常(网络断开)不崩溃,本地缓存待下次提交 | S6 | ⬜ |
> **ST-40 HMAC 实现指引(C-4 修复)**
> ```gdscript
> # endless_records_manager.gd
> # HMAC Key 存于 GDScript 编译常量(不含在存档内),对抗普通玩家手动编辑
> # 注意:本地 HMAC 无法对抗逆向工程,主要目的是防止随意手改
> const _HMAC_KEY: PackedByteArray = [0xA3, 0x7F, 0x2C, ...] # S5 前生成 32 字节随机密钥
>
> func _sign(data: String) -> String:
> var crypto := Crypto.new()
> var hmac := crypto.hmac_digest(HashingContext.HASH_SHA256, _HMAC_KEY,
> data.to_utf8_buffer())
> return hmac.hex_encode()
>
> func save_records(records: Array) -> void:
> var payload := JSON.stringify(records)
> var signed := { "data": records, "sig": _sign(payload) }
> FileAccess.open("user://endless_records.json", FileAccess.WRITE
> ).store_string(JSON.stringify(signed))
>
> func load_records() -> Array:
> if not FileAccess.file_exists("user://endless_records.json"): return []
> var raw := JSON.parse_string(
> FileAccess.open("user://endless_records.json",
> FileAccess.READ).get_as_text())
> if not raw is Dictionary: return []
> var expected := _sign(JSON.stringify(raw.get("data", [])))
> if raw.get("sig", "") != expected:
> push_warning("EndlessRecords: 签名验证失败,分数不可信")
> return [] # 拒绝载入被篡改的数据
> return raw.get("data", [])
> ```
---
### 1.6 Steam 云存档 (Steam Cloud Save)
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-50 | `ProfileManager.save_run()` / `save_settings()` 通过 `Steam.beginFileWriteBatch()` / `Steam.endFileWriteBatch()` 写入,确保 Steam Cloud 同步 | S6 | ⬜ |
| ST-51 | `user://run_a.json``user://run_b.json``user://save_data.json` 均在 Steamworks 后台 Remote Storage 中启用 | S6 前置 | ⬜ |
| ST-52 | 跨设备(PC → Steam Deck)存档同步测试:PC 端存档可在 Steam Deck 读取并继续游戏 | S6 | ⬜ |
| ST-53 | Steam Cloud 配额不超标(Steam 默认 100MB/游戏;所有存档文件估算总量 < 1MB) | S6 | ⬜ |
> **ST-50 实现指引**
> ```gdscript
> # profile_manager.gd
> func save_run(data: Dictionary) -> void:
> if Steam.is_steam_running():
> Steam.beginFileWriteBatch() # GodotSteam: 开始批量写入,触发 Steam Cloud 同步
> var which := get_int("run_write_slot", 0)
> var path := _SLOT_A if which == 0 else _SLOT_B
> var f := FileAccess.open(path, FileAccess.WRITE)
> if f:
> f.store_string(JSON.stringify(data))
> if Steam.is_steam_running():
> Steam.endFileWriteBatch() # 结束批量写入,触发 Steam Cloud 上传
> set_int("run_write_slot", 1 - which)
> ```
> **注意**`beginFileWriteBatch` / `endFileWriteBatch` 仅在 `Steam.is_steam_running()` 时调用;离线模式下正常写本地文件,不崩溃。`Steam.is_steam_running()` 需 GodotSteam 插件支持。
---
## 二、Nintendo Switch(未来扩展)
> 以下为 Nintendo Lotcheck 最常见的阻断项,仅在立项 Switch 移植时启用。
### 2.1 系统级强制项
| 编号 | 检查项 | 优先度 |
| :--- | :--- | :--- |
| SW-01 | 游戏内按钮提示使用 Switch 图标(A/B/X/Y),不得显示 Xbox 图标 | P0 |
| SW-02 | Joy-Con 横持模式不崩溃(若不支持须在商品页说明) | P0 |
| SW-03 | 游戏可从睡眠状态恢复(`NOTIFICATION_APPLICATION_PAUSED` 处理) | P0 |
| SW-04 | 存档容量不超过 Switch 游戏卡限制(存档 < 32MB | P0 |
| SW-05 | 不在联机功能中收集 Nintendo Account 信息 | P0 |
| SW-06 | Logo & RatingCERO / PEGI / ESRB)在正确位置显示 | P0 |
| SW-07 | 游戏内截图功能不包含版权保护内容 | P1 |
### 2.2 性能要求
| 编号 | 检查项 | 优先度 |
| :--- | :--- | :--- |
| SW-10 | 掌机模式(720p)稳定 30fpsSwitch 性能下限) | P0 |
| SW-11 | 内存使用峰值 < 2.5GBSwitch 总 RAM 4GB 共享系统/GPU | P0 |
| SW-12 | 存档读写不阻塞主线程超过 100ms | P0 |
---
## 三、无障碍合规(Accessibility — 跨平台必须项)
> 参见 `architecture_design.md ADR-C1` 获取完整技术实现规范。
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| AC-01 | 元素标签同时用颜色 + 形状图标标识(色盲玩家可区分) | S3 | ⬜ |
| AC-02 | 所有 UI 字体支持缩放(50%~150%,设置项) | S4 | ⬜ |
| AC-03 | 高对比度模式(黑底白字选项)| S6 | ⬜ |
| AC-04 | 所有 VFX 提供「减少闪光」选项(防光敏性癫痫)| S6 | ⬜ |
| AC-05 | 游戏内所有音效有对应视觉提示(不依赖纯音频传达关键信息) | S6 | ⬜ |
---
## 四、评级申报
| 平台 | 机构 | 提交时机 | 状态 |
| :--- | :--- | :--- | :--- |
| Steam | IARC(自评系统,Steam 自动申报) | S6 发布前 | ⬜ |
| Steam(欧洲) | PEGI 自评或正式申请 | S6 发布前 | ⬜ |
| Switch | CERO(日本)/ PEGI / ESRB(需提前约 8 周提交) | 若立项 Switch | ⬜ |
---
## 五、发布前最终清单
| 编号 | 检查项 | 完成标志 |
| :--- | :--- | :--- |
| PRE-01 | Steam 商店页(截图 × 5、宣传片、描述文字)审核通过 | Steam 后台审核 OK |
| PRE-02 | 版本号 `1.0.0` 打 git tag,构建哈希记录在 `CHANGELOG.md` | git tag v1.0.0 |
| PRE-03 | 所有 P0 认证项状态为 ✅ | 本文档所有 P0 行 ✅ |
| PRE-04 | 发布分支通过完整 Wave 1~20 通关测试(无崩溃) | CI 通关测试日志 |
| PRE-05 | `crash_reporter.gd` 已注册为首位 Autoload,日志路径可写 | project.godot 验证 |
-592
View File
@@ -1,592 +0,0 @@
# 魔法工匠:骨架垂直切片开发计划
# 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-01C# 压力)按 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_circleS0 建立)
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 与 5002000 弹幕阈值、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 后必 releasepool 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_CLOSEADR-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 XPLevel 5→6 需 54 XPP6-N16 公式)— *实测 Lv5→6 需 38 XP(公式 `roundi(10×1.4^(lv-1))`*
- [x] **P-S2-04**:商店第 1 次刷新 20G,第 2 次 30GWave 结算后重置(P6-N23
- [x] **P-S2-05**:3 波循环完整无崩溃;SpellDeck JSON 存档后重载数据一致 — *`ProfileManager` A/B 双槽 `run_a.json`/`run_b.json`*
- [x] **P-S2-06ADR-A2**:模拟写入 `run_a.json` 过程中杀进程,重进游戏应能从未损坏槽恢复上一完整波次/商店状态;`schema_version` 变更时旧档经 `_migrate_run` 可读 — *双槽交替写入已验证*
- [x] **P-S2-07ADR-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` Dictionaryswap-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_damageP6-N34
### 出口检查
> ✅ **已通过**2026-06):子母弹链与 multicast 实测通过;`lock_for_battle()` 防竞争。
> **背包 UI2026-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-N14can_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 dispatch4 指令 `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`AutoloadSoA stride=8MAX_ZONES=64`spawn_zone`if 守卫 + 内层 `while + -=` 速率限制 P6-N71swap-and-pop 移除 + VFX);`minion_manager.gd`AutoloadMAX_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-01C# 压力)按 R-08 顺延。**
> - ✅ **④ 共鸣系统 + MATRIX(`_flatten_matrix`)**`_flatten_matrix`(仅 Row A 执行,Row B 注入隐式邻接 MODIFIERP6-N20/P6-N13);`_check_resonance`adjacent 配方扫描,index 差≤2 跳 MODIFIER,注入结果 + `_consumed` 掩码 P6-N29);`SpellNode.element_tags``SpellDeck` 消费掩码(PackedByteArraypop/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` 仅处理 LINEARCIRCUIT 主链/分支内 TRIGGER 顺延;`execute_sub` 暂无 LOGIC 分支;LOOP 体内嵌套 LOGIC 顺延;共鸣 `anywhere_in_deck` 留 stubMATRIX+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 SoASTRIDE=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 # AutoloadPackedFloat32Array 持有、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 每秒对范围内敌人施加 POISONtick_accum 余量不丢失(P6-N71)— *实测通过 2026-06-05:区域 1.0s tick 对范围内实体施加 POISONfalse→true);速率限制(0.5s 不触发、累计 1.0s 触发);2.5s 大帧后 tick_accum 余量精确 =0.5duration 耗尽 swap-and-pop 移除*
- [x] **P-S5-05**:召唤炮台后 UI 召唤物计数 +1;炮台消亡后计数 -1MINION_SPAWNED/EXPIRED 事件)— *实测通过 2026-06-05`action_summon_turret` 经法术 VM 召唤 → 计数 0→1 且 MINION_SPAWNED.current_count=1;炮台对范围内最近敌人开火(无敌则不开火);recall → 1→0 且 MINION_EXPIRED.current_count=0MAX_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-053 层嵌套分叉(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` 帧时间 < 1msProfiler 实测);若超过,启用 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-0520 精英 `_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.0496msADR-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 + 背景 Polygon2Dz=-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 W8450HP)、Final Boss W201800HP+ 阶段系统(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 子弹稳定 60fpsProfiler 实测)— *实测通过 2026-06-05RTX 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 条全 PASSENEMY_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-05W20 满载:500敌+2000弹+500状态+32 Zone):`MEMORY_STATIC`=105.9MB + 引擎基础≈70MB → **估算 RSS≈175.9MB34%,目标<512MB ✓)**;热数据(全 SoA+MultiMesh)仅 0.3MBVRAM=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` **121**`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` | §关键规范速查 |
+26 -1
View File
@@ -53,6 +53,8 @@ graph TD
end
```
> **⚠️ 实现现状 (2026-07-20 审计)**:数据层实际为**纯 `data/*.json`**7 个文件:spells / cores / enemies / waves / resonance / status_effects / balance),各 Manager 自行 `FileAccess` 直读;全项目**零 `.tres` 数据资源**(仅 `assets/ui/ui_theme.tres`),无 `resources/` 目录。图中 `ConfigMgr` autoload 虽已注册(`project.godot`),但**从未被任何调用方引用,是死代码**。文中后续所有 `res://resources/**/*.tres` 路径均为设计蓝图,与实际 `data/*.json` 布局不符。详见 [`docs_dev/doc_code_audit_2026-07-20.md`](../../docs_dev/doc_code_audit_2026-07-20.md)。
---
## 3. 核心子系统:超模块化法术系统 (HMWS)
@@ -263,6 +265,11 @@ func reset() -> void:
`SpellEvaluator` 的 while 循环只能处理线性指令流。为此,在 `SpellEvaluator.compile_wand(core, raw_deck)` 预编译阶段引入 **拓扑扁平化 (Topology Flattening)**,将不同 Core 的空间拓扑提前转换为线性指令序列,运行时 while 循环无需感知拓扑:
> **⚠️ 实现现状 (2026-07-20 审计)**:真实签名与本节伪代码不同:
> - `compile_wand(core, raw_nodes: Array)` —— 第 2 参是 `Array` 而非 `SpellDeck``spell_evaluator.gd:36`)。
> - `execute_compiled(compiled, caster_id: int, spawn_pos: Vector2)` —— 执行期不传 `ctx`/`core``feature_tags``CompiledDeck` 编译期快照读取(`spell_evaluator.gd:321`)。
> 详见 [`docs_dev/doc_code_audit_2026-07-20.md`](../../docs_dev/doc_code_audit_2026-07-20.md)。
| Core 类型 | 扁平化策略 |
| :--- | :--- |
| **LINEAR** | 无需处理,直接输出原始 SpellNode 序列 |
@@ -279,6 +286,9 @@ func update_max_ops(cpu_limit: int) -> void:
# MAX_OPS_PER_CPU = 8(基准值,SpellEvaluator 常量)
const MAX_OPS_PER_CPU: int = 8
_max_ops = clamp(MAX_OPS_PER_CPU * cpu_limit, 8, 200)
# ⚠️ 实现现状(2026-07-20)SpellEvaluator 从不调用本函数、从不读 cpu_limit;
# 实际硬编码 max_ops = MAX_OPS_PER_CPU * 5(代码常量为 40,本处写 8,文档内部亦不一致)。
# 结果:升级 CPU 词条对施法预算无效——本设计更优,代码待修。见审计报告 D 节。
func compile_wand(core: CoreDefinition, raw_deck: SpellDeck) -> CompiledDeck:
match core.topology:
@@ -719,10 +729,14 @@ func _node_has_tag(node: SpellNode, tag_pattern: String) -> bool:
* 下穿回滞(`_active_count < 1500 and _collision_mode == 1`):反向切回,避免阈值附近频繁震荡。**⚠️ 反向切换 Jitter 风险**:从 SpatialGrid 切回 Area2D 时,约 1500 颗子弹需要重新启用 Node(`process_mode = INHERIT`)。若全部在单帧完成,将产生约 1500 次节点状态更改(额外 1~2ms,可见微卡顿)。**缓解策略**:反向切换同样须分帧渐进(`BATCH_ENABLE_PER_FRAME = 100`,约 15 帧完成),过渡期内存活子弹继续走 SpatialGrid 路径,碰撞正确性不受影响。
* 两段路径(信号回调 vs 手动查询)最终均调用同一 `_on_bullet_hit(bullet_id, enemy_id)` 函数,后处理逻辑完全共用。
> **⚠️ 实现现状 (2026-07-20 审计)**:上述 Area2D 优先 + `_collision_mode` 三档运行时切换**从未实现**。现状是 **SpatialGrid-only**`BulletManager._check_collision` 无条件走 `SpatialGrid.query_circle`,代码中不存在 Area2D / CircleShape2D / `body_entered` / `_collision_mode` / `BATCH_DISABLE_PER_FRAME`。这一简化与 `implementation_plan.md` §S0 的 **R-04 决议**(永久锁定 SpatialGrid 为 200+ 主力、不保留 Area2D 回退)一致——本节 §4.3/§4.4 属尚未同步的旧设计。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
### 4.4 渲染优化
子弹渲染策略依据同屏数量分三档,**Area2D 与 MultiMeshInstance2D 不共存**——MultiMesh 绕过节点树,意味着子弹无法挂载 Area2D。因此两者适用于不同的弹幕规模区间:
> **⚠️ 实现现状 (2026-07-20 审计)**:下表三档**未实现**。现状:`MultiMeshInstance2D` **从第 1 帧起单档常开**(无 Node2D 池、无 <200/200-2000 档),子弹与敌人均走 MultiMesh。同步是 **GDScript 逐实例 `set_instance_transform_2d` 循环**(非文档的 C# `SetBuffer` 批量上传);子弹无按型颜色(统一 `modulate`),敌人有 per-instance color。既然碰撞已 SpatialGrid-onlyNode 池档位已无意义,此简化为**代码更优**。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
| 同屏弹幕数 | 渲染方案 | 碰撞方案 | 说明 |
| :--- | :--- | :--- | :--- |
| < 200 | Node Pool (Node2D) | Area2D | 全功能,支持粒子特效、信号回调 |
@@ -771,6 +785,8 @@ func _node_has_tag(node: SpellNode, tag_pattern: String) -> bool:
> **目标**60fps → 单帧总预算 **16.67ms**。以下为 S0 性能验收的基准分配;S0 Profiler 实测后需将"S0 实测值"列回填并提交 git,作为后续切片的性能基线快照。
> **⚠️ 实现现状 (2026-07-20 审计)**:下表"语言"列标注 C#`BulletManagerCs`/`EnemyManagerCs`/`SpatialGridCs`/`SpellEvaluatorCs`)的热路径**均未接线**——三个 C# 内核从未实例化、从未进场景树,战斗逻辑 **100% 由 GDScript 回退路径承载**R-08 顺延)。C# `AsSpan()` 零拷贝在代码中不存在。表中"S0 实测值"确为 GDScript 回退实测(见 :795 摘要)。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
| 系统 | 语言 | 预算上限 | S0 实测值 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| `BulletManagerCs._PhysicsProcess` | C# | 2.0ms | **0.37ms** | 2000 子弹 GDScript SoA 积分(P-S0-01C# 热路径 S1 填充后预计更低) |
@@ -823,6 +839,8 @@ func _node_has_tag(node: SpellNode, tag_pattern: String) -> bool:
### 5.1 GameCycleManager 状态机 (FSM)
> **⚠️ 实现现状 (2026-07-20 审计)**`GameCycleManager` Autoload **不存在**。顶层状态机现内嵌在场景子节点 `combat_manager.gd`,仅 **5 态**INIT/BATTLE/SETTLEMENT/SHOP/GAME_OVER),缺文档的 WAVE_INTRO 开场、WAVE_RESULT 升级卡结算、GAME_CLEARED 通关、PAUSE 独立态(暂停改由 `get_tree().paused` 处理)。`GAME_STATE_CHANGED` payload 用字符串态名而非枚举。同理 §5.10 的 `UIManager` Autoload 也不存在——全部 UI 在 `combat_s2.gd` 程序化构建,无 `HUD.tscn`/`shop.tscn` 等场景,`DamageNumber` 飘字未实现。下文 GameCycleManager/UIManager 相关设计保留作目标蓝图。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
`GameCycleManager` 是游戏最顶层的协调者,持有全局状态机并驱动各 Manager 的生命周期。
```gdscript
@@ -2815,6 +2833,8 @@ DoT(持续伤害)、召唤物(Minion)、持续性法术的属性在**施
> **结论:采用独立的 MinionManager(方案 B),不在 EnemyManager 中混管召唤物。**
> **⚠️ 实现现状 (2026-07-20 审计)**MinionManager 存在,但数据结构用的是 **`Array[Dictionary]`(随 §5.12)而非本节的 SoA `stride=8`**(两处文档矛盾,代码选了 Array);`behavior_state` 枚举未实现。召唤物**只实现了 Stationary 炮台一种**,随从/卫星/镜像(含 hp/碰撞体积/移动)均未落地。`MAX_MINIONS=20` FIFO 顶替已实现。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
`MinionManager` 为独立的 Autoload 单例,管理玩家召唤的炮台/傀儡/宠物类实体:
- **与 EnemyManager 隔离**EnemyManager 的 SoA 中 `faction_and_type` 字段不扩展到友军,
@@ -2894,7 +2914,8 @@ func _physics_process(delta: float) -> void:
while _zone_data[base + 6] >= _zone_data[base + 5]:
_zone_data[base + 6] -= _zone_data[base + 5] # -= tick_interval 保留余量精度
for t_id in targets:
StatusManager.apply(t_id, sid, 1.0, int(_zone_data[base + 7])) # intensity=1.0, owner_id=zone_owner
# ⚠️ 修正(2026-07-20):真实签名 apply(entity_id, status_type_id, stacks:int=1, duration:float=-1.0, owner_id:int=-1)——5 参
StatusManager.apply(t_id, sid, 1, -1.0, int(_zone_data[base + 7])) # stacks=1, duration=default, owner_id=zone_owner(现实 zone_manager.gd:60 即如此调用)
if _zone_data[base + 4] <= 0.0: # duration 耗尽,移除此 Zone
var cx_exp: float = _zone_data[base + 0]; var cy_exp: float = _zone_data[base + 1]
@@ -2923,6 +2944,8 @@ func reset() -> void:
> **结论**:所有 Feature Tag 字符串必须通过 `CoreFeatureTag` 常量访问,禁止在业务代码中写裸字符串。
> **⚠️ 实现现状 (2026-07-20 审计)**:代码**未按本节的 `String` 常量 + `in` 判定实现**。`core_feature_tag.gd` 实为 **`int` 常量**PERSISTENT_MEMORY=1, DUAL_STREAM=2, ALWAYS_CAST_LAST=3, SHUFFLE_DECK=4, INFINITE_SPELLS=5),`core.feature_tags``int` 位掩码,用 `&` 判定。**⚠️ 潜在 bug**:这些值非 2 的幂,`&` 会串扰(如 `ALWAYS_CAST_LAST(3) & PERSISTENT_MEMORY(1) = 1` 误判持久内存;当前仅 `wand_memory=1` 未触发)。修复建议:改真·2 的幂位标志(1,2,4,8,16),或回退到本节的 String + `in`(后者天然无冲突)。详见 [审计报告 D 节](../../docs_dev/doc_code_audit_2026-07-20.md)。
```gdscript
# core_feature_tag.gd (Autoload: CoreFeatureTag)
## Core 特性标签常量表 — 所有系统通过此处引用,禁止裸字符串
@@ -2974,6 +2997,8 @@ const ALWAYS_CAST_LAST: String = "always_cast_last"
> **结论:S2 实现波次边界自动存档,采用 A/B 双槽写入防崩溃损坏,支持断点续玩。**
> **⚠️ 实现现状 (2026-07-20 审计)**A/B 双槽 + 波次自动存档 + `WM_CLOSE` 兜底 + 链式迁移已实现。差异:① 选槽逻辑现按 `saved_at` **时间戳选最新未损坏槽**(比下文伪代码"固定顺序取第一个有效"更健壮,**代码更优**);② `SCHEMA_VERSION` 已到 **2**(新增 `wand` 键),本节仅记 v1;③ 实存字段为 `wave_num/shop_seed/player_stats/wand`**缺** `elapsed_sec`(影响无尽续玩计时)、`active_core_idx``unlocked_upgrades`、多 Core 数组(待补)。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
商业 RogueliteHades / Slay the Spire / Balatro)均支持中途退出后恢复到当前波次起始状态。
**存档时机**
+9 -3
View File
@@ -5,6 +5,10 @@
---
> **⚠️ 实现现状 (2026-07-20 审计)**:本文档描述的整套 Boss 架构(`BossManager` Autoload、`BossDef`/`BossPhaseDef`/`.tres` 资源、`BossAttackRegistry` 攻击模式注册表、场地机制如冰墙/场地收缩、多阶段弹幕、状态机 `BossInstance`)**均为设计蓝图,代码 0% 实现**。现状 Boss 只是 `EnemyManager` SoA 中 `type=4`Mini BossWave 8/`type=5`Final BossWave 20)的普通厚血实体;唯一的"阶段行为"是当血量跨越 66% / 33% 阈值时召唤一批 `FAST` 杂兵援军(`wave_manager.gd:94-117`)。以下 `BossManager`/`.tres`/攻击模式注册表/场地机制全部尚未落地,保留作为未来实现的目标设计。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
---
## 1. 设计原则
- **阶段驱动(Phase-Driven**:Boss 行为由明确的阶段(Phase)枚举驱动,阶段切换在 `_physics_process` 中以血量阈值触发,不使用独立 Timer Node。
@@ -141,6 +145,8 @@ func _tick_attack(delta: float) -> void:
## 5. 具体 Boss 设计模板
> **⚠️ 实现现状 (2026-07-20 审计)**:下表 HP 与阶段阈值均为**设计目标**,与代码现值不符。① **血量**:代码实取 `balance.json``boss_hp`Mini=**450** / Final=**1800**`balance.json:8-11`,疑为占位/待平衡值),并非本节的 2000 / 10000,也非 `numerical_design §3.2` 的 50000;三处数值互相矛盾。② **阶段阈值**:代码统一用 **66% / 33%** 两段(`wave_manager.gd:99-102`),并非本节 Mini 的 60%/30% 或 Final 的 70%/40%/15%。③ **攻击模式 / 场地机制 / move_speed / enrage_vfx** 字段全部未实现;每次"进阶"只是召唤 `4 + phase*2` 个 FAST 杂兵。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
### 5.1 Mini Boss — Wave 8(待策划填充)
| 字段 | 值 |
@@ -151,7 +157,7 @@ func _tick_attack(delta: float) -> void:
| **Phase 0**100%→60% HP| 攻击模式:`"charge_slam"` + `"spread_shot_3"` |
| **Phase 1**60%→30% HP| 追加:`"summon_minion_x2"`move_speed_mult=1.2 |
| **Phase 2**30%→0% HP| 追加:`"zone_aoe_fire"`move_speed_mult=1.5enrage_vfx="boss_enrage" |
| 死亡事件 | `BOSS_KILLED`EventID 待分配,建议 ID=15|
| 死亡事件 | `BOSS_KILLED`**未实现**:该常量不存在;现状仅在生成时发 `BOSS_SPAWNED=19`,死亡走通用 `ENEMY_KILLED=7`|
| 战场机制 | 无(Mini Boss 不设场地机制,Final Boss 才有)|
### 5.2 Final Boss — Wave 20(待策划填充)
@@ -165,8 +171,8 @@ func _tick_attack(delta: float) -> void:
| **Phase 1**70%→40% HP| 追加:`"zone_ice_wall"`;场地:随机生成 4 个冰墙障碍(`ZoneManager` 驱动)|
| **Phase 2**40%→15% HP| 追加:`"enrage_laser_sweep"`;场地收缩:边界每 10 秒向中心缩小 50px |
| **Phase 3**15%→0% HP| 全弹幕覆盖;`"phase3_barrage"`;恢复部分 HP 后进入死亡序列(演出用)|
| 死亡事件 | `BOSS_KILLED``EventID` **16**+ `GAME_CLEARED`**17**);阶段切换为 **15**(均以 `implementation_plan.md` §2.1 为准|
| 战场机制 | 场地收缩(Phase 2+)、冰墙障碍(Phase 1+|
| 死亡事件 | **未实现**`BOSS_KILLED` / `GAME_CLEARED` / `BOSS_PHASE_CHANGED` 三个常量均不存在(ID 15/16/17 实为 `SPELL_EQUIPPED`/`LEVEL_UP`/`SHOP_OPENED`)。现状:生成发 `BOSS_SPAWNED=19`,死亡走 `ENEMY_KILLED=7`,阶段切换发通用 `GAME_STATE_CHANGED=20`payload `to="BOSS_PHASE_N"`|
| 战场机制 | 场地收缩(Phase 2+)、冰墙障碍(Phase 1+——**均未实现** |
---