# render.effect_particle_motion —— MDE/MSE、粒子、动作事件挂点和回收 状态:`PARTIAL` 本合同不仅检查 MDE/MSE 粒子播放器,还检查 40250 `ActorInstanceMotionEvent` 在每个动作帧分派的特效、音效、飞行物、特殊攻击、显示/隐藏、传送和目标特效。判断标准是资源字段、事件类型、帧时序、挂点矩阵、生命周期和所有 Actor 的消费范围一致;只验证“特效能播放”不能视为与 40250 等价。 ## 40250 参考调用链 - `GameLib/RaceMotionData.cpp` / `RaceMotionDataEvent.h` - 读取 `MotionEventDataCount`、事件类型、`StartingTime`,按 `1 / g_fGameFPS` 转为动作帧。 - `EFFECT` 保存 `EffectFileName`、`EffectPosition`、`AttachingEnable`、`FollowingEnable`、`IndependentFlag` 和骨骼名。 - `EFFECT_TO_TARGET` 保存目标偏移、跟随/钓鱼标志;`FLY` 使用独立的 `FlyFileName`/`FlyPosition`;`SPECIAL_ATTACKING` 保存 `DuringTime`、`EnableHitProcess`、攻击参数和碰撞球。 - 动作加载时还从同名 `.mss` 读取声音脚本,后续按动作帧调用 `SoundEventProcess`。 - `GameLib/ActorInstanceMotionEvent.cpp` - `MotionEventProcess()` 对当前动作的每一帧事件调用 `MotionEventProcess(dwcurFrame, index, data)`。 - `EFFECT` 分独立世界特效、挂骨骼并跟随、挂骨骼但只快照、挂 Actor 根节点四条路径。 - `EFFECT_TO_TARGET` 分钓鱼落点、目标对象跟随和目标位置快照三条路径。 - `SPECIAL_ATTACKING` 建立带持续时间、命中去重和动态碰撞球的 splash 区;`FLY` 从骨骼/角色位置创建 `.fly` 实例并调用 `OnShoot`。 - `CHARACTER_SHOW/HIDE` 改变渲染模式和透明度;`WARP` 做地图阻挡检查、朝向目标并移动到目标前 270cm。 - `SOUND` 调用 `PlaySound3D`,声音脚本由 `SoundEventProcess` 独立更新。 - `EffectLib/EffectManager.cpp`、`EffectInstance.cpp`、`ParticleInstance.cpp`、`PythonEffectModule.cpp` - 负责 MSE/MDE 注册、解析、粒子时间曲线、混合、颜色、缩放、旋转、纹理动画、渲染空间、生命周期和实例回收。 ## 当前端实现映射 - `formats/msa.cpp` / `formats/msa.h` - 已读取动作时刻、通用效果/声音/骨骼/挂点字段,另有 MDE/MSE 和攻击数据的基础解析。 - 本轮已补齐 40250 动作事件的专用数据:`FlyFileName`/`FlyPosition`/飞行骨骼、震屏 `DuringTime`/`Power`/`AffectingRange`、特殊攻击 `DuringTime`/`EnableHitProcess`/攻击参数/碰撞球,以及 type 7/8/9 的稳定事件记录。 - `extension/src/metin2_anim.cpp` - 已在 `.msa` 播放时间跨过事件时发出 `motion_event`/`motion_event_detailed`,并提供骨骼姿态查询。 - 本轮 `motion_event_detailed`/`get_events()` 已暴露上述专用字段和 `collision_spheres`,表现层可按 40250 分支消费;生产路由尚未全部接入。 - `project/fx/effect_player.gd`、`project/fx/mde.gd`、`project/fx/mse.gd`、`project/fx/effect_registry.gd` - 已覆盖 MDE/MSE、粒子曲线、材质、颜色、混合、纹理动画、one-shot/loop 生命周期、注册/缓存和 `fx_spawned`/`fx_finished` 追踪。 - `project/fx/motion_effect_anchor.gd` / `target_effect_anchor.gd` - 已实现角色根、骨骼姿态、follow/independent、目标中心和目标快照的基础矩阵转换;缺失骨骼时会清理特效。 - `project/game_scene.gd` - 本地主角 type 1/10 走完整挂点/目标特效,type 2/3 使用固定震屏/屏闪,type 6 只从待发队列触发 `client.shoot`;本轮远端真实 Actor 的 type 1/10、7/8/9 和 direct sound 也经由 `NetWorld` 路由。 - 本地和远端真实 Actor 已接入 type 7/8 的 `__HideEvent`/`__ShowEvent` 等价消费;type 9 已按 40250 计算目标前 270cm、检查 `world.is_blocked`、调整朝向并在本地主角路径发送一次 `FUNC_WAIT`。仍没有特殊攻击事件的 splash 碰撞球,也没有把 `.fly` 文件按 40250 的起点/目标/owner/skill 链创建。 - `project/ui/player_view.gd`、`project/ui/mob_view.gd` - 已按当前动作加载同名 `.mss` 并逐 60FPS 更新 3D 声音脚本;本轮还为远端真实视图提供统一的 animator signal 接入点。 - `project/ui/remote_player_view.gd`、`project/net_world.gd` - 远端 PC 和怪物会创建真实模型并播放 `.msa`;本轮 `net_world` 已为有 native animator 的远端模型连接 `motion_event_detailed`,并由 `GameScene` 消费远端 type 1/10 特效、type 7/8 显示隐藏和直接动作声。飞行仍只走服务端 `GC_CREATE_FLY`,避免把远端动作帧错误地变成客户端发包。 ## 分支等价矩阵 | 40250 判据/副作用 | 当前实现 | 结论 | |---|---|---| | MSE/MDE 注册、解析、粒子曲线、混合和 one-shot 回收 | `effect_player`/`mse`/`mde`/`effect_registry` + FX 测试 | 基本等价;真实渲染后端像素证据仍有限 | | 动作事件按 `StartingTime → dwFrame` 在每个动作 Actor 上触发 | native 以时间区间发信号,本地主角和有真实 animator 的远端 Actor 均接入详细事件 | 部分等价;时间精度/重复跨帧和占位 Actor 升级前后的完整逐帧对照仍未完成 | | EFFECT 的独立、骨骼跟随、骨骼快照、根节点四分支 | 本地和远端真实 Actor 的 type 1 都用 `spawn_motion` 和 anchor | 部分等价;基础矩阵存在,但远端 target/清理和非详细 fallback 仍需逐分支对照 | | EFFECT_TO_TARGET 的钓鱼、目标跟随、目标快照 | 本地主角 type 10 有目标 anchor,钓鱼直接跳过 | 部分等价;缺少 40250 fishing effect 生命周期/落点字段和目标失效后的统一回收证据 | | FLY 读取 `.fly`、骨骼起点、目标、owner/skill,并执行 `OnShoot` | 当前 type 6 已保留 `fly_file`/`fly_pos`/`fly_bone` 并能传到 native 事件字典;`GC_CREATE_FLY` 的索引飞行已按 40250 注册表加载 `.msf`,动作事件仍从待发队列触发 `client.shoot` | 部分等价;索引资源参数已接入,动作事件 `.fly` 起点/owner/skill、动态碰撞和 attach/tail 仍未闭合 | | SPECIAL_ATTACKING 持续时间、动态球、命中去重和结束清理 | 当前 parser/native 字典已保留 `duration_time`、攻击参数和碰撞球 | 部分等价;splash 的生产碰撞/去重/生命周期分支仍缺失,资产中实际存在 818 个 type 4 事件 | | SOUND 直接动作声和 `.mss` 帧声 | 本地和远端详细事件都能触发 direct sound,PlayerView/MobView 更新 `.mss` | 部分等价;频率/距离和路径转换尚未做逐事件对照 | | SCREEN_WAVING/FLASHING 使用事件自身参数 | 当前固定 `shake(0.06, 9.0)` 和固定 0.12s 屏闪 | 不等价;type 2 的 `DuringTime`、`Power`、`AffectingRange` 没有保留/使用 | | CHARACTER_SHOW/HIDE、WARP | 本地和真实远端 Actor 的 type 7/8 已按事件帧切换 actor model 可见性;type 9 由 `_apply_motion_warp` 执行目标前 2.70m、阻挡检查和朝向,本地再发送 `FUNC_WAIT` | 部分等价;真实 `seomjeon.msa`、允许/阻挡/无目标路径有回归,远端目标生命周期和 live 服务端状态包仍待验证 | | 事件资源和模型从网络实体删除、切图、死亡时清理 | FX anchor 对局部无效目标会回收,map reset 会清飞行/实体 | 部分等价;没有所有 Actor 事件实例、splash、fishing effect 和跨图中断的统一清理矩阵 | ## 已执行测试 - `project/fx_test.gd`:PASS;MSE 解析、EffectPlayer 和 registry。 - `project/effect_playback_test.gd`:0 failures;播放时间和生命周期基础路径。 - `project/target_effect_test.gd`:0 failures;目标特效跟随/快照基础路径。 - `project/skill_fx_test.gd`:PASS;技能 FX 基础映射。 - `project/effect_color_test.gd`、`effect_emission_test.gd`、`effect_faces_test.gd`、`effect_lie_test.gd`、`effect_random_rotation_test.gd`、`effect_render_regression_test.gd`、`effect_rotation_test.gd`、`effect_scale_test.gd`、`effect_space_test.gd`、`effect_surface_test.gd`、`effect_texture_animation_test.gd`:均无断言失败;surface/texture animation 在 headless 下明确跳过像素检查。 - `project/motion_effect_anchor_test.gd`:断言显示 `failures=0`,但进程以 exit 139 结束并有 6 个 ObjectDB 泄漏,不能作为稳定通过证据。 - 资源静态统计:当前资产中的 `MotionEventType` 为 type 1=1892、2=263、4=818、6=268、7=47、8=47、9=47、10=197,共 3579 个事件;没有 type 5 样本,但不能因此删除 type 5 分支。 ## Deep audit round 2026-09-21 ### Fresh regression results - `fx_test.gd`、`effect_playback_test.gd`、`target_effect_test.gd`、`skill_fx_test.gd` 及颜色、发射、面、lie、随机旋转、渲染回归、旋转、缩放、空间、surface、texture animation 测试均无断言失败;surface/texture 的 headless 像素检查明确跳过。 - `motion_effect_anchor_test.gd` 的断言为 0 failures,但进程 exit 139,并泄漏 6 个 ObjectDB;因此不能升级为稳定回归通过。 ### Static parity conclusion - MDE/MSE 基础解析、粒子曲线、材质/颜色/纹理动画和本地主角基础挂点存在,但 `MotionEvent` 仍未完整保留 FLY、SPECIAL_ATTACKING、SCREEN_WAVING 参数以及 CHARACTER_SHOW/HIDE/WARP 专用字段。 - `game_scene.gd` 主要连接本地主角;RemotePlayerView/MobView 的动作没有接入 40250 式统一 motion-event 分派,因此远端特效、飞行、音效、隐藏、震屏和 warp 仍不一致。 - 当前 type 4/7/8/9 生产分支缺失,资产统计分别发现 818、47、47、47 个事件;type 6 也未按参考 `.fly` 起点、目标、owner/skill 形成等价飞行实例。 - 基础 FX 断言通过不能覆盖 splash 命中去重、事件跨帧/loop、目标删除、死亡、地图切换和实例回收;挂点测试的 exit 139/泄漏需先修复。 ### Active source group review - 对 Visual Studio `EffectLib` 活跃编译单元逐组核对后,参考链补充确认:`CEffectData::LoadScript` 同时加载 mesh、particle、light 和 `.mss` 声音数据;`CEffectManager::RegisterEffect/CreateEffectInstance/Update/Render/Destroy` 负责缓存、实例池、更新/渲染顺序和销毁;`CParticleSystemInstance`/`CParticleInstance`、`CEffectMeshInstance`、`CLightInstance` 分别维护粒子、网格帧/纹理帧和光源生命周期。 - 当前端虽然有 `EffectPlayer`/MDE/MSE/registry 的格式和局部播放测试,但没有证明声音脚本、mesh/light 三类实例、固定池耗尽、注册失败和 `DestroySystem` 的顺序与参考一致;本组静态核对完成,合同仍为 `PARTIAL`。 ## 已确认差异和未验证项 1. 已将 `MotionEvent` 扩展为按 40250 事件类型保留专用数据,并让 `motion_event_detailed` 传完整字典;本地主角和真实远端 Actor 已消费 type 1/7/8,仍需证明所有分支使用这些字段。 2. 已把动作事件连接扩展到 `NetWorld` 创建的真实远端 PC/NPC/Mob;仍需把震屏、局部 FX、飞行和隐藏行为按本地相机与远端 Actor 的范围/目标语义完整分开。 3. 必须建立 `.msa` 事件快照测试:每种事件的字段、帧、跨帧/loop、重复触发和缺字段回退;并建立与 40250 样本资源的数量和时序基线。 4. 必须增加 SPECIAL_ATTACKING splash 的碰撞、命中去重、持续时间、死亡/删除/切图清理测试,以及 FLY `.fly` 的起点、目标、owner/skill 和回收测试。 5. `motion_effect_anchor_test` 的 exit 139 和 ObjectDB 泄漏需要先修复测试稳定性,才能把基础挂点证据提升为可靠回归门禁。 ## Implementation fix round 2026-09-22 — dedicated MotionEvent payload and local show/hide - 对照 `RaceMotionDataEvent.h` / `RaceMotionData.cpp`,`formats::MotionEvent` 现在独立保存: - type 6 的 `FlyFileName`、`FlyPosition`、挂接骨骼; - type 2 的 `DuringTime`、`Power`、`AffectingRange`; - type 4 的 `DuringTime`、`EnableHitProcess`、攻击参数和 `SphereData*`; - type 7/8/9 的启动时间和稳定的详细事件字典。 - `Metin2AnimPlayer::get_events()` / `motion_event_detailed` 已传出 `fly_*`、震屏参数、特殊攻击参数和 `collision_spheres`。 - `GameScene` 已按 40250 的 type 7/8 事件帧调用本地 Actor 的 `__HideEvent` / `__ShowEvent` 等价接口;`PlayerView` / `MobView` 暴露同一 Actor 级可见性 seam。 - `NetWorld` 已按 40250 的每 Actor 事件处理边界连接远端 native animator,`GameScene` 消费远端 type 1/10 FX、type 7/8 显示隐藏和 direct sound;远端 type 6 不发客户端包。 - 回归:`formats_msa_test`、真实 type 2/4/6/7 资源的 `motion_event_payload_test`、`motion_event_visibility_test`、`net_world_motion_event_test` 通过。 - 本轮仍为 `PARTIAL`:`.fly` 资源和飞行实例、splash 碰撞/去重/清理、type 9 warp、震屏范围和统一跨图清理没有完成。 ## Implementation fix round 2026-09-22 — WARP event branch - 对照 `GameLib/ActorInstanceMotionEvent.cpp::ProcessMotionEventWarp` 和 `ActorInstanceEvent.cpp::__OnWarp`,`GameScene::_apply_motion_warp` 已实现相同的 270cm 单位换算、目标前落点、地图阻挡拒绝、朝向目标和本地主角 `FUNC_WAIT` 状态回调;远端 Actor 也消费 type 9,但不伪造本地网络包。 - `project/motion_event_warp_test.gd` 使用真实 `seomjeon.msa` 验证 type 9,并覆盖可移动、阻挡、无目标三条分支;测试通过。 - 当前合同仍为 `PARTIAL`:type 4 splash、type 6 `.fly` 数据权威、震屏参数/范围和所有 Actor 的跨图/死亡清理仍未闭合。 ## 下一轮验证入口 - 先以本轮 `formats/msa`/native payload 为输入,实现按 Actor 的事件连接/路由。 - 以 type 4、6、7、8、9 的真实资产各选样本,补逐帧事件、碰撞、飞行、显示隐藏和 warp 测试。 - 统一本地/远端/怪物/切图/死亡的 FX 实例清理,并把 `fx_spawned`/`fx_finished` 纳入可审计事件日志。