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

20 KiB
Raw Blame History

combat.flying_projectile —— 飞行物、目标、地形碰撞、命中和回收

状态:PARTIAL

本合同覆盖 40250 客户端的两条飞行物来源:

  • 普攻/弓箭的 .msa FLY 事件 → OnSetFlyTarget / OnShootCG_FLY_TARGETINGCG_SHOOT
  • 服务端 GC_CREATE_FLYGC_FLY_TARGETINGGC_ADD_FLY_TARGETING → 客户端 CFlyingManager 弹道、目标追踪、背景/Actor 碰撞和爆炸回调。

40250 参考调用链

客户端发射事件

  • GameLib/ActorInstanceFly.cpp OnGetFlyTargetPosition
    • 从目标 Actor 的飞行目标包围球读取位置。
  • UserInterface/PythonPlayerEventHandler.cpp:232 CNormalBowAttack_FlyEventHandler_AutoClear::OnSetFlyTarget
    • 读取主角位置/朝向并发送 SendFlyTargetingPacket(target VID, target position)
  • UserInterface/PythonPlayerEventHandler.cpp:241 OnShoot
    • 在动作资源的 FLY 事件帧发送 SendShootPacket(skill)
  • UserInterface/PythonPlayerEventHandler.cpp:259 OnExplodingAtAnotherTarget
    • 若启用对应处理,向另一 Actor 发送攻击包并调用目标 OnShootDamage;当前参考实现该函数主体被直接 return,必须保留这个可达性事实,不能凭注释臆造伤害链。

飞行数据和弹道

  • GameLib/FlyingData.cpp CFlyingData::LoadScriptFile
    • .fly 资源读取 initial velocity、cone/roll、angular velocity、gravity、range、acceleration、homing、bomb effect、hit-on-background、hit-on-another-monster、pierce count、bomb range 和 attach data。
  • GameLib/FlyingInstance.cpp CFlyingInstance::Create
    • 绑定 CFlyTargetcanAttack、起点、旋转方向、局部初速/加速度和剩余射程。
  • GameLib/FlyingInstance.cpp:347 CFlyingInstance::Update
    • homing → 速度/加速度推进 → 维护 attach effect → 超程 → 目标线段命中 → 穿透/爆炸 → 其它 Actor 动态碰撞 → 地形高度/静态背景碰撞。
  • GameLib/FlyTarget.cpp CFlyTarget
    • 目标可以是对象或坐标;对象被删除时转为最后目标位置,避免悬空指针。
  • GameLib/FlyingObjectManager.cpp
    • Update 返回 false 后从实例表删除;地图/场景销毁时删除全部实例和挂接特效。

网络收包

  • UserInterface/PythonNetworkStreamPhaseGame.cpp:2559 RecvAddFlyTargetingPacket
    • 找不到射手时只记录并返回;存在目标 VID 时绑定对象,否则通过地图高度构造坐标目标。
  • :2599 RecvFlyTargetingPacket
    • 与上面相同,但替换当前目标而不是追加。
  • :2697 RecvCreateFlyPacket
    • 起点或终点实例不存在时不创建;否则 CreateIndexedFly(type, start, end)
  • :2637-2695 SendShootPacketSendAddFlyTargetingPacketSendFlyTargetingPacket
    • 每个发送操作后都发送 sequence。

