# combat.attack_combo_damage ## Scope 审计普攻、连击输入窗口、攻击动作状态包、`.msa` 命中窗口、碰撞命中、伤害包、击退/硬直、弓箭 FLY 帧和换图清理。 ## Reference call chain - `CPythonPlayer::NEW_Attack` - 检查 private shop、`__CanAttack`、死亡/晕眩/施法/坐骑等级/弓箭条件。 - 按目标/方向调用 `CInstanceBase::NEW_Attack` 或 `NEW_AttackToDestInstanceDirection`。 - `CInstanceBase::InputComboAttack` / `__ComboProcess` / `__RunNextCombo` - 根据职业、武器、坐骑、连击技能等级和 `ComboInputData` 推进段号;动作结束时清理 combo。 - `CPythonPlayerEventHandler::OnAttack` - 每个动作段发送 `FUNC_COMBO + motion index` 状态包。 - `CActorInstance::AttackProcess` / `__NormalAttackProcess` - 在 `.msa` 的 hit window 内用武器扫掠球和目标 `.msm` 防御球检测命中。 - `CPythonPlayerEventHandler::OnHit` - 设置目标并发送 `CG_ATTACK`;根据受击方 owner、巨型/霸体和外力执行击退及同步位置。 - `CNormalBowAttack_FlyEventHandler_AutoClear` - 起手 `OnSetFlyTarget` 发送目标,`.msa FLY` 帧 `OnShoot` 发送 `CG_SHOOT`;事件处理器随旧 Actor/动作生命周期失效。 ## Current call chain - `NetPlay._on_pick` / `_update_auto_attack` / `_reserve_process_click_actor` - 目标选择、攻击距离、自动攻击和预约追击。 - `NetPlay._do_attack_swing` / `_run_next_combo` / `_combo_process` - 播放动作、发送 `FUNC_COMBO`、读取 `.msa` combo/hit windows 和攻击速度。 - `NetPlay._attack_process` / `_normal_attack_process` - 读取攻击者采样点、目标 defending spheres,执行 Z-cylinder 几何判定和 hit dedup。 - `NetPlay._process_attack_success` / `_on_hit` - 受击动画、特效、硬直、击退、目标更新和 `client.attack`。 - `GameScene._queue_shot` / `_on_local_motion_event(type=FLY)` - 将弓箭技能先排队,等 `.msa FLY` 帧再发送 `CG_SHOOT`;`_on_world_reset` 现在清理旧队列。 ## Branch matrix | 场景 | 40250 | 当前端 | 结论 | |---|---|---|---| | 普攻输入 | `NEW_Attack` 由方向/目标/按键分派,并受 CanAttack gates 限制 | `_on_pick`、SPACE/auto attack 和 `_do_attack_swing` 已接入 | PARTIAL:鼠标 press/release owner 不完全相同 | | 连击段 | `ComboInputData` 的 start/next/end 时间和动作尾清理段号 | `.msa` 数据、combo 表和 motion_bound 清理已实现 | MAPPED + tests | | 状态包 | 每段 `FUNC_COMBO + motion index`,技能使用 `FUNC_SKILL|motion` | `_send_state` 发送对应 func/arg/位置 | MAPPED | | `.msa` 命中窗 | AttackProcess 每帧扫掠真实武器球与 `.msm` 防御球 | 仅在有效 `AttackingData/HitDataContainer` 和真实 defending sphere/bone 存在时判定 | FIXED + regression;WeaponTrace 视觉副作用仍未等价 | | 命中限制 | `m_HitDataMap`、`fInvisibleTime`、hit limit、combo 每窗去重 | `_hit_dedup`、invisible、limit 和 combo 分支已实现 | MAPPED + tests | | 受击后果 | `OnHit` 发 CG_ATTACK,按 owner/巨型/霸体决定击退和 sync position | `_process_attack_success` / `_on_hit` 已映射 | PARTIAL:服务端确认/死亡包实际闭环未 fixture 化 | | 弓箭 | 起手 target,FLY 帧才 shoot;旧 handler 清除 | `_pending_shots` 等 FLY 帧发送;本轮已在 world reset 清空 | MAPPED + regression | | 换图/断线 | 旧 Actor handler 和动作状态失效,不带旧攻击事件 | reset 已清 shot queue,其他攻击/命中窗状态仍分散 | PARTIAL | ## Equivalence matrix | 项目 | 状态 | 证据 | |---|---|---| | Preconditions | MAPPED | `_can_attack`、safe/dead/stun/skill/mount/arrow gates 已定位 | | Branch structure | PARTIAL | 鼠标 press/release、方向攻击、自动攻击、弓箭和坐骑完整排列不足 | | Algorithms/formulas | PARTIAL | combo/hit geometry 已映射;缺失资源 fallback 已移除,WeaponTrace 和服务端伤害仍不等价 | | State transition order | PARTIAL | 动作绑定、hit window、CG_ATTACK、击退和 sync position 顺序需包级证明 | | Constants/units | PARTIAL | 攻击距离、speed ratio、hit limit、stiffen/invisible 等需从资源和 fixture 双证 | | Timing/event sources | PARTIAL | 参考由 Actor motion event 驱动,当前由 Godot `_process` 和 signal 驱动 | | Resource/data sources | PARTIAL | `.msa/.msm` 资源读取和缺失资源拒绝分支已接入;真实资源覆盖与 WeaponTrace 数据源仍未完全证明 | | Protocol side effects | PARTIAL | FUNC_COMBO/CG_ATTACK/CG_SHOOT 已映射,真实服务端回包闭环未验证 | | Interruption/failure/cleanup | PARTIAL | 本轮补了 pending shot;死亡、换图、断线、动作缺失和重复命中仍需测试 | ## Regression evidence - `project/combat_fx_test.gd`:攻击锁定、连击段、hit window、命中去重、击退、抗击倒、弓箭 target/shoot 时序。 - `project/combat_parity_test.gd`:攻击锁、伤害数字和受击反馈。 - `project/gamescene_test.gd`:world reset 清理旧弓箭 FLY/CG_SHOOT 队列。 - `extension/tests/net_state_queue_test.cpp`:远端 `FUNC_ATTACK`、`FUNC_COMBO`、`FUNC_SKILL` 到达/动作状态。 ### Active GameLib weapon-trace review - `GameLib/WeaponTrace.cpp`/`.h` 使用动态池,按武器骨骼矩阵采样时间点,构建连续刀光顶点带,并分别支持纹理/alpha、生命周期、采样间隔、reach scale、TurnOn/TurnOff 和清理;它是 `ActorInstance` 攻击动作的渲染副作用,不是一次性的命中特效。 - 当前战斗链有 `skill_fx`、hit effect 和动作事件,但没有证明每个武器类型的骨骼采样、刀光顶点、alpha/texture 模式和跨动作清理与 `WeaponTrace` 相同;现有 `combat_fx_test` 只覆盖战斗桩和效果入口。 - 本轮完成 `WeaponTrace` 活跃源码静态核对,攻击/连击合同继续为 `PARTIAL`。 ## Status `PARTIAL`。普攻/连击主体已经有较完整的本地模拟;当前已删除缺少有效动作攻击数据时的本地 CG_ATTACK 合成路径,但 WeaponTrace、服务端实际伤害结果和完整输入/生命周期矩阵仍未达到 40250 完全等价。 ## Audit round 2026-09-20T21:12Z 本轮沿 `CActorInstance::AttackingProcess -> __NormalAttackProcess -> OnHit` 重新核对当前攻击实现,并重跑 `combat_fx_test`、`combat_parity_test`、`gamescene_test` 和 23 项 native CTest: - 状态包顺序、命中窗建立、`fInvisibleTime`/hit-limit 去重、受击硬直/击退、弓箭 FLY 帧以及换图清理已有可执行实现和回归证据。 - 明确保留差异:当前 `_emit_swing` 在缺少 `.msa` 命中窗时立即退化发送 `CG_ATTACK`,`_normal_attack_process` 在空 samples 时使用正前方/近距离兜底;40250 在没有有效 `m_pkCurRaceMotionData`/`HitDataContainer` 时不会合成这条本地命中路径。 - `WeaponTrace` 仍只有参考端源码证据,当前没有动态武器骨骼采样、刀光顶点带、alpha/texture 模式和动作中断回收的等价实现。 - native CTest 首次串行回归出现一次 `net.classic_encstream` 的加密头断言失败,单测复跑和全量复跑均通过;暂记为测试环境/时序抖动,不作为本轮攻击实现通过依据。 结论:缺失资源 fallback 已按 40250 的无有效攻击数据分支修复;WeaponTrace 和真实服务端伤害/死亡/击退包序进入下一轮修复候选,合同保持 `PARTIAL`。 ## Audit and implementation round 2026-09-21 沿 `ActorInstanceBattle.cpp::ComboAttack`、`ActorInstanceMotion.cpp::__SetMotion` 和 `PythonPlayerEventHandler::OnAttack` 重新核对事件顺序,确认参考端是:先绑定 `InterceptOnceMotion`(同时绑定攻击数据、清理命中表,并触发弓箭 `OnSetFlyTarget`),再由 `__OnAttack` 发送 `FUNC_COMBO + motion index`。当前端原先在 `_emit_swing` 中先发状态包、再绑定 Godot 动作,存在服务端状态已发出但本地命中窗口尚未建立的顺序差异。 本轮已调整为“绑定动作/建立命中窗口 → 弓箭 SetFlyTarget → 发送 FUNC_COMBO”,并新增 `combat_fx_test.gd` 的跨对象顺序断言;`combat_fx_test.gd`、`combat_parity_test.gd`、 `gamescene_test.gd` 均通过。 仍保持 `PARTIAL`:当前没有 `WeaponTrace` 的骨骼采样/刀光生命周期实现,也没有真实服务器对 `CG_ATTACK`、`CG_SYNC_POSITION`、死亡/伤害/击退包的端到端 fixture;弓箭和攻击状态在换图、 断线、动作中断后的全部清理仍需要继续核对。 ## Implementation fix round 2026-09-20T21:18Z 按 40250 `CActorInstance::isValidAttacking` 与 `ActorInstanceCollisionDetection.cpp::__NormalAttackProcess` 修复攻击命中源: - `NetPlay::_emit_swing` 不再在缺少当前动作 `AttackingData/HitDataContainer` 时直接发送 `CG_ATTACK`;仍只发送动作对应的 `FUNC_COMBO` 状态包,真正的 `CG_ATTACK` 只能由命中处理链触发。 - `NetPlay::_attack_process` 现在要求当前动作存在有效 `AttackingData` 和命中窗;空命中窗不命中。 - `_defending_spheres` 不再为缺少 `.msm` 防御球的节点合成占位胶囊;空 `m_DefendingPointInstanceList` 直接无命中。 - `combat_fx_test.gd`、`netplay_test.gd` 增加缺少动作攻击数据和缺少防御球的反例,`combat_parity_test.gd`、`playable_combat_test.gd` 同步回归通过。 本轮修复了“缺失资源仍产生本地命中”的结构差异;`WeaponTrace` 骨骼采样/刀光生命周期、 真实服务端伤害确认和死亡/击退包序仍保持 `PARTIAL`。 ## Audit round 2026-09-20T21:24Z — WeaponTrace call-chain review 本轮只做差异审计,没有把现有 `has_trail` 配置字段或一次性 `.mse` 特效误判为 `WeaponTrace` 已实现。沿 40250 的完整调用链核对结果如下: - `ActorInstanceAttach.cpp::AttachWeapon` 仅对非 bell/fan/bow 武器创建 `CWeaponTrace`,并绑定武器模型实例;当前 `equip_model.gd` 只设置 `weapon_gr2`/`shield_gr2` 与强化 `.mse`,没有按武器类型创建轨迹对象。 - `ActorInstance::INSTANCEBASE_Deform` 每帧执行 `TraceProcess`;当前 `PlayerView`/`Metin2Model` 没有对应的每帧武器骨骼采样出口。 - `CWeaponTrace::Update` 同时维护 short/long 两条世界坐标时间队列,使用默认 `lifeTime=0.18s`、`samplingTime=0.003s`、武器 bound-box 长度和 `__GetReachScale()` 生成刀根/刀尖采样;当前端没有时间队列、reach scale、采样间隔 或真实武器骨骼矩阵等价物。 - `CWeaponTrace::BuildVertex/Render` 用 cubic spline 生成 triangle strip,分别提供 alpha/texture 渲染模式;当前端没有 WeaponTrace mesh、顶点带、材质模式或独立 `RenderTrace` 阶段。`socket_glow_fx_system.gd` 的 `has_trail` 只是强化配置结果, 没有消费方。 - `ActorInstanceMotion.cpp::__SetMotion` 在绑定新动作前统一 `__HideWeaponTrace`, 成功绑定可攻击动作后 `__ShowWeaponTrace`;`Destroy/Clear` 删除全部 trace, `TurnOff` 保留旧队列以便自然淡出。当前 `_bind`、播放结束、换装、换图和销毁路径 都没有这一组开关/淡出/清理语义。 现有证据:`player_motion_test.gd`、`combat_fx_test.gd`、`combat_parity_test.gd` 和 `playable_combat_test.gd` 通过,但它们只能证明动作/战斗门和命中链,不能证明刀光。 `weapon_attach_test.gd` 当前还存在 race 0 的 03150 双手动作左手贴合断言失败并伴随 exit 139;该失败属于武器动作绑定的独立问题,也说明不能用当前武器挂点测试替代 WeaponTrace 证据。 结论:`combat.attack_combo_damage` 继续为 `PARTIAL`。下一次实现应新增独立 `WeaponTrace` 适配层,并在真实武器模型上验证“创建过滤 → 动作开关 → 双轨迹采样 → 刀光网格/alpha → 动作中断自然淡出 → 换装/换图销毁”的全链路;在此之前不得声称 40250 的武器刀光已经复刻。 ## Implementation round 2026-09-20 — WeaponTrace adapter 已新增 `project/ui/weapon_trace.gd`,并将它接入 `project/ui/player_view.gd` 与 `project/ui/equip_model.gd`,对应 40250 的以下调用边界: - `EquipModel._weapon_trace_enabled()` 对 bell/fan/bow 不创建刀光,普通武器才允许创建;换装和空武器路径执行 detach/clear/free。 - `PlayerView._refresh_weapon_trace()` 在附着的 `Weapon` 模型上建立 trace;`_bind()` 在绑定新动作前 `TurnOff`,攻击/连击动作成功绑定后 `TurnOn`,播放结束保留旧点并自然淡出。 - `WeaponTrace._process()` 每帧维护 short/long 世界坐标时间队列;默认 `0.18s` 生命周期、`0.003s` spline 采样步长,并复刻参考端三对角 cubic spline 与 triangle strip 顶点交错。 - `StandardMaterial3D` 提供双面、透明、无光照、不写深度的 alpha 路径,并保留 texture/alpha 两种模式;独立 `weapon_trace_test.gd` 覆盖附着、采样、网格、TurnOff 尾迹、过期、重挂和 PlayerView 攻击开关。 当前仍有明确适配差异,不能标记为完全一致:Godot 端点由 attached weapon 的 mesh AABB 推导,尚未由 native bone matrix 直接提供 `m_fLength * __GetReachScale()` 的同一坐标语义;远端/怪物渲染覆盖、`lot_ade10-2.tga` 真实视觉资源和服务端动作资源驱动的截图证据仍缺;`weapon_attach_test.gd` 的 race 0 / 03150 双手动作左手贴合断言仍失败并 exit 139。`socket_glow_fx_system.gd::has_trail` 仍是独立强化配置,不能当作 WeaponTrace 消费方。 结论:本轮把 WeaponTrace 从“生产链缺失”修复为“生产链已接入、核心生命周期有证据、坐标端点和覆盖范围仍 PARTIAL”。 ## Implementation fix round 2026-09-21T03:42Z — MainInstance damage billboard 沿 40250 `PythonNetworkStreamPhaseGame::RecvDamageInfoPacket` → `CInstanceBase::AddDamageEffect` → `CInstanceBase::ProcessDamage` 复核本地伤害显示: 参考端只要 `CharacterManager` 找到目标实例,且目标就是 `MainInstance`,仍会按 `bSelf` 入伤害队列并在本地主角上显示 `damage_*.dds`/MISS。 当前端 GameScene 为避免把本地主角重复挂入 `_by_vid`,将其单独保存为 `_local_node`。 修复前 `NetWorld._process_damage_queue()` 只从 `_by_vid` 取节点,导致本地主角自己的 伤害包已经入队,却在渲染前被静默丢弃。现在对本地 VID 使用 `_local_node` 回退,保持 普通远端 actor 与 MainInstance 的同一 `ProcessDamage` 分类和渲染路径。 新增 `project/damage_local_main_parity_test.gd`,修复前专项回归失败,修复后通过; 合同仍为 `PARTIAL`,因为服务端真实伤害/死亡包、WeaponTrace 坐标端点、远端/怪物覆盖和 攻击生命周期清理尚未达到 40250 完全等价。 ## Implementation fix round 2026-09-21T03:46Z — multi-part WeaponTrace 沿 `ActorInstanceAttach.cpp::__IsRightHandWeapon`、`__IsLeftHandWeapon` 和 `CActorInstance::AttachWeapon` 继续核对:参考端不是每个角色只有一个刀光。 匕首会同时挂 `PART_WEAPON` 与 `PART_WEAPON_LEFT`,骑马扇也可能有双部件;每个可追踪 部件都独立创建一个 `CWeaponTrace`,并由 `m_WeaponTraceVector` 统一执行开关、纹理和 清理操作。 修复前当前 `PlayerView._refresh_weapon_trace()` 只查找 `Weapon` 并维护一个对象, 因此第二个部件的刀光不会出现。现在按 `Weapon`、`Shield` 部件顺序维护多条 trace, 逐部件执行 `attach/TurnOn/TurnOff/detach`,并补齐对应的纹理、Texture/Alpha 模式 控制入口。新增 `project/weapon_trace_multi_part_parity_test.gd`,修复前回归失败, 修复后与 `weapon_trace_test.gd`、`equip_model_test.gd`、`weapon_attach_test.gd`、 `player_motion_test.gd` 和 `combat_fx_test.gd` 一起通过。 仍为 `PARTIAL`:当前刀根/刀尖端点仍由 Godot mesh AABB 推导,不是参考端骨骼矩阵加 `__GetReachScale()`;远端/怪物 trace 覆盖、真实刀光纹理视觉和服务端攻击闭环仍未证明。 ## Implementation fix round 2026-09-21T03:55Z — WeaponTrace geometry and reach scale 本轮继续沿 40250 的 `GameLib/WeaponTrace.cpp`、 `InstanceBaseEffect.cpp::__Warrior_SetGeomgyeongAffect` 和 `ActorInstanceCollisionDetection.cpp::__NormalAttackProcess` 核对端点与攻击扫掠: - 参考端在 `SetWeaponInstance` 中从武器 bound box 到骨骼原点计算 `m_fLength`,`Update` 再沿武器局部 Z 轴使用 `m_fLength * fReachScale` 生成刀尖; 当前端不再把任意 AABB 角点当作方向,而是取 attached mesh AABB 的最大半径作为长度, 沿局部 Z 轴延伸,并提供可验证的 `reach_scale`。 - 参考端 `AFFECT_GEOMGYEONG` 开启时把 reach scale 设为 `1.5`,关闭时恢复 `1.0`; 当前 `PlayerView` 将该状态广播到所有 Weapon/Shield trace,并在重新附着时保留当前值。 - 参考端普通攻击把 `dsiSrc.v3Position - dsiSrc.v3LastPosition` 乘以 `__GetReachScale()` 后再参与碰撞变换;当前 `NetPlay._normal_attack_process` 通过 `_attack_sample_positions` 对同一段位移应用该缩放,因此视觉刀光和命中扫掠共用同一 reach scale 来源。 修复前专项回归分别复现了端点方向/长度错误及缺少 reach scale API、Geomgyeong 未改变 reach scale、攻击采样没有暴露 reach-scaled endpoint;修复后以下测试通过: `weapon_trace_endpoint_parity_test.gd`、`weapon_trace_reach_affect_parity_test.gd`、 `weapon_reach_scale_attack_parity_test.gd`、`weapon_trace_multi_part_parity_test.gd`、 `weapon_trace_test.gd`、`combat_fx_test.gd`、`combat_parity_test.gd`、 `playable_combat_test.gd`、`test_combat_status_effect_parity.gd`、`netplay_test.gd`。 合同仍为 `PARTIAL`:当前端点的长度输入来自 Godot attached mesh AABB,尚未直接消费 native `CActorInstance` 的同一骨骼 composite matrix、旋转和绑定坐标语义;远端/怪物 覆盖、真实刀光纹理视觉、服务端真实攻击/死亡包级 fixture 和跨动作/断线清理仍未闭合。 ## Audit evidence reconciliation 2026-09-21T04:00Z 重新运行当前工作树的 `weapon_attach_test.gd`,加载 `00010.gr2` 和 `03150.gr2` 均正常, 进程退出码为 0;此前记录的 race 0 / 03150 左手贴合断言失败不再是当前待办项,已从 manifest 的 `remaining` 移除。该校正只更新证据状态,不改变 WeaponTrace native 骨骼 composite matrix、远端/怪物覆盖和服务端攻击闭环仍未证明的结论。