Files
spellforge/docs_dev/plans/2026-07-23-missing-features-roadmap.md
T
joywayer 5889b3c1e5 fix(shop): 可售属性排除未接线项,修正路线图「零代码」表述
get_sellable_attrs() 此前只看 attributes.json 是否有 shop 段,不看 PlayerStats
是否真的实现了该属性。运行时实测:给一个合成属性加 shop 段(不改任何代码)会被列出、
可买、扣钱、购买计数增加,但 get_attr_value 永远读到 0.00——玩家买到的是空气,
且 _at_soft_cap 因为 0.0 < soft 永远不封顶,变相无限花钱买无效果。

PlayerStats 新增 has_attr(attr_id) 谓词(查 _attr_effective,只有 _recompute_attrs
真正算过的属性才算数,不是查 _attr_def 有没有这一节);get_sellable_attrs 用它做
第二道过滤。诊断特意拆进独立函数 _report_unwired_shop_attrs 才调用 push_error+assert——
实测 assert(false) 在本项目运行环境下会让「当前函数」提前返回声明类型的默认值:若诊断写
在收集循环里,一旦命中就会让 get_sellable_attrs 本身连同已收集好的合法属性一起返回空数组,
比原来的「静默卖空气」更糟(整个货架消失);拆成独立函数调用后,中断只发生在诊断函数
自己的调用帧内,调用方(get_sellable_attrs)仍会正常继续并返回过滤后的正确列表。
已注入未接线属性验证:4 个合法属性照常出现,注入项被排除,无一致性问题。

顺带订正路线图 docs_dev/plans/2026-07-23-missing-features-roadmap.md 里「加 shop 段
即可上架、零代码」的表述——该结论只在属性已先接入 PlayerStats 框架(player_stats.gd
的裸字段 + _recompute_attrs 分支 + _attr_effective 条目,以及 attribute_tab.gd 的
ATTR_ORDER)之后才成立,E3-① 延后的 7 个属性都还没有这一步;同时记录商店属性子面板
当前坐标最多容纳 6 行的限制,供后续实现者提前规划。
2026-08-03 12:51:13 +08:00

