# 连续打击与伤害叠加设计 (Consecutive Hits & Damage Stacking)

> **⚠️ 实现现状 (2026-07-20 审计)**：`VULNERABILITY`（易伤，+5%/层）状态**未实现**——无 JSON 条目、`apply_damage` 不读取。现状代码改用 **`COMBO_MARK` 每层 +2%**（`enemy_manager.gd`）。文中标注为 P2 长期方案的 `is_combo_tracker` **已提前实现**并正确守卫 DoT tick（COMBO_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` 函数中，确保此类型状态可正确无限叠加层数（或设置上限）。
