# quest.quest_event_navigation ## Scope 审计 `GC_QUEST_INFO` 任务日志、任务计时/计数、任务信件按钮、`GC_SCRIPT` NPC 对话与 `CPythonEventManager` 事件标签、确认/输入/选择、任务地图信号、Atlas/小地图导航、剧情镜幕、相机/淡入淡出和副本结果提示。判断标准是当前端是否与 40250 的协议字段、任务生命周期、事件处理顺序、脚本按钮语义和地图/过场副作用统一;本地生物学家、悬赏、活动调度等脚本 helper 的独立测试不等于 40250 客户端 parity。 ## Reference call chain - `UserInterface/PythonNetworkStreamPhaseGame.cpp` - `RecvQuestInfoPacket` 先按 `QUEST_SEND_IS_BEGIN` 判定 BEGIN/UPDATE/END,再按 flag 读取 title、clock name/value、counter name/value、icon;BEGIN 注册新任务并记录开始时间,UPDATE 只更新带 flag 的字段,END 删除指定任务,最后刷新任务 UI。 - `RecvScriptPacket` 读取动态 `GC_SCRIPT`,交给 `CPythonEventManager::RegisterEventSetFromString`,然后以 server skin 打开 QuestDialog;`RecvQuestConfirmPacket` 将 message、timeout、request PID 交给 UI。 - `SendScriptAnswerPacket` 使用 8-bit answer;`SendScriptButtonPacket` 使用 unsigned 32-bit index,任务日志点击通过 `-2147483648 + questIndex` 形成 unsigned wire value。 - `UserInterface/PythonQuest.cpp` - `CPythonQuest::MakeQuest/RegisterQuestInstance/SetQuestClockValue` 都会更新 `iStartTime`;`GetQuestLastTime` 用 `iStartTime + iClockValue - currentSecond`,因此任务窗口关闭时计时仍继续。 - `CPythonQuest` 保持任务实例容器的顺序,`GetQuestIndex` 将 UI 行号映射到真实 quest index。 - `UserInterface/PythonEventManager.cpp` / `PythonEventManager.h` - `ProcessEventSet` 逐命令处理 42 类标签:LETTER/DELAY/ENTER/WAIT/NEXT/DONE/QUESTION、图片、QUESTBUTTON、地图信号、相机、FADE/WHITE、ITEM/MOB、WINDOW_SIZE、INPUT、CONFIRM_WAIT、SELECT_ITEM 和 DUNGEON_RESULT。 - `NEXT`/取消发送 answer 254;`DONE` 只执行 `DoneEvent/CloseSelf`,不发送 answer。`RUN_CINEMA` 只在首命令时替换为 `.msc` 事件文件;FADE/WHITE 设置 wait flag,动画结束后由 `EndEventProcess` 解锁。 - `Client/Eternexus/root/uiquest.py` / `interfacemodule.py` / `uicharacter.py` - QuestDialog 的 skin、按钮和 ESC 语义由 UI 层执行;任务信件点击发送 unsigned script button 后删除信件按钮;任务日志最多显示 5 条并使用原始任务顺序。 - `UserInterface/PythonMiniMap.cpp` / `Client/Eternexus/root/uiminimap.py` / `atlasinfo.txt` - `ADDMAPSIGNAL` 写入 CPythonMiniMap 信号点并打开 Atlas;`CLEARMAPSIGNAL` 清除信号点;`SETCMAPPOS` 改变 Atlas 中心,不改变玩家世界坐标。 ## Current call chain - native `EntityStore::apply` 已解析 `GC_SCRIPT`、`GC_QUEST_CONFIRM` 和 flag-driven `GC_QUEST_INFO`,`M2Client` 暴露 `script_answer/script_button/script_select_item/quest_input/quest_confirm/quest_cancel/get_quests`,`M2Client::poll` 通过 signal 将脚本、确认和任务变更交给 Godot。 - `project/ui/quest_dialog.gd` 已实现 42 个事件标签的解析/状态机、图片、选择分页、输入、确认等待、地图信号、相机、淡入淡出、电影镜幕和副本结果信号;`project/game_scene.gd` 已连接对话、相机、淡入淡出、选择物品、小地图和 Atlas。 - `project/ui/quest_log.gd` 与 `project/ui/char_status_ui.gd` 提供任务按钮和 5 条分页列表;`project/ui/atlas_ui.gd` 提供地图拼接、拖动、玩家/队伍/公会领地/观察者标记;`project/atlas_navigation_system.gd` 是另一个带硬编码地图/NPC/距离规则的本地 helper。 - `project/biologist_quest_system.gd`、`dynamic_bounty_event_system.gd`、`event_manager_system.gd`、`metin_spawn_event_system.gd` 是本地游戏规则/活动 helper,40250 Windows 客户端没有对应的客户端 authority 实现;它们不能替代任务/事件回包链。 ## Branch matrix | 场景 | 40250 | 当前端 | 结论 | |---|---|---|---| | 任务 BEGIN/UPDATE | BEGIN 注册全新实例并记录开始时间;UPDATE 只更新带 flag 字段 | `EntityStore` 的 BEGIN/未知 UPDATE 会记录 `clock_start_ms`,并追加到 `m_quest_order`;普通 UPDATE 保持位置和起点,`clock_value` 更新会重置起点 | PARTIAL:生命周期与本地计时已对齐,CTimer/服务端时间基准仍需端到端验证 | | 任务 END | `DeleteQuestInstance(index)` 后刷新 UI | `EntityStore::apply` 与 classic parser 都删除 `m_quests[index]`、移除 `m_quest_order` 项并排入一次 `quest_changes` 刷新 | PARTIAL:删除和顺序分支已对齐,BEGIN 时间戳和 UI 重建仍未闭合 | | 任务倒计时 | `iStartTime` 从 BEGIN 或 clock update 时记录,窗口关闭也继续倒计时 | native 保存 `clock_start_ms`;QuestLog 和 `CharStatusUI` 每次显示按起点重新计算,关闭窗口期间不重置 | PARTIAL:关闭/重开语义已对齐,精确 CTimer 时间基准、过期任务清理和后台恢复仍未端到端验证 | | 任务列表顺序/索引 | vector 顺序与 `GetQuestIndex(row)` 一致 | `EntityStore::m_quest_order` 按 CPythonQuest vector 语义枚举;UPDATE 保持位置、BEGIN 追加、END 删除 | PARTIAL:native 顺序已对齐,真实 UI 行号/服务器序列仍未端到端验证 | | 任务日志点击 | `-2147483648 + questIndex` 按 unsigned 32-bit 发 `CG_SCRIPT_BUTTON` | `QuestLog`/`CharStatusUI` 传入负数 sentinel,`M2Client` 按 40250 的范围转换为 `uint32` | PARTIAL:wire sentinel 已对齐,真实 server 回包和任务窗口生命周期仍未测 | | 任务信件按钮 | 新信件同 index 先删除旧按钮、插入最前;点击发送 script button 并删除按钮 | `QuestLog.recv_quest` 顺序/点击删除逻辑已存在 | PARTIAL:UI 结构接近,但实际任务日志负索引门控仍会阻断另一入口,重连/服务器重新发送未测 | | GC_SCRIPT 事件标签 | 42 标签按逐命令、等待、锁定和事件回调处理 | parser/state machine 基本覆盖 42 标签;首命令 `RUN_CINEMA` 会读取 `.msc` 并替换事件流,缺失资源不打开空窗口 | PARTIAL:真实 `.msc` 资源序列和中断清理仍未验证 | | 对话按钮语义 | QUESTION 发送 0-based answer;NEXT/取消发送 254;DONE 只关闭;ESC 根据按钮类型分支 | QUESTION/NEXT/取消、DONE 的 `DoneEvent/CloseSelf` 和 ESC 的 QUESTION/NEXT/CANCEL 分支已对齐 | PARTIAL:真实 server 脚本结束/重复提交/中断时序仍未完全覆盖 | | 输入/选择/确认 | INPUT→`CG_QUEST_INPUT_STRING`;SELECT_ITEM 打开选择窗;CONFIRM_WAIT 显示倒计时并超时取消;END_CONFIRM_WAIT 关闭 | native/UI 信号和主要控件存在,选择/输入/确认测试通过 | PARTIAL:真实超时、重复提交、断线和 server close 清理未覆盖 | | 地图信号/Atlas | ADDMAPSIGNAL 同时写小地图 signal point/Atlas waypoint 并打开 Atlas;CLEAR 清除;SETCMAPPOS 调中心 | GameScene 将同一坐标写入 Minimap 与 AtlasUI,Atlas overlay 绘制橙色 waypoint 并支持清除 | PARTIAL:两处消费链已对齐,真实 Atlas 资源、地图切换和多点生命周期仍未端到端验证 | | 相机/淡入淡出/镜幕 | 相机设置、blend、restore 和 fade wait 由应用/事件窗完成;动画完成后释放 wait | GameScene 已连接 camera/fade,QuestCurtain 可开关 | PARTIAL:没有真实 `.msc`/资源和中断清理回归,skin/Done/ESC 边界与原版不一致 | | 自定义任务/活动 helper | 40250 客户端只负责显示/请求,规则通常由服务端给回包 | 当前 helper 本地推进生物学家、悬赏、活动掉落和倍率 | GAP(custom authority):本地测试通过不能说明与 40250 Windows 客户端统一 | ## Implementation-equivalence matrix | 项目 | 状态 | 证据/限制 | |---|---|---| | Preconditions | PARTIAL | native 阶段、answer/input 和 script-button sentinel 边界存在;事件窗口/重复提交/服务器任务存在性缺少门控 | | Branch structure | PARTIAL | 42 标签和 UI 主分支已覆盖;RUN_CINEMA 的缺失资源分支、Atlas 任务信号和部分中断分支已补齐/接通,真实过场消费者仍未验证 | | Algorithms/formulas | PARTIAL | 事件等待、颜色、相机参数和地图坐标转换大体存在;任务计时缺 reference 的 start-time 算法,硬编码 Atlas/NPC/活动 helper 不是 40250 客户端算法 | | State transition order | PARTIAL | 参考的 GC 任务包→创建/更新/删除→刷新主顺序已补齐;QuestLog 只在可见时递减,脚本 close/answer 仍有额外本地动作 | | Constants/units | PARTIAL | 5 条任务、30/16/24 字段、42 标签、5×9/按钮布局、254/255 answer 和 uint32 script-button sentinel 已对齐;skin 和地图信号显示语义未闭合 | | Timing/event sources | PARTIAL | native signal、毫秒事件等待和 fade callback 存在;任务 start timestamp、真实 `.msc` 事件、超时/断线/重连时序缺证据 | | Resource/data sources | PARTIAL | quest icon、ITEM/MOB 名称和 minimap DDS 使用资源接口;Atlas/NPC helper 另有硬编码 registry,RUN_CINEMA 仍缺真实 `.msc` 资源与消费者证据 | | Protocol side effects | PARTIAL | GC_SCRIPT/QUEST_INFO/CONFIRM parser 和基本 CG quest packets 存在;任务按钮 wire、DONE close-only、ESC 的 answer 分支已补,电影文件注册未统一 | | Interruption/failure/cleanup | GAP | 任务结束删除已覆盖 native 两条解析路径,但地图切换、对话 ESC/DONE、确认超时、fade/cinema 中断、信号清理和任务列表重建没有完整 server-sequence 回归 | ## Regression evidence - `project/quest_test.gd`、`project/quest_event_test.gd`:通过,覆盖脚本对话、确认、任务按钮和 42 标签/状态字段;使用 fake client/离线 parser,没有真实 `GC_QUEST_INFO` END、unsigned button 和 server sequence。 - `project/test_quest_dialog_parity.gd`:修正 DONE/ESC/CANCEL 断言后 47 项通过;该测试现在确认 `[DONE]` 处理时发 `done_event`、按钮点击只关闭,QUESTION 的 ESC 发送最后选项,CONFIRM_WAIT 的取消发送 254。 - `project/test_quest_hud_parity.gd`:19/19 通过;任务按钮布局和 fake quest array 通过,未覆盖 native unordered map、计时持续性和 `M2Client::script_button` 负数拒绝。 - `project/quest_curtain_test.gd`、`project/atlas_test.gd`、`project/test_atlas_navigation_parity.gd`、`project/test_map_name_shower_parity.gd`、`project/test_minimap_radar_parity.gd`:通过;主要验证本地窗口、硬编码导航 registry、地图标记和假数据,未证明 `ADDMAPSIGNAL` 在 Atlas 的实际资源/生命周期等价。 - `project/test_biologist_quest_parity.gd`:28/28 通过;验证本地生物学家任务 helper,不是 40250 Windows 客户端功能实现。 - `project/test_event_manager_parity.gd`:24/24 通过;验证本地活动倍率/宝盒 helper,40250 客户端没有对应 authority,不能作为 parity PASS。 - `build-debug/extension/net_entity_test`、`build-debug/extension/net_classic_session_test`、`build-debug/extension/net_bounds_test`、`build-debug/extension/net_classic_wire_test`:通过基础 quest/script packet、quest END 删除、vector 顺序/UPDATE 活跃状态和负数 sentinel 检查;任务计时、DONE 语义、RUN_CINEMA 消费者和 Atlas 信号仍未覆盖。 ## Deep audit round 2026-09-21 ### Fresh regression results - `godot --headless --path project --script quest_test.gd`、`quest_event_test.gd`:均退出码 0;脚本解析、确认、任务日志和 42 标签本地状态机通过。 - `godot --headless --path project --script test_quest_dialog_parity.gd`:退出码 0,45/45 通过;但 DONE 断言明确要求发送 `script_answer(254)`,与 40250 DONE 只关闭事件的实现相反,因此该条不能作为正确 parity 证据。 - `godot --headless --path project --script test_quest_hud_parity.gd`:退出码 0,19/19 通过;fake quest array 的按钮、分页、计数和时间格式通过,但使用负数 script button 值的 fake client,未经过 native wire gate。 - `godot --headless --path project --script quest_curtain_test.gd`、`atlas_test.gd`、`test_atlas_navigation_parity.gd`、`test_map_name_shower_parity.gd`、`test_minimap_radar_parity.gd`:均退出码 0;窗口、Atlas、地图标记和小地图 helper 通过。 - `godot --headless --path project --script test_biologist_quest_parity.gd`:退出码 0,28/28 通过;只验证本地生物学家任务 helper。 - `godot --headless --path project --script test_event_manager_parity.gd`:退出码 0,24/24 通过;只验证本地活动倍率/宝盒 helper。 - `build/extension/net_entity_test`、`net_classic_session_test`、`net_classic_wire_test`:均退出码 0;基础任务/脚本包通过。 ### Static parity findings - 40250 `GC_QUEST_INFO` BEGIN/UPDATE/END 的 END 会真正删除 quest instance;当前 `EntityStore` 对 END 未删除对应记录,旧任务可能继续出现在 `get_quests` 和日志中。 - 40250 在 BEGIN/clock update 保存 `iStartTime`,窗口关闭时计时仍继续;当前端已在 `QuestInfo::clock_start_ms` 保存起点,并让 QuestLog/CharStatusUI 按起点计算剩余值,仍需验证 Godot 单调时钟与参考 `CTimer::GetCurrentSecond()` 的时间基准和后台行为。 - 40250 任务列表保持 vector 顺序并通过真实 quest index 映射;当前端已加入 `m_quest_order` 并覆盖 BEGIN/UPDATE/END 的位置语义,仍缺真实任务日志回包到 UI 行号的端到端验证。 - 40250 任务日志按钮使用 `-2147483648 + questIndex` 的 unsigned 32-bit wire 值;当前 `M2Client::script_button(int)` 看到负数直接拒绝,虽然 fake UI 测试把负数记录为成功。 - `QuestDialog` 的 DONE、QUESTION/NEXT/CANCEL ESC 和确认取消已按 40250 分支对齐;真实 server 断线、重复输入和事件窗口中断仍缺端到端证据。 - `RUN_CINEMA` 的首命令替换和 `.msc` loose-resource 读取已在 `QuestDialog.begin_script()`;缺失/空文件现在按 40250 注册失败语义不打开空对话框,仍缺真实 `.msc` 资源序列/中断验证。ADDMAPSIGNAL 现在同时进入 Minimap 与 AtlasUI,仍缺真实地图资源和切图生命周期验证。 - 本地 `biologist_quest_system`、活动倍率/宝盒 helper 是服务端规则的复制状态机,不属于 40250 Windows 客户端 authority,不能因 28/28、24/24 通过而标记 parity。 ### Round conclusion 本轮仍为 `PARTIAL`:任务/对话/Atlas 基础骨架和多数局部测试可运行,但 END 删除、持续计时、任务按钮 wire、DONE/ESC、电影播放消费者和 Atlas 信号消费存在明确差异,且现有 DONE 测试断言本身与 40250 不一致。本轮未修改实现代码。 ## Active UserInterface quest-event bridge review - `PythonEventManagerMoudle.cpp` 暴露从文件/字符串注册 event set、清理、限制数量、逐行位置/宽度、等待/跳过/结束、handler/answer、可见起始行和 `SendScriptButtonPacket`;位置更新使用事件管理器的坐标变换,不能只用一个对话框文本数组替代。 - `PythonGameEventManagerModule.cpp` 将中心位置注入 `CGameEventManager` 后调用 `Update`,是过场/地图事件的逐帧桥接;`PythonQuest.h` 维护 quest instance 的标题、clock/counter 名和值、图标、创建/删除和索引查询。 - 当前端有脚本解析和 QuestDialog/Atlas 骨架,但 event-set 生命周期、handler 回调、持续计时/逐帧位置、任务实例删除以及日志按钮 wire 值尚未统一;RUN_CINEMA 和地图信号也没有发现完整消费者,合同保持 `PARTIAL`。 ## Static review evidence 已将 `PythonEventManagerMoudle.cpp`、`PythonGameEventManagerModule.cpp`、`PythonQuest.h` 纳入 active source 证据;本轮只确认 reference bridge 的完整入口和状态字段,没有把局部脚本测试当作行为等价。 ## Status `PARTIAL`。当前端已经有较完整的任务脚本解析、对话控件、任务 HUD、地图和过场骨架,42 个事件标签与基础 native packet 可工作;但任务生命周期/计时/顺序、ESC 语义、电影事件消费和 Atlas 任务信号存在明确差异,另有多个本地规则 helper 不是 40250 客户端实现。DONE、任务 END 和 script-button wire 已有对应修复。 ## Implementation fix round 2026-09-21 — quest END removes the instance 按 40250 `CPythonNetworkStream::RecvQuestInfoPacket()` 的明确分支复刻任务结束语义: `QUEST_SEND_IS_BEGIN` 旗标存在且后续 `isBegin == 0` 时,参考端将包分类为 `QUEST_PACKET_TYPE_END`,调用 `CPythonQuest::DeleteQuestInstance(index)`,随后刷新任务 列表。当前端此前只把 `begin` 改成 false,`get_quests()` 仍会枚举原有记录,造成已结束 任务继续显示。 现已在 `EntityStore::mut_quest_info`(classic parser)和 `EntityStore::apply`(协议兼容 路径)加入同一删除分支,并保留一次 `quest_changes` 刷新事件。新增的 native 回归先在 修复前确认“结束任务仍存在”失败,修复后 `net_entity_test`、`net_classic_session_test` 和 `git diff --check` 均通过。 合同仍为 `PARTIAL`:任务 BEGIN 的 start timestamp、vector 顺序、负数 script-button sentinel、DONE/ESC、RUN_CINEMA 消费者、Atlas 信号和断线清理仍未闭合。 ## Implementation fix round 2026-09-21 — quest-log script-button sentinel 继续按 40250 `uicharacter.py::QuestButtonClick` 与 `CPythonNetworkStream::SendScriptButtonPacket(unsigned int)` 核对,确认任务日志入口的 `-2147483648 + questIndex` 不是非法参数,而是写入 `uint32` wire 字段的任务索引 sentinel。此前 `M2Client::script_button(int)` 以 `idx < 0` 直接拒绝,导致 `QuestLog`/`CharStatusUI` 的任务日志点击无法到达服务器。 现已允许普通非负 `uint32` 索引,以及 `INT32_MIN .. INT32_MIN + 0xffff` 的任务索引 sentinel;其他负数仍拒绝,避免把任意错误值包装成大整数。`net_bounds_test` 已覆盖 普通端点、sentinel 端点和越界负值,构建与运行通过。 本合同仍为 `PARTIAL`:任务 BEGIN 的 start timestamp、vector 顺序、RUN_CINEMA 消费者、 Atlas 信号和断线清理仍未闭合。 ## Implementation fix round 2026-09-21 — CANCEL and ESC answer branches 继续按 40250 `uiquest.py::MakeNextButton` / `OnPressEscapeKey` 修复剩余对话关闭分支: `BUTTON_TYPE_CANCEL` 的取消按钮发送 `SCRIPT_ANSWER(254)`;ESC 在 QUESTION 时发送最后 一个选项索引,在 NEXT/CANCEL 时发送 254,在 DONE 时只关闭;没有可见答复按钮时才 回落到 native 的 255 cancel sentinel。此前当前端统一调用 `quest_cancel()`,会把 CONFIRM_WAIT 和 QUESTION/NEXT 的关闭语义压成错误的 255/取消路径。 新增回归先确认修复前 QUESTION ESC 与 CONFIRM_WAIT 取消断言失败,修复后 `test_quest_dialog_parity.gd` 47 项、`quest_test.gd`、`quest_event_test.gd` 和 `git diff --check` 通过。 本合同仍为 `PARTIAL`:任务时钟与列表顺序、RUN_CINEMA 资源消费者、Atlas 信号、真实 服务端脚本序列和断线/重连清理仍未闭合。 ## Implementation fix round 2026-09-21 — DONE event and close-only button 按 40250 `PythonEventManager::ProcessEventSet(EVENT_TYPE_DONE)` 与 `uiquest.py::MakeNextButton(BUTTON_TYPE_DONE)` 修复当前任务对话时序:`[DONE]` token 被消费时立即触发 `DoneEvent`;随后生成的“关闭”按钮只调用 `CloseSelf()`,不发送 `CG_SCRIPT_ANSWER(254)`。此前当前端把 `done_event` 延迟到按钮点击,并把 DONE 错误地 当成 NEXT/取消发送 254。 新增回归先把测试断言改为参考行为并确认修复前失败;修复后 `test_quest_dialog_parity.gd` 45 项、`quest_test.gd` 和 `git diff --check` 通过。 本合同仍为 `PARTIAL`:ESC 尚未按 CANCEL/DONE/NEXT 分支完全复刻,任务时钟、索引顺序、 RUN_CINEMA 消费者、Atlas 信号和断线清理仍未闭合。 ## Implementation fix round 2026-09-21 — quest instance vector order 按 40250 `CPythonQuest::RegisterQuestInstance`、`MakeQuest` 和 `DeleteQuestInstance` 的容器语义修复任务列表顺序:参考端使用 vector 保存任务实例, 普通 UPDATE 不改变位置,未知 UPDATE 在末尾创建,BEGIN 先删除同 index 的旧实例再把新 实例追加到末尾,END 同时删除实例和对应顺序项。当前端此前直接枚举 `unordered_map`,相同任务回包在不同运行中可能改变任务行顺序。 现已在 `EntityStore` 增加 `m_quest_order`,classic mutator 与协议兼容 `apply` 两条路径 共用相同的注册/删除规则;`quest_indices()` 只按该顺序返回仍存在的任务。修复前新增的 顺序回归按预期失败,修复后 `net_entity_test`、`net_classic_session_test`、 `net_bounds_test` 和 `git diff --check` 均通过。 本合同仍为 `PARTIAL`:精确时间基准、真实任务日志到 UI 行号映射、 RUN_CINEMA/Atlas 生命周期、服务端脚本序列和断线清理仍未闭合。 ## Implementation fix round 2026-09-21 — classic UPDATE keeps the quest active 复核发现 classic parser 的 `mut_quest_info` 与协议兼容 `apply` 对同一 UPDATE 的结果不 一致:前者会把已有任务的 `begin` 暂存字段写成 false,后者保持为有效实例。40250 在 `QUEST_PACKET_TYPE_UPDATE` 中只调用 `MakeQuest`(仅当任务不存在)和各字段 setter, 不会把已存在任务变成结束态;只有 `QUEST_PACKET_TYPE_END` 才删除实例。 现已让 classic mutator 对所有非 END 的任务包保持 `QuestInfo.begin == true`,并增加了 “UPDATE 保持活动实例且更新标题”的回归。修复前该断言失败,修复后 `net_entity_test`、`net_classic_session_test` 和 `git diff --check` 通过。 本合同仍为 `PARTIAL`:任务 start timestamp、真实 UI 行号映射、RUN_CINEMA/Atlas 生命周期、 服务端脚本序列和断线清理仍未闭合。 ## Implementation fix round 2026-09-21 — ADDMAPSIGNAL reaches Atlas 按 40250 `PythonEventManager::ProcessEventSet(EVENT_TYPE_ADD_MAP_SIGNAL)` 和 `CPythonMiniMap::AddSignalPoint` 核对,任务脚本坐标不只是圆形小地图的边缘提示;参考端 同时调用 `AddWayPoint(TYPE_WAYPOINT, ...)`,因此 Atlas 也能显示同一个 waypoint, `CLEARMAPSIGNAL` 会移除两处记录。 当前端的 `GameScene` 现在把 `map_signal_added` 同时转发给 `Minimap` 和 `AtlasUI`, Atlas overlay 使用与小地图相同的 server-centimetre→world→map 坐标转换绘制橙色标记, 并由 `map_signals_cleared` 同时清理。`atlas_test.gd` 新增添加/清理回归,测试通过。 本合同仍为 `PARTIAL`:真实 Atlas 资源、地图切换后的 signal 生命周期、RUN_CINEMA 真实 `.msc` 序列和断线/中断清理仍未闭合。 ## Implementation fix round 2026-09-21 — missing RUN_CINEMA resource 按 40250 `CPythonEventManager::RegisterEventSetFromString()` 的首命令特例和 `CPythonNetworkStream::RecvScriptPacket()` 的返回值判断修复过场脚本失败分支: `RUN_CINEMA` 指向不存在或空路径时,参考端注册返回 `-1`,不会调用 `OnScriptEventStart()`;当前端现在清理临时 event set、保持窗口关闭且不发出 `opened`, 不再显示空白锁定的任务对话框。有效 `.msc` 仍沿当前 loose-resource 读取路径替换事件流。 `test_quest_dialog_parity.gd` 新增缺失资源回归,当前 49 项全部通过;真实 `.msc` 文件的 完整事件序列、过场播放器消费者和中断清理仍保持 `PARTIAL`。 ### 2026-09-22 死代码删除 以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:`atlas_navigation_system.gd`, `biologist_quest_system.gd`, `dynamic_bounty_event_system.gd`, `event_manager_system.gd`, `metin_spawn_event_system.gd`, `test_atlas_navigation_parity.gd`, `test_biologist_quest_parity.gd`, `test_event_manager_parity.gd`, `test_map_name_shower_parity.gd`。