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

6.4 KiB
Raw Blame History

连续打击与伤害叠加设计 (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)。详见 审计报告

需求分析

用户希望实现“对同一个目标,同一种伤害在一定间隔内认定为持续多次伤害,从而可以制作伤害叠加”。

这通常被称为 “连击系统 (Combo System)”“弱点叠加 (Vulnerability Stacking)”。 与 DoT(中毒/燃烧)不同,这种机制侧重于 “攻击频率”“爆发奖励”

核心概念

1. 连击窗口 (Combo Window)

  • 每个敌人对每种伤害来源(或类型)维护一个 LastHitTimeStackCount
  • 如果在 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 中增加:

# 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_MARKintensity 来加成伤害。

追加选择:推荐方案 B,因为它复用了现有的 Tick 和合并逻辑,不需要写额外的 Timer。方案 A 适合需要多来源(source_id)区分的场景(如多玩家共同攻击同一目标)。

R4-C3 补充(COMBO_MARK 的 StatusTypeDef 参数)COMBO_MARK 对应以下 StatusTypeDef 配置:

  • stack_mode = 1Intensity 强度叠加):每次施加 +1 层,intensity 字段记录当前层数。
  • max_stacks = 00 = 无限叠加,不设层数上限;由高频攻击自然封顶)。
  • tick_interval⚠️ 语义说明(P6-N14 修正)StatusTypeDef.tick_intervalStatusManager 的通用实现中是"DoT 伤害跳字间隔"。但 COMBO_MARK 不造成 DoT 伤害,其 _apply_dot_tick() 回调被设计为空操作(pass)。 此处 tick_interval 被 COMBO_MARK 重载为连击窗口刷新机制:每次施加 COMBO_MARK 时,调用方手动将 remaining_duration 重置为连击窗口时长(如 2.0s),利用 StatusManager 的 duration 计时器实现超时重置。 推荐实践:为避免混淆,COMBO_MARKtick_interval 设置为一个大于连击窗口的值(如 999.0),确保在窗口内 tick_accumulator 永不达到触发阈值,彻底避免意外的 DoT 跳字。 长期方案(P2:为 StatusTypeDef 增加 is_combo_tracker: bool 字段,从根本上区分"持续伤害状态"和"计数追踪状态",使 StatusManager._apply_dot_tickis_combo_tracker = true 时直接跳过。
  • can_catalyze = [](空数组):连击标记本身不触发元素反应,只作为伤害加成计算输入。 StatusTypeDef.can_catalyze 字段类型为 Array[int],必须赋空数组 [] 禁止赋值 false(GDScript 4 严格类型模式下触发 bool→Array 类型错误)。 详见 combat_mechanics_depth.md §3.0StatusTypeDef 完整字段)。

实现逻辑 (基于 Status 系统)

  1. 定义新状态: StatusID.VULNERABILITY (易伤标记)。

    所有状态引用使用 StatusID Autoload 常量(架构为数据驱动整数 ID,不存在 StatusType 枚举):

    • StatusID.VULNERABILITY = 8(在 status_id.gd 中登记)
    • StatusID.COMBO_MARK = 7(已定义) 禁止写法:StatusType.VULNERABILITYStatusType.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.gdStatusID Autoload)中追加 VULNERABILITY: int = 8 常量。 COMBO_MARK: int = 7 已在 advanced_mechanics_summons_and_environment.md §3.2 定义。 禁止在此文件外使用裸整数或 StatusType 枚举(见 ADR-R5-N2 规范)。
  2. EnemyManager.gdapply_damage 函数中,计算扎血前先检查 StatusID.VULNERABILITY 进行倍率修正。
  3. StatusManager.gdapply_status 函数中,确保此类型状态可正确无限叠加层数(或设置上限)。