222 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 缺失功能路线图(Missing-Features Roadmap
> **日期**2026-07-23
> **用途**:盘点 Spellforge 当前所有未实现/占位的功能,分组为史诗(Epic),给出目标·现状·依赖·工作量·优先级·验收标准·切分建议,作为后续逐个 `spec → plan → 实现` 的统筹入口。
> **来源**`docs_dev/doc_code_audit_2026-07-20.md`(对抗性复核过)+ 2026-07-23 代码复核(见各 Epic「现状」引用行号)。已完成并从本表剔除:Mana(`93dffd7`)、元进展(`MetaProgress`)、Boss 阶段+攻击系统(`95e5587`)。
> **⚠️ 复核约定**:审计为 7-20 快照,本文 7-23 复核了关键声明;每个 Epic 真正立项写详细计划前,仍须对其「现状」再复核一次当前代码(行号可能漂移)。
---
## 0. 优先级与推荐顺序
优先级分层(用户 2026-07-23 选定 P0/P1 四项为最先详细规划):
| 层级 | Epic | 一句话理由 |
| :-- | :-- | :-- |
| **P0** | E1 战斗深度 | 元素/轨迹词条现为死代码,落地即让大量已有内容"活过来",build 多样性提升最直接 |
| **P0** | E3 经济与 build | 货架 B/C + 出售 + 属性词条,撑起 roguelite 的核心成长/取舍循环 |
| **P1** | E4 游戏循环/里程碑 | 通关演出 + `GAME_CLEARED` + NG+ + Boss Rush,让游戏有"结局"与重玩驱动 |
| **P1** | E6 Boss 深化 | 承接已完成的 Boss 核心:场地机制、通关演出、玩家无敌帧 |
| P2 | E2 状态/元素反应 | 依赖 E1 元素成熟;催化/反应矩阵是深度上限 |
| P2 | E5 教程/引导 | 独立打磨项,商业化必需但非玩法核心 |
| P3 | E7 渲染/性能/债务 | LOD/C# 热路径/UI .tscn 化/i18n 债务,长尾优化 |
**推荐实现顺序(含依赖)**
```
E6-a 玩家无敌帧(i-frames) ← 先行小项:Boss 弹幕公平性前置,E1 也受益
→ E1 战斗深度(抗性 → 连锁/弹跳 → 归航 → 轨迹修正)
→ E3 经济与 build(属性词条系统 → 货架 C → 货架 B → 出售退款)
→ E4 游戏循环(GAME_CLEARED/通关演出 → 里程碑 → Boss Rush/Endless 词条 → NG+
→ E6-b Boss 深化其余(场地机制:冰墙/收缩 → 通关/死亡演出)
→ E2 状态反应 → E5 教程 → E7 债务
```
工作量记号:**S**≈3–5 任务、**M**≈610 任务、**L**≈1116 任务(Boss 系统为 12 任务=M~L 参照系)。
---
## E1. 战斗深度(元素抗性 / 连锁 / 弹跳 / 归航 / 轨迹修正) · P0 · 工作量 M
**目标**:让子弹的 pierce 以外的深度维度真正生效,使元素、抗性、轨迹类词条与核心特性产生行为差异。
**现状(代码复核 2026-07-23**
- ~~元素抗性~~ ✅ **完成**2026-07-24`feat/element-resistance`):`apply_damage_from_context``ctx.damage_type``_RESIST` 表传入 `calc_damage(0, res)``enemies.json` 每型可选 `resistances{元素名:-0.9~0.9}`(支持负=弱点加伤);设计器敌人页加 6×5 抗性网格。MCP 实测:抗火0.5→半伤、弱冰-0.5→1.5×、抗性+护甲叠加45。⚠️ **已知限制**DOT 伤害(`status_manager.gd:116` 直调 `apply_damage`)暂不享元素抗性(架构上 DOT 不走 context,见 E2)。(~~`player_stats.gd:23 resistance` 玩家侧抗性仍占位~~ —— **2026-07-31 订正**:该字段零消费方且不在权威属性表内,属实现先于设计的残留,已随 E3-① 删除;玩家侧减伤真要做时按货架 C 属性词条立项,见 `docs_dev/specs/2026-07-31-player-attributes-design.md` §2.6。)
- ~~连锁/弹跳~~ ✅ **完成**2026-07-24`feat/bullet-bounce`):镜像 pierce 管线——`CastStats.bounce_add``_apply_modifier` 折叠 → `_push_projectile` 写冷数据 → `_check_collision` 命中转向最近未访问敌(`_find_nearest_unvisited`)、`damage_mult×decay` 递减、`visited_targets` 防回跳、命中循环跳过已访问防密集群重复命中。bounce 优先于 pierce(耗尽后落 pierce)。`modifier_bounce` 词条上架。MCP 实测:bounce=2→3 敌 100/90/81、速度重定向证明优先级、折叠 bounce_add=2。
- ~~归航~~ ✅ **完成**2026-07-30`feat/bullet-homing`):镜像 bounce 管线——`CastStats.homing_add`(float) → `_apply_modifier` 折叠 → `_push_projectile` 写冷数据 `homing_strength/homing_range/homing_target_id``_gd_integrate` 在位置积分**之前**调 `_apply_homing`,用 `wrapf` 取最短转向、`clampf``strength*delta` 限角速度、`rotated()` 保速率。目标为**发射后锁定**:仅首帧与目标死亡时经 `_find_nearest_unvisited` 重选;bounce 命中重定向时改写 `homing_target_id`bounce 选目标、homing 追上去)。`modifier_homing` 词条上架。MCP 实测见下方「⚠️ 有意偏离」与 spec §5。
- ⚠️ **有意偏离本文档原方案**:本行原写「`_gd_integrate` 消费 `_enemy_pos_snapshot`」,实现**没有**消费它,而是**删除**了 `_enemy_pos_snapshot` 及其唯一生产者 `EnemyManager.fill_pos_snapshot`。理由:最终设计采用**按 entity_id 锁定目标**(轨迹可读、目标死亡有明确判据 `get_pos_by_id` 哨兵),而该快照**按槽位存位置、不含 entity_id**,结构上支撑不了锁定语义;接上它反而要每帧 O(敌人数) 重扫最近点。净效果是**减少**一份每帧 O(敌人数) 开销。
- ⚠️ **性能**:重选**失败**路径会每帧重跑 `query_circle(r=400)`,实测 300 弹即越过 2.0 ms 线,故同期落地**退避 + 抖动**(`homing_retry_at``HOMING_RETRY_MS=200`)。抖动必需——朴素固定退避会把同帧失败的子弹锁进同相,尖峰每 12 帧永久复发。详见 spec §5。
- 轨迹修正(加速/曲线):`_data +11 acceleration` 已积分,但无"转向/曲线"类修饰器写入。
**设计来源**`docs/mechanics/combat_mechanics_depth.md §4`(抗性公式)、子弹冷数据字段。
**依赖**:无硬依赖;元素 tag 体系已在(`SpellNode.element_tags`)。敌人需有 per-type/per-status 抗性数据源(新增 `enemies.json` 抗性字段或状态减抗)。
**切分(建议 4 个子计划)**
1. ~~**元素抗性 (1-Res)**~~**完成**2026-07-24`feat/element-resistance`spec/plan `docs_dev/{specs,plans}/2026-07-24-element-resistance*`):enemies.json `resistances{元素名:-0.9~0.9}`EnemyManager `_RESIST` 查表传入 `calc_damage(0,res)`;设计器 6×5 抗性网格。
2. ~~**连锁/弹跳 chain/bounce**~~**完成**2026-07-24`feat/bullet-bounce`spec/plan `docs_dev/{specs,plans}/2026-07-24-bullet-bounce*`):冷数据 `bounce_remaining/visited_targets/decay/range`,命中转向最近未访问敌+×0.9 递减,bounce 优先于 pierce`modifier_bounce` 词条。
3. ~~**归航 homing**~~**完成**2026-07-30`feat/bullet-homing`spec/plan `docs_dev/{specs,plans}/2026-07-30-bullet-homing*`):冷数据 `homing_strength/homing_range/homing_target_id`,每帧以 `strength*delta` 为上限朝**锁定目标**转向(`wrapf` 最短转向 + `rotated()` 保速率),目标死亡才重选;失败退避 + 抖动(`homing_retry_at`);bounce 协同改写目标;`modifier_homing` 词条。**偏离**:未消费 `_enemy_pos_snapshot` 而是删除之(快照按槽位存位置、不含 entity_id,支撑不了锁定语义),见上方「现状」。
4. **轨迹修正**:曲线/环绕类(angular velocity 字段)+ 对应修饰器。S(可选,最低价值,YAGNI 可延后)。
**验收标准**:抗性 50% 的敌人受火伤减半(数值实测);bounce=2 子弹依次命中 3 个不同敌人且伤害递减;homing 子弹命中偏移目标;各修饰器可在商店买到并影响弹道;纯数据驱动、MCP 运行时实测。
---
## E2. 状态 / 元素反应(催化 / 环境反应 / 状态交互矩阵 / VULNERABILITY · P2 · 工作量 M
**目标**:状态之间、元素之间产生化学反应(催化引爆、易伤叠加、地表反应),把 StatusManager 从"独立计时器"升级为"交互矩阵"。
**现状**`status_registry.gd` 不解析 `can_catalyze`(死字段);无 overwrite/immunity 规则、无反应地表;VULNERABILITY 状态(+5% 受伤,`consecutive_hits_stacking.md:80`**未实现**(现只有 COMBO_MARK +2%)。
**设计来源**`docs/mechanics/consecutive_hits_stacking.md``combat_mechanics_depth.md` 状态矩阵。
**依赖**:E1(元素/抗性成熟后反应才有意义)。
**切分**:① VULNERABILITY 状态 + 命中叠层(S);② 状态交互矩阵(overwrite/immunity/refresh 规则表,JSON 驱动,S~M);③ 催化引爆(catalyze:某状态达阈值被特定元素引爆成 AoE,M);④ 反应地表(借 ZoneManager,M,可延后)。
**验收标准**VULNERABILITY 叠层增伤生效;毒+火催化引爆造成范围伤;矩阵规则(如冰覆盖火)按 JSON 生效;MCP 实测。
---
## E3. 经济与 build(货架 B/C + 出售退款 + 属性词条系统) · P0 · 工作量 M~L
**目标**:把商店从"只卖法术"扩成完整 roguelite 经济:核心抽取、属性升级、出售退款,配合玩家属性成长系统。
**现状(代码复核 2026-07-23;属性部分 2026-07-31 订正)**`shop_manager.gd` 无 shelf/sell/core/attr 任何字段(grep 零匹配)→ **仅货架 A(法术抽取)**。无出售退款(G5)。
- ⚠️ **2026-07-31 订正**:本行原写「`player_stats.gd``resistance` 等占位属性(4/5 stats 未接线)」,**与事实不符**。权威属性表(`docs/design/numerical_design.md` §1.1)是 **11 个属性**`hp_max`/`mana_max`/`move_speed`/`cast_delay_mod`/`recharge_speed_mod`/`luck`/`cpu_limit`/`attunement_×4`),`armor`/`resistance` **根本不在表内** —— 它们是实现先于设计的孤儿字段(零消费方),已随子计划 ① 删除。真实缺口是:三条**死数据**(`cpu_limit`/`move_speed`/`cast_delay_mod`,消费方已存在只是没接上,子计划 ① 已修)+ 三类**未立项新机制**(`attunement_×4`/`luck`/`recharge_speed_mod`)。详见 `docs_dev/specs/2026-07-31-player-attributes-design.md` §0/§1。
**设计来源**`docs/design/game_design.md §5`(三货架)、`numerical_design.md`(属性曲线、退款比例)。
**依赖**:货架 C 依赖「属性词条系统」先落地;核心抽取复用已有 `cores.json`/`make_core_by_id`
**切分(建议 4 个子计划,属性词条优先)**
1. ~~**属性词条系统**~~**完成**2026-07-31`feat/player-attributes`spec/plan `docs_dev/{specs,plans}/2026-07-31-player-attributes*`):`AttributeFormula` 纯静态公式模块(`hybrid` 连乘 / `inverse` 反向下限 / `add_int``pct` + `pct` 越界钳 0);`PlayerStats``_attr_def` + `_modifiers` 并把公式结果写回静态类型裸字段(读取端零开销);`add_modifier` / `remove_modifiers_from(source)` 按来源增撤;`data/attributes.json` + 设计器「属性」页。**接线三条死数据**:`spell_evaluator` MAX_OPS 改读生效 `cpu_limit`(法杖份额以 `source="core"` 加成接入,运行时实测 3→120 / 5→200 / 8→320 步)、`player_manager``const MOVE_SPEED` 改读 `PlayerStats.move_speed`(**发版基准仍是 200**;实测手段是临时挂一条 `+50%` 加成使生效值变 300,测得玩家实际位移速度同步由 200 变 300 px/s,证明读取端是活的)、`_handle_auto_cast` 开火时乘 `cast_delay_mod`。删孤儿字段 `armor`/`resistance``hp_max`/`cpu_limit` 移出存档(改为派生值)。
- ⚠️ **实际范围小于原描述**:原写「定义 4/5 缺失 player stats(如暴击/急速/范围/吸血/减伤)」—— 那些属性名是拟稿推测,不存在于任何权威文档(见上方「现状」订正)。本期只做**框架 + 三条死数据**,明确延后三项,各有理由:
- `attunement_fire/ice/lightning/poison`(4 个元素精通)—— 属**新增玩法维度**而非断线,需接入元素伤害管线,独立立项。
- `luck` —— 权威表定义它「影响暴击率」,而**暴击链目前是死的**(`bullet_manager.gd:81``is_crit` 硬编码为 `false``CastStats.crit_chance` 从未被掷判);打通暴击链是其前置。
- `recharge_speed_mod` —— 代码中**不存在充能系统**(全项目 grep `recharge` 零命中),属新增机制而非断线。
- 另:`mana_max` 的「与法杖取 Min」语义(权威 §1.1)未迁入框架,现状是核心值直接覆盖玩家值;那是独立设计判断,记为已知遗留。
-**`cast_delay_mod` 的饱和区问题已处理(`hard: 0.01 → 0.05`,提交 `ba7c885`,决定已关闭)**`_handle_auto_cast``if` 而非 `while`,每物理帧至多一次施法 → 60 次/秒硬顶,实际速率按**整帧量化**,故饱和点 = `(1/60) / 法杖基准间隔``circuit_fork` 0.0278 / `wand_basic` 0.0333 / `wand_fast` 0.0667)。原 `hard: 0.01` 远在所有杖的饱和点之下,从饱和点买到 0.01 射速纹丝不动。因饱和点随杖而异、不存在对所有杖都最优的单一 `hard`,取 0.05 为折中:`wand_basic` / `circuit_fork` 无无效区间,`wand_fast` 仍剩 `[0.05, 0.0667)`(较原来的 6.7 倍缩至 1.33 倍)。完整实测数据集见 `docs_dev/specs/2026-07-31-player-attributes-design.md` §2.2b。若日后要把 `wand_fast` 那段也消掉,正确做法是让 `hard` **逐杖化**(挪进 `cores.json`)而非继续挪这个全局数 —— 属独立立项,**不是本期遗留待办**。
2. ~~**货架 C(属性购买)**~~**完成**2026-07-31`feat/shelf-c-attribute-shop`spec/plan `docs_dev/{specs,plans}/2026-07-31-shelf-c-attribute-shop*`):`PriceFormula` 纯静态定价模块(`geometric`/`linear`/`flat` 三曲线,`class_name` 零依赖,可脱离游戏进程单测,风格对齐 `AttributeFormula`);`ShopManager` 新增货架 C 状态(`_attr_purchases` 唯一写入点 `_apply_attr_purchases()` 重建加成,来源常量 `MOD_SOURCE_SHOP_C="shop_c"`);`attributes.json` 逐属性加 `shop` 段(`mode`/`step`/`price_base`/`price_growth`/`curve`)驱动可售集合与定价,**无 `shop` 段即不可售**(数据驱动,加属性零代码,运行时实测:注入无 `shop` 段的合成属性会被正确排除);`ProfileManager` schema 2→3 新增 `attr_purchases` 持久化,`apply_run()` 顺序为**先 `ShopManager.apply_attr_purchases_save``PlayerStats.load_save_data`**(颠倒会在"已有历史购买"场景下吞血,运行时实测复现:错误顺序丢 30 HP、正确顺序不丢);商店 UI 新增「📊 属性」按钮打开独立子面板(原计划设想内联在商店面板,实测面板仅余 18px 放不下,改独立子面板);设计器「属性」页扩展 `shop` 五字段编辑。
- ⚠️ **实际范围**:仅落地 `attributes.json` 现有的 4 个框架内属性(`hp_max`/`move_speed`/`cast_delay_mod`/`cpu_limit`)。E3-① 延后的 B 类 7 个属性(`attunement_*` ×4、`luck``recharge_speed_mod``mana_max` 的 Min 语义)仍待各自前置(暴击链、充能系统、元素伤害管线等)解除后才有意义可买;**「加 `shop` 段即可上架、零代码」只在该属性已接入 PlayerStats 框架之后才成立**——上述 7 个都还没有,届时仍需先在 `scripts/autoloads/player_stats.gd`(裸字段 + `_recompute_attrs()` 一行 + `_attr_effective` 字面量各加一条)与 `addons/game_designer/attribute_tab.gd``ATTR_ORDER` 常量把它接进框架,之后加 `shop` 段才是零代码(2026-08 最终评审修复:`get_sellable_attrs()` 现同时校验 `shop` 段与 `PlayerStats.has_attr()`,未接线的属性会被排除而非被静默售卖——此前的「已运行时验证」结论范围有误,验证的只是"有 shop 段即出现在列表",没验证"未接线属性会怎样")。另外,商店「属性」子面板当前坐标常量(`scenes/main/combat_s2.gd``_setup_attr_shop_ui`)在关闭按钮之前最多容纳 6 行,7 个属性一次性全部上架会超出,需要先做布局/滚动改造。
-**`cast_delay_mod` 的 soft 上限验证**:三个饱和点(`wand_basic` 0.0333 等)全部低于货架购买软上限 `soft`(0.1),常规购买够不到饱和区。运行时实测买到 `soft` 需 22 次购买,此时帧距仍为 3 帧(未饱和),证明 `soft` 才是玩家实际能碰到的约束。
3. **货架 B(核心抽取)**:商店提供 Core 抽取/更换(复用 `_CORE_ROSTER`),与现有换杖打通。S~M。
4. **出售退款 G5**:背包内法术/核心可出售,按 `numerical` 退款比例返还货币。S。
**验收标准**:三货架并存刷新正确;买属性词条后伤害/射速等实测变化;核心抽取可换 build;出售返还货币且从背包移除;纯数据驱动 + 设计器可调 + MCP 实测。
---
## E4. 游戏循环 / 里程碑(通关演出 / GAME_CLEARED / 里程碑 / Boss Rush / Endless 词条 / NG+ · P1 · 工作量 L
**目标**:给游戏一个"结局"与重玩驱动:W20 通关演出 + `GAME_CLEARED` 事件、里程碑奖励、Boss Rush/Endless 全局词条、NG+Phase4)。
**现状(代码复核 2026-07-23**`scripts/``GAME_CLEARED`/`NG_PLUS`/`boss_rush`/`milestone` 任何引用(grep 零匹配)。游戏**只能靠死亡结束**;Boss 死亡已发 `BOSS_KILLED=30`E4 可挂钩),但无通关流程。
**设计来源**`docs/design/game_design.md §8`
**依赖**Boss 系统(已完成,`BOSS_KILLED` 可作通关触发);存档系统(已在,NG+ 需扩 schema)。
**切分(建议 5 个子计划)**
1. **通关演出 + `GAME_CLEARED` 事件**W20 Boss 死 → `GAME_CLEARED{elapsed, wave}` → 通关结算屏(复用 settlement)。事件常量续 31。S。
2. **里程碑 W5/W10/W15**:达波次触发奖励/事件(宝箱/免费法术/属性)。S~M。
3. **Boss Rush 模式**:连续 Boss 波模式入口(复用 BossManager + waves 子集)。M。
4. **Endless 全局词条**>W20 每 N 波抽全局增益/减益词条(正负权衡)。M。
5. **NG+Phase4**:通关后开启的更高难度循环,敌人/Boss 强化曲线 + 存档标记。M。
**验收标准**W20 击杀 → 通关屏 + `GAME_CLEARED` 发出;里程碑奖励发放;Boss Rush 可进入并结算;Endless 词条按波刷新生效;NG+ 循环难度提升且可存读;MCP 实测。
---
## E5. 教程 / 引导(TutorialManager / 渐进解锁 / 靶场) · P2 · 工作量 M
**目标**:新手引导从"仅难度选择"升级为真正教学:分步教程、内容渐进解锁、练习靶场。
**现状**FTUE 仅难度选择(`main_menu.gd`);无 `TutorialManager` autoload、无渐进解锁、无靶场场景。
**设计来源**`docs/design/tutorial_design.md`
**依赖**:解锁树可复用 `MetaProgress`
**切分**:① TutorialManager(分步提示状态机 + 事件钩子,M);② 渐进解锁(法术/核心按进度解锁,接 MetaProgress,S~M);③ 靶场/练习模式(无伤害假人场景,S)。
**验收标准**:首次进入分步教学可完成;未解锁内容不出现在商店/背包;靶场可自由测试 build;MCP 实测。
---
## E6. Boss 深化(玩家无敌帧 / 场地机制 / 通关·死亡演出) · P1 · 工作量 M
**目标**:承接已完成的 Boss 核心(阶段+7 招式+敌弹),补齐当初范围外的深度项。
**现状**Boss 核心已完成(`95e5587`)。✅ **玩家无敌帧已完成**(分支 `feat/player-iframes`2026-07-24`take_damage` 时间戳门控 0.5s 窗口 + `is_invulnerable()` + 8Hz 闪烁 + 复活 `PLAYER_DAMAGED`/hurt 音效 + 顺带修复 balance_tab `_save` 丢键;时长入 `balance.json` `player_iframe_sec`)。**未实现**:场地机制(冰墙障碍 / 场地收缩,`boss_design §5.2`);Boss 通关/死亡演出序列(`PHASE_DYING` 阶段)。
**设计来源**`docs/technical/boss_design.md §5`
**依赖**ZoneManager(场地机制复用);BossManager(已在)。
**切分(建议 3 个子计划)**
1. ~~**玩家无敌帧 (i-frames)**~~**完成**2026-07-24`feat/player-iframes`):PlayerStats 时间戳无敌窗口 + HUD 闪烁反馈;所有外部伤害(敌弹/Boss 接触/近战)经 `take_damage` 单点尊重 i-frame。spec/plan`docs_dev/{specs,plans}/2026-07-24-player-iframes*`
2. **场地机制**BossManager 招式可生成冰墙障碍(ZoneManager 阻挡区)+ 场地边界周期收缩(bosses.json 阶段字段 `arena_shrink`/`hazards`)。M。
3. **通关/死亡演出**`PHASE_DYING` 阶段 + 死亡序列 VFX + 恢复部分 HP 的终章弹幕(final boss)。S~M,与 E4 通关演出衔接。
**验收标准**:连续受击有无敌窗口、HUD 反馈;冰墙阻挡玩家移动/子弹、场地按时收缩;Boss 死亡播放序列后才移除并触发通关(衔接 E4);MCP 实测。
---
## E7. 渲染 / 性能 / 债务(LOD 剔除 / DamageNumber / C# 热路径 / UI .tscn 化 / i18n 债务) · P3 · 工作量 M~L(可拆分独立小项)
**目标**:长尾优化与技术债清理,不阻塞玩法但影响手感/规模/可维护性。
**现状**`enemy_manager.gd:25 _visible_flags` 声明却从不置 1 → 屏内剔除/低频分离力未实现;无伤害飘字(DamageNumber);C# 热路径(BulletManagerCs/EnemyManagerCs/SpatialGridCs)为死代码(R-08 已承认);UI 全程序化(无 `.tscn`);i18n 债务:法术/核心/状态 `display_name`/`description` 仍硬编码未 tr 化,缺裸中文静态检查。
**设计来源**`architecture_design.md §4.4`、R-08、ADR-A1。
**依赖**:无。各项可独立作为"缝隙任务"穿插。
**切分(独立小项)**:① DamageNumber 飘字(S,高手感回报);② 敌人 LOD 屏内剔除 + 屏外低频分离(S~M);③ 内容名 tr 化 + 裸中文静态检查(S,i18n 收尾);④ UI 关键面板 `.tscn` 化(M,可选);⑤ C# 热路径落地(L,仅当 GDScript 达性能瓶颈时;当前 500 敌 3.6% 预算,非必需);⑥ **计算模块化重构(M~L,2026-07-31 立项确认,用户指定)** —— 见下。
### E7-⑥ 计算模块化重构(用户 2026-07-31 指定的架构方向)
**原则**:凡涉及计算的都由**专门功能模块**负责,便于测试、配置,符合「专门模块负责专门功能」的设计理念。`AttributeFormula`E3-① 建)是已验证的模板:`class_name` 纯静态、零依赖(不碰 autoload/场景树),因此**不启动游戏就能单元断言**,改公式立刻回归。
**现状盘点(代码复核 2026-07-31**:计算散落 8 处,仅 1 处已模块化。
| 计算 | 现在住在哪 | 状态 |
| :-- | :-- | :-- |
| 属性合成 | `AttributeFormula` | ✅ 已模块化(模板) |
| 伤害合成(一半) | `DamageContext.calc_damage``damage_context.gd:26`) | ⚠️ 与上下文数据混在同一个类 |
| 伤害合成(另一半) | `EnemyManager.apply_damage:285-289` | ❌ 内联在管理器 |
| 经验曲线 | `PlayerStats.xp_for_level` | 纯静态但与状态混住 |
| 分数 | `EndlessRecords.compute_score` | 同上 |
| 刷新费用 | `ShopManager.get_reroll_cost` | ❌ 内联 |
| 蓝耗 + heat 乘子 | `SpellEvaluator._charge_mana` | ❌ 内联在法术 VM |
| 波次缩放 | `WaveManager:61,62,76` | ❌ 内联三处 |
**最严重的一处 —— 伤害被劈成两半,分别在两个文件里,且两处都在扣护甲**
```gdscript
# damage_context.gd:26
base_damage × mult × (1 resistance) armor × (1 pierce_rate)
# enemy_manager.gd:285-289 ← 又算了一遍
damage × (1 + combo_stacks × 0.02) armor # 保底 1
```
目前**没有**重复扣,因为 `apply_damage_from_context` 特意传 `calc_damage(0.0, res)` 把 armor 置 0 留给下游 —— 但该约定只存在于调用点,两个函数各自看都是完整公式。想知道「这一击到底怎么算」必须同时读两个文件并知道那条不成文约定。连击的 `+2%/层` 也硬编码在管理器里,不在任何数据文件中。
**立项时必须先决定的设计问题**(都不该在实现期顺手定):伤害那两半如何合并、`calc_damage``armor` 参数语义要不要改、连击 `+2%` 进哪个数据文件、DOT 伤害(`status_manager.gd:116` 直调 `apply_damage`,绕过 context 故不享元素抗性,E1-① 记录的已知限制)是否借此一并归位。
**风险**:触碰全游戏最热路径(500 敌 + 1500 弹),必须有迁移前后的基准对比。
**依赖**:无硬依赖。`PriceFormula`(E3-② 货架 C 建)会成为第二个已模块化的例子,可与 `AttributeFormula` 一起确立命名/签名/测试惯例,再据此重构其余 7 处。
**验收标准**:飘字随命中显示;屏外敌人低频更新且 FPS 提升;4 语言无裸中文;MCP/截图实测。
---
## 附:立项流程
每个子计划遵循已验证的流水线(同 Boss):
1. `superpowers:brainstorming` → 澄清范围/设计决策 → `docs_dev/specs/<date>-<feature>-design.md`(提交)。
2. `superpowers:writing-plans` → 逐任务实现计划 → `docs_dev/plans/<date>-<feature>.md`(提交)。
3. `superpowers:subagent-driven-development` → 子代理逐任务实现 + 两阶段评审 → 验收 → 合并。
4. 全程 Godot MCP 运行时实测;纯数据驱动;改动记 memory。
> 本路线图为"活文档":完成一个 Epic/子项后在此勾除并更新现状引用。