Files
mtgodot-poc/audit/contracts/combat.attack-combo-damage.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

240 lines
18 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.
# 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 + regressionWeaponTrace 视觉副作用仍未等价 |
| 命中限制 | `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 化 |
| 弓箭 | 起手 targetFLY 帧才 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、远端/怪物覆盖和服务端攻击闭环仍未证明的结论。