当前端实现映射

  • project/fly_object.gd
    • FlyDataFlyInstanceFlyManager 对应 CFlyingData/CFlyingInstance/CFlyingManager
    • 已实现 .msfCFlyingData::LoadScriptFile 字段解析和 cm→m 单位适配,并保留目标对象/坐标、homing、线段到点命中、超程、地形高度/静态 Physics layer 2 碰撞、穿透计数、爆点和地图清理。
  • project/net_world.gd
    • _on_flyplayersettingmodule.py 的索引表加载 .msf 并消费服务端飞行 cue_on_fly_targeting 暂存 shooter 的目标队列;clear_for_map_change 清理飞行物和爆点。
    • 服务端索引经验球通过 FLY_EXP 加载 ga_piece_yellow_small2.msf;离线 spawn_exp_fly 仍是独立的多球测试/表现辅助入口。
  • project/net_play.gd
    • _emit_swing 在弓箭起手时调用 _send_fly_target,并由 bow_shot_fired 进入发射链。
    • send_fly_targeting 处理扇形/圆形目标追加。
  • project/world_shot.gdproject/fx/skill_fx.gd
    • 提供世界投射物/技能表现层。
  • extension/src/net/*
    • 当前 classic parser/store/client 提供飞行事件和目标坐标的网络入口。

分支等价矩阵

参考判据/副作用 当前实现 结论
.fly/.msf 资源读取飞行参数和 attach data FlyData.load_msf 解析参考字段、相对/绝对资源路径和 cm→m 单位;FlyInstance 已创建 AttachFile/FlyTraceNetWorld._indexed_fly_data 绑定 40250 注册资源;远端 Actor 的 MSA FLY 也进入同一实例入口 部分等价;精确远端 skill 上下文、相机尾迹几何和所有资源矩阵仍缺
起点/终点实例缺失则不创建飞行物 _on_flyVector3.INF/节点存在门控后返回 基本等价;完整 packet handler/sequence 仍未 live 验证
对象目标随 Actor 移动,目标删除后转位置 FlyTargetAnchor.fly_target_position 使用包围球中心;notify_target_despawn 在渐隐/替换前立即转为位置目标 基本等价;迟到目标包和 VID 重用的包级矩阵仍未完成
homing、速度/加速度、射程和线段到点命中 FlyInstance._adjust_homing/update 基本等价;坐标轴和单位转换需真实资源验证
m_fCollisionSphereRadius = max(move distance*2, configured radius) FlyInstance.update 在同一顺序计算膨胀半径,并通过 actor_provider 查询当前可见 Actor 基本等价;Actor 碰撞球半径的真实模型标定仍未完全证明
hitonanothermonster + 已命中对象集合 + OnExplodingAtAnotherTarget FlyInstance._find_another_actorhitted_objectsat_another 回调和穿透分支已接入;射手被排除 部分等价;EffectManager/真实动态 culling 仍未完全复现
hit-on-background 先地形高度后静态背景碰撞 当前动态 Actor 分支后仍是 sample_height 后 Physics layer 2 ray 基本等价的 Godot 平台适配;真实 culling 半径/查询范围未证明相同
pierce count:命中后 bomb,减计数,耗尽才 explode FlyInstance.pierce/target_hitted 与动态 at_another 分支均执行相同的 bomb/继续/爆炸顺序 基本等价;多种真实资源与同帧背景竞争仍未完整验证
OnShootDamage 仅在 canAttack 对象目标命中时触发 当前 shoot_damage signal 有入口,但服务端 GC 弹道使用 can_attack=false 基本等价;真实本地弓箭事件到服务端回包链未完整证明
超程 OnExplodingOutOfRange、背景/目标回调和删除 exploded signal + manager 删除;BombEffect 优先走 EffectRegistry,资源不可用才 fallback flash 基本等价;EffectManager 真实生命周期和全部回调包序仍未完整验证
Add/Set FlyTargeting 追加与替换 入站通过 shooter meta fly_target_queueappend;出站普通弓箭走独立 fly_targeting/CG_FLY_TARGETING,附加目标走 add_fly_targeting/CG_ADD_FLY_TARGETING 基本等价;shooter 不可见/本地主角/VID 重用分支待验证
GC_CREATE_FLY indexed normal/firecracker/auto-fire 类型 INDEXED_FLY_TYPES 与 40250 注册表一致;_on_fly 已分别复刻普通、随机位置烟花和 +100cm AUTO_FIRE 起点,AttachFile 优先作为实际视觉 基本等价;资源缺失 fallback、真实模型半径和包序仍未完全证明
每个 outbound 飞行目标/射击包后发送 sequence ClassicSession 字节级验证 CG_FLY_TARGETINGCG_ADD_FLY_TARGETINGCG_SHOOT 均追加正确 sequence;完整动作队列与真实 socket 包序仍未 live 验证 部分等价

已执行测试

  • project/fly_test.gdPASS;覆盖 CFlyingManager/CFlyingInstance 基础弹道、命中、超程和 __CanShot
  • project/fly_reset_test.gd:PASS;覆盖地图切换时飞行物和爆点清理。
  • project/test_exp_fly_parity.gd:6/6 PASS;覆盖经验光球飞行和吸收。
  • project/fly_target_bounds_test.gd:0 failures;覆盖模型目标包围球中心和 CPU/GPU 一致性,但有 ObjectDB leak warning。
  • project/test_click_target_effect_parity.gd:0 failures;覆盖静态障碍碰撞和目标效果清理,但有 ObjectDB/RID 清理警告。
  • project/combat_fx_test.gdPASS;覆盖命中/连击/效果主链。
  • project/fly_data_msf_test.gdPASS;覆盖 geompung.msf、经验球和 arrow_01.msf 的字段、相对资源路径、attach data、枚举及 cm→m 转换。
  • project/indexed_fly_type_test.gdPASS;覆盖 40250 NORMAL/FIRE_CRACKER/AUTO_FIRE 的索引类型、起点高度、对象/位置目标和随机距离范围。
  • project/fly_dynamic_collision_test.gd:PASS;覆盖动态 Actor 双目标穿透和同一 Actor 的 HittedObjectSet 去重。
  • project/fly_target_lifecycle_test.gd:PASS;覆盖包围球目标中心和 Actor 销毁时立即转位置目标。
  • project/fly_attachment_effect_test.gdPASS;覆盖绝对 AttachFile 解析、EffectRegistry 实例、FlyTrace 轨迹、BombEffect 和资源视觉不重复绘制。
  • project/remote_motion_fly_test.gdPASS;覆盖远端 Actor GC_FLY_TARGETING 队列、MSA FLY 事件、owner VID/skill 和资源飞行实例创建。

Implementation fix round 2026-09-21T06:34Z

FlyingObjectManager::CreateIndexedFly 接入 INDEXED_FLY_TYPES:NORMAL 保持射手根位置和对象目标,FIRE_CRACKER 使用 +200cm 起点、-90° 基准方向与 20..50m/10..25m 随机位置目标,AUTO_FIRE 使用 +100cm 起点并保留对象目标。新增 indexed_fly_type_test.gd,修复前 FIRE_CRACKER/AUTO_FIRE 断言失败,修复后通过。

FlyingInstance::Update 接入动态 Actor 碰撞:每帧使用 max(move_distance * 2, collision_sphere_radius),排除 owner,按节点实例身份维护 hitted_objects,触发 at_another 并复刻 pierce/bomb/explode 分支。按 FlyTarget/ActorInstanceFly 接入 FlyTargetAnchor 包围球中心,并在 NetWorld 的 despawn、同 VID 替换前调用 notify_target_despawn,保证渐隐节点不再继续移动飞行目标。新增 fly_dynamic_collision_test.gdfly_target_lifecycle_test.gd,均通过。

修复后 fly_testfly_reset_testfly_data_msf_testmotion_event_fly_testtest_exp_fly_paritynetbridge_testcombat_fx_testnetplay_test 和静态建筑碰撞回归通过。真实 EffectManager/FlyTrace 资源表现、真实模型动态半径、包序和不可见 Actor 生命周期仍使合同保持 PARTIAL

Implementation fix round 2026-09-22T07:55Z

复核 CFlyingManager::DeleteAllInstances 的销毁边界时发现,当前 FlyInstance.handler 闭包捕获实例自身,而实例又持有该 Callable;地图清理只清数组会留下 RefCounted 环。现新增 _release_instance(),在地图清理和自然过期两个删除路径中同步解除 handler、target、world、data 引用,再移除实例;地图清理的 visual/flash 节点也按 Loading 的同步删除边界直接释放。

修复前 fly_reset_test.gd 虽 PASS 但退出报告 5 个 ObjectDB 泄漏;修复后退出无 ObjectDB 泄漏。test_exp_fly_parity.gd 行为断言仍为 6/6 PASS,但其旧式单函数 fixture 仍报告 3 个 ObjectDB/不在 SceneTree 的路径警告,属于测试 harness 生命周期待清理,不将其伪装成完全干净证据。动作事件 FLY 的 owner/skill/包序、真实模型半径、远端不可见 Actor 和 EffectManager 全量生命周期仍保持 PARTIAL

Implementation fix round 2026-09-21T06:46Z

继续按 40250 FlyingData::LoadScriptFileFlyTraceFlyingObjectManager 的资源链修复飞行特效:TailLength 按参考保持秒单位,TailSize 只做 cm→m 转换;AttachData 现在会实例化 AttachFile,并维护 line/multi-line/sine/exp 四类尾迹历史和按时间衰减的轨迹几何。BombEffect 优先通过 EffectRegistry 资源实例化,只有资源不存在时才使用兼容性 flash,避免资源特效和通用球体重复绘制。

同时修复 EffectRegistry.msf 内绝对资源路径的解析:存在的绝对路径不再被重复拼接到资源根目录。新增 fly_attachment_effect_test.gd 覆盖 AttachFile 实例、FlyTrace 历史、BombEffect、资源视觉去重和地图清理;fly_data_msf_test.gd 增加 TailLength 单位回归。资源特效链已进入正式运行路径,但精确相机朝向、所有远端 FLY/SHOOT owner-skill 组合、EffectManager 的每种资源生命周期和真实包序仍未完成,因此合同继续保持 PARTIAL

Implementation fix round 2026-09-21T06:54Z

按 40250 CActorInstance::ProcessMotionEventFly 补齐远端 Actor 的动作事件飞行链:NetWorld 现在从 GC_FLY_TARGETING/GC_ADD_FLY_TARGETING 保存的当前目标队列取目标,在远端 MSA 的 type 6 事件帧创建同一个 .msf 飞行实例,使用 Actor 动作位置/挂骨骼位置作为起点,并传递 owner VID 与 skill index;对象目标、坐标目标和目标队列消费均保持分支语义。GC_CREATE_FLY 的服务端索引飞行仍与动作事件路径分离。

新增 remote_motion_fly_test.gd 覆盖远端目标队列、资源实例、owner/skill 和单次消费,修复后的测试无 ObjectDB 泄漏。远端动作事件没有原生 m_kCurMotNode.uSkill 的完整网络字段时仍只能使用事件字段或当前动作参数回退,精确 skill 上下文、包序、不可见 Actor 和全量资源矩阵继续保持 PARTIAL

Implementation fix round 2026-09-21T06:59Z

按 40250 CNormalBowAttack_FlyEventHandler_AutoClear::OnSetFlyTargetCPythonNetworkStream::SendFlyTargetingPacket 修正出站主目标链:新增 M2Client.fly_targetingClassicSession::send_fly_targeting 和 Godot 绑定,普通弓箭不再误发 CG_ADD_FLY_TARGETING;扇形/圆形技能的附加目标继续使用独立的 add_fly_targeting。ClassicSession 回归逐字节验证两种 header 和 sequence,战斗回归验证弓起手优先调用主目标 API。

该修复只闭合了主/附加目标包类型和局部 sequence 证据;不可见 shooter、VID 重用、真实 socket 中乱序/重连和完整 MSA→CG_SHOOT 时序仍保持 PARTIAL

Implementation fix round 2026-09-21T07:03Z

继续核对 FlyingInstance::UpdateAttachInstance 后修正尾迹几何:40250 的 MULTI_LINESINEEXP 分支使用 (-sin(roll) * offset, 0, -cos(roll) * offset),当前端原先把局部 Z 号写反,导致资源飞行物的尾迹/AttachFile 偏到相反一侧;现已改为同号。LINE 的 effect 朝向按参考只使用基础飞行旋转,其他三种 偏移类型才叠加 m_qAttachRotation。新增直接检查 MULTI_LINE 局部 Z 偏移的回归断言。

fly_attachment_effect_testfly_data_msf_testfly_testfly_dynamic_collision_testfly_target_lifecycle_testfly_reset_test 均通过。coneangle/rollangle 初始旋转、 相机相关 FlyTrace 顶点生成、完整资源矩阵和远端原生 skill 上下文仍未闭合,合同保持 PARTIAL

Active GameLib flying-core review

  • GameLib/FlyingData.h 定义 .fly 的初速、cone/roll、角速度、重力、加速度、homing、range、bomb effect、background/another-monster 命中、pierce、collision sphere 和 attach tailFlyingInstance.h 保存速度/旋转/剩余射程、目标、已命中集合和 attach effect 生命周期。
  • FlyingObjectManager.h 按 index/type 注册飞行资源,创建/更新/渲染/删除实例;FlyTrace.cpp 则用动态池记录位置时间 deque,按 tail length 衰减并生成相机相关的飞行尾迹。当前 fly_object.gd 已按索引读取 .msf,接入动态 Actor 碰撞/已命中集合、AttachFile/FlyTrace、BombEffect 资源清理和远端 MSA FLY 创建;精确相机朝向、模型半径标定、全部资源组合和远端原生 skill 上下文仍缺证据。
  • 本轮完成 FlyTarget/FlyingData/FlyingInstance/FlyingObjectManager/FlyTrace 的活跃源码静态核对;测试通过只证明基础 Godot 弹道,不提升合同状态。

已确认差异和未验证项

  1. 普通 GC 飞行物已从 40250 索引注册表加载 .msf 建立 FlyData,本地主角和远端 Actor 的动作事件 type 6 都进入同一实例入口;远端原生 m_kCurMotNode.uSkill 上下文、动作/网络包序仍未闭合。
  2. hitonanothermonster 已接入动态 Actor 碰撞、按节点身份的 HittedObjectSet 去重、穿透后继续飞行和另一目标回调;真实客户端的 culling 范围、模型动态半径和背景/Actor 同帧竞争仍未证明一致。
  3. 正式目标命中已经使用 FlyTargetAnchor 包围球中心,目标销毁也会转为最后坐标;.fly 的 bomb range/collision sphere 与真实模型资源组合仍没有逐类标定矩阵。
  4. _on_fly_targeting 只把目标队列挂在可见 shooter 节点;40250 的 CPythonCharacterManager 实例表与当前 NetWorld 可见节点表不是同一生命周期,远端暂不可见、主角本地节点、VID 重用和迟到 GC_CREATE_FLY 尚未证明一致。
  5. bombeffect 已优先走 EffectRegistryAttachFile/FlyTrace 也已进入飞行实例生命周期;精确相机尾迹、颜色/材质、所有资源组合和 EffectManager 的逐资源回收仍未证明一致。
  6. 已有测试覆盖 .msf 字段、索引类型、动态 Actor 双目标穿透/去重、目标删除、AttachFile/FlyTrace/BombEffect、主/附加目标 header+sequence 和地图清理;仍缺 Add/Set 乱序、真实服务端包序、完整资源矩阵和背景/Actor 同帧竞争。

Audit round 2026-09-21T06:24Z

本轮沿 FlyingObjectManager::CreateIndexedFlyFlyingInstance::UpdateFlyTarget::NotifyTargetClearCActorInstance::OnGetFlyTargetPosition 做了源码级闭环核对,并验证了当前资源目录包含 40250 注册的 7 个 FIRE_CRACKER .msf、1 个 AUTO_FIRE .msf 以及普通索引 .msf

  • playersettingmodule.py 的 16 个索引资源路径已在当前 INDEXED_FLY_FILES 中出现,但类型元数据没有进入当前表;NetWorld._on_fly 所有索引都统一创建普通对象目标,未复刻 FIRE_CRACKER 的 +200cm 起点/随机位置目标和 AUTO_FIRE 的 +100cm 起点。
  • 40250 每帧按 max(move_distance * 2, collision_sphere_radius) 扫描动态 Actor,并用 HittedObjectSet 去重;当前已按同一顺序实现 provider 查询、owner 排除、去重和 at_another 分支,但 provider 的可见性边界与真实模型半径仍是适配层差异。
  • 40250 目标 Actor 销毁时立即 ClearFlyTargeter(),飞行物切换为最后目标坐标;当前 _on_despawn 先走渐隐/延迟释放,FlyInstance 仍持有旧 Node3D,目标删除、同 VID 重建和迟到 GC_CREATE_FLY 尚未达到同一时序。
  • 40250 对象目标位置来自 CActorInstance::OnGetFlyTargetPosition 的包围球中心;当前 FlyInstance.target_position() 读取 Node3D 根位置,fly_target_bounds_test 的包围球 helper 尚未接入正式飞行命中链。
  • 40250 的 bombeffect、attach/tail 和 FIRE_CRACKER/AUTO_FIRE 的资源表现由 EffectManager/FlyTrace 管理;当前只生成通用 SphereMesh/flash,资源路径虽能解析但尚未真正消费。

因此本轮没有提升合同状态;当前结论仍为 PARTIAL。资源消费、动态碰撞和目标生命周期已从“缺失”推进到“有运行证据的部分等价”,后续应优先补远端动作事件 FLY、包序/乱序、不可见 Actor 与完整资源生命周期矩阵。

下一轮验证入口

  • 为动作事件 FLY 增加远端 Actor 的原生 skill 上下文和 CG_FLY_TARGETING → CG_SHOOT → sequence 真实状态机/包序测试。
  • 增加背景与 Actor 同帧竞争、三 Actor/多种 collision sphere 的真实可见性与半径矩阵。
  • 增加 GC_FLY_TARGETING → GC_ADD_FLY_TARGETING → GC_CREATE_FLY 乱序、不可见 shooter、目标删除/VID 重用和 map reset 测试。
  • 对照每个 INDEX_FLY_TYPE 的 AttachFile、BombEffect、尾迹材质、颜色和回收时序,补齐资源矩阵后再决定是否提升合同状态。