Files
spellforge/docs/mechanics/consecutive_hits_stacking.md
T
joywayerandClaude Opus 4.8 ad2f4c0bc6 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>
2026-07-20 14:35:55 +08:00

95 lines
6.4 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.
# 连续打击与伤害叠加设计 (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)。
## 需求分析
用户希望实现“对同一个目标,同一种伤害在一定间隔内认定为持续多次伤害,从而可以制作伤害叠加”。
这通常被称为 **“连击系统 (Combo System)”** 或 **“弱点叠加 (Vulnerability Stacking)”**。
与 DoT(中毒/燃烧)不同,这种机制侧重于 **“攻击频率”** 和 **“爆发奖励”**。
## 核心概念
### 1. 连击窗口 (Combo Window)
* 每个敌人对每种伤害来源(或类型)维护一个 `LastHitTime``StackCount`
* 如果在 `WindowDuration` (例如 2秒) 内再次受到同类伤害:`StackCount++`, 刷新 `LastHitTime`
* 如果超时:`StackCount` 重置为 0。
### 2. 叠加收益 (Stacking Benefits)
`StackCount` 增加时,可以产生以下效果:
* **Damage Scaling**: 第 N 次伤害提高 `N * 10%`
* **Proc Trigger**: 每第 3 次攻击触发一次爆炸 (3-Hit Passive)。
* **State Transition**: 叠满 5 层后,施加“破甲”或“眩晕”。
## 数据结构设计
在现有的 `EnemyManager` 混合 ECS 中,我们需要拓展 `StatusContext` 或新增 `ComboContext`
由于连击通常与特定的“来源”或“伤害标签”绑定,建议存放在 `StatusContext` 中的专用字段,或者复用 Status 机制。
### 方案 A: 专用 Combo Tracker
`EnemyEntityManager` 的 Context 中增加:
```gdscript
# combo_tracker.gd — 通过 ObjectPool 复用,避免 GC 开销
class_name ComboTracker
extends RefCounted
var source_id: int = -1 # 来源实体 ID(支持多武器/多玩家,-1 表示无效)
var damage_tag: int = 0 # 伤害类型位掩码(PHYSICAL=1, FIRE=2, LASER=4 ...
var count: int = 0 # 当前连击层数
var timer: float = 0.0 # 剩余窗口时间(秒)
func reset() -> void:
source_id = -1
damage_tag = 0
count = 0
timer = 0.0
```
### 方案 B: 利用现有 Status 系统
定义一种特殊的 StatusType `COMBO_MARK`
* 每次攻击施加 `COMBO_MARK`
* `addEffect` 逻辑中,如果已存在,则 `intensity++` (层数) 并刷新 `duration`
* 伤害计算时,检查 `COMBO_MARK``intensity` 来加成伤害。
**✅ 追加选择:推荐方案 B**,因为它复用了现有的 Tick 和合并逻辑,不需要写额外的 Timer。方案 A 适合需要多来源(`source_id`)区分的场景(如多玩家共同攻击同一目标)。
> **R4-C3 补充(COMBO_MARK 的 StatusTypeDef 参数)**`COMBO_MARK` 对应以下 `StatusTypeDef` 配置:
> - `stack_mode = 1`**Intensity 强度叠加**):每次施加 +1 层,`intensity` 字段记录当前层数。
> - `max_stacks = 0`**0 = 无限叠加**,不设层数上限;由高频攻击自然封顶)。
> - `tick_interval`:⚠️ **语义说明(P6-N14 修正)**`StatusTypeDef.tick_interval` 在 `StatusManager` 的通用实现中是"DoT 伤害跳字间隔"。但 `COMBO_MARK` **不造成 DoT 伤害**,其 `_apply_dot_tick()` 回调被设计为空操作(`pass`)。
> 此处 `tick_interval` 被 COMBO_MARK **重载为连击窗口刷新机制**:每次施加 COMBO_MARK 时,调用方手动将 `remaining_duration` 重置为连击窗口时长(如 2.0s),利用 `StatusManager` 的 duration 计时器实现超时重置。
> **推荐实践**:为避免混淆,`COMBO_MARK` 的 `tick_interval` 设置为一个大于连击窗口的值(如 `999.0`),确保在窗口内 `tick_accumulator` 永不达到触发阈值,彻底避免意外的 DoT 跳字。
> **长期方案(P2**:为 `StatusTypeDef` 增加 `is_combo_tracker: bool` 字段,从根本上区分"持续伤害状态"和"计数追踪状态",使 `StatusManager._apply_dot_tick` 在 `is_combo_tracker = true` 时直接跳过。
> - `can_catalyze = []`(空数组):连击标记本身不触发元素反应,只作为伤害加成计算输入。
> `StatusTypeDef.can_catalyze` 字段类型为 `Array[int]`,必须赋空数组 `[]`
> 禁止赋值 `false`GDScript 4 严格类型模式下触发 bool→Array 类型错误)。
> 详见 `combat_mechanics_depth.md §3.0`StatusTypeDef 完整字段)。
## 实现逻辑 (基于 Status 系统)
1. **定义新状态**: `StatusID.VULNERABILITY` (易伤标记)。
> 所有状态引用使用 `StatusID` Autoload 常量(架构为数据驱动整数 ID,不存在 StatusType 枚举):
> - `StatusID.VULNERABILITY = 8`(在 status_id.gd 中登记)
> - `StatusID.COMBO_MARK = 7`(已定义)
> 禁止写法:`StatusType.VULNERABILITY`、`StatusType.COMBO_MARK`。
2. **施加规则**:
* 每次 `ActionLaser` (高频攻击) 命中时,施加 1 层 `StatusID.VULNERABILITY`,持续 0.5秒。
3. **叠加规则**:
*`StatusContext.addEffect` 中,如果发现已有 `StatusID.VULNERABILITY`,则 `stack++`
4. **伤害修正**:
*`onApplyDamage` 中,计算最终伤害前,读取目标的 `StatusID.VULNERABILITY` 层数。
* `FinalDamage = BaseDamage * (1 + Stack * 0.05)` (每层增伤 5%)。
* 由于激光每秒 10 次,0.5秒内能叠 5 层,伤害会越来越高。
## 扩展玩法示例
* **聚焦激光 (Focus Laser)**: 初始伤害低,但对同一目标持续照射越久伤害越高(利用 0.1s 间隔的刷新机制)。
* **三环被动 (Three-Hit Passive)**: 类似于 LOL 的薇恩。给敌人挂一个隐藏的 `StackStatus`,当 Stack 到达 3 时,消耗所有 Stack 并造成由最大生命值决定的真实伤害。
## 代码落地计划
1.`status_id.gd`StatusID Autoload)中追加 `VULNERABILITY: int = 8` 常量。
`COMBO_MARK: int = 7` 已在 `advanced_mechanics_summons_and_environment.md §3.2` 定义。
禁止在此文件外使用裸整数或 StatusType 枚举(见 ADR-R5-N2 规范)。
2.`EnemyManager.gd``apply_damage` 函数中,计算扎血前先检查 `StatusID.VULNERABILITY` 进行倍率修正。
3.`StatusManager.gd``apply_status` 函数中,确保此类型状态可正确无限叠加层数(或设置上限)。