From d7420a284fca93464601de47b1807d470f35d8d6 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 31 Jul 2026 16:04:04 +0800 Subject: [PATCH] =?UTF-8?q?docs(roadmap):=20=E6=96=B0=E5=A2=9E=20E7-?= =?UTF-8?q?=E2=91=A5=20=E8=AE=A1=E7=AE=97=E6=A8=A1=E5=9D=97=E5=8C=96?= =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=88=E7=94=A8=E6=88=B7=202026-07-31=20?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E7=9A=84=E6=9E=B6=E6=9E=84=E6=96=B9=E5=90=91?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原则:凡涉及计算的都由专门功能模块负责,便于测试与配置。 AttributeFormula 是已验证的模板(纯静态零依赖,故不启动游戏就能单元断言)。 盘点出计算散落 8 处、仅 1 处已模块化。最严重的是伤害被劈成两半分居两个文件, 且两处都在扣护甲——目前不重复扣仅因调用点特意传 armor=0,而该约定只存在于 调用点,两个函数各自看都是完整公式。连击 +2%/层 也硬编码在管理器里。 记录立项时必须先决定的设计问题与热路径风险,避免实现期顺手定。 顺序:货架 C(含 PriceFormula)先做,本项独立立项。 Co-Authored-By: Claude Opus 5 --- .../2026-07-23-missing-features-roadmap.md | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs_dev/plans/2026-07-23-missing-features-roadmap.md b/docs_dev/plans/2026-07-23-missing-features-roadmap.md index 8540a97..001b607 100644 --- a/docs_dev/plans/2026-07-23-missing-features-roadmap.md +++ b/docs_dev/plans/2026-07-23-missing-features-roadmap.md @@ -170,7 +170,39 @@ E6-a 玩家无敌帧(i-frames) ← 先行小项:Boss 弹幕公平性前置, **依赖**:无。各项可独立作为"缝隙任务"穿插。 -**切分(独立小项)**:① DamageNumber 飘字(S,高手感回报);② 敌人 LOD 屏内剔除 + 屏外低频分离(S~M);③ 内容名 tr 化 + 裸中文静态检查(S,i18n 收尾);④ UI 关键面板 `.tscn` 化(M,可选);⑤ C# 热路径落地(L,仅当 GDScript 达性能瓶颈时;当前 500 敌 3.6% 预算,非必需)。 +**切分(独立小项)**:① 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/截图实测。