原版场景节点关联、可见性与 Room 父 ID

本批结论

新增 core_sim/NativeSceneAttachment.cs,将 CPositionableObject/Room Piece 的节点关联控制接入核心;节点创建、实际节点操作、运行时对象查询通过明确的后端接口提供。

本批不替换现有 Godot 地图节点树。对象工厂、Group 接受/拒绝、实际模型创建、完整世界分区与攻击型 proc 仍未完成,武器可构造覆盖保持 1053/1419。

三种关系不能合并

原字段含义本批字段
对象 +8/+12自身运行时 ID,不是 BINLAYOUT 源 IDRuntimeId
对象 +16/+20编辑器父对象的运行时 IDParentRuntimeId
对象 +76所属场景/拥有者对象Owner
对象 +80变换父对象引用TransformParent
对象 +88自身 Ogre SceneNodeNode
对象 +96挂接目标 SceneNodeAttachmentNode
对象 +104固定挂接标记;仅字节恰好为 1 时固定AttachmentPinned
对象 +105对象可见性字节,本批 API 使用规范 boolVisible

例如:已有 TransformParent 时,624DA0 设置新 Owner 不替换 TransformParent,但仍将 AttachmentNode 请求为新 Owner 的节点。因此计算节点派生姿态应依据实际节点树,不能直接使用 ParentRuntimeId 或 TransformParent 引用作为节点父亲。

本批夹具将此关联结果接到上一批 ResolveNodePoses,验证派生位置采用实际挂接父节点。这里只验证数据连接,不是实际 Godot 节点树验收。

624B80:可见性与挂接

当新旧可见性相同时不操作节点;否则:

  1. 存储新可见性。
  2. 有自身节点时,通过 6746C0 从实际父节点脱离。
  3. 如果可见,或者 TransformParent 非空,则尝试加入 AttachmentNode,再请求节点更新,参数为 true/true。
  4. 调用节点可见性入口,参数为新可见性和 cascade=true。

所以隐藏子节点仍可能保留在父节点下。不能用隐藏等同于脱离节点树,更不能由此推断碰撞资产已不存在。

6746C0 非空父节点路径调用两次 getParentSceneNode,再调用父节点 removeChild。本批保留调用次数和顺序;在受测后端中检查这些操作,而不将其简化成缓存的一次查询。

624D30/624EC0/624DA0

设置挂接节点:624D30

  • 旧 AttachmentNode 非空且固定字节等于 1:完全不修改。
  • 否则先更新固定字节,即使请求节点与旧值相同。
  • 请求节点改变时先脱离自身节点,再存储新目标。
  • 新目标非空时,翻转对象可见性并直接调用基类 624B80恢复原可见性,以强制传播。
  • 新目标为空时清固定字节。
  • 固定字节 2 不等价于 1;测试保留这个区别。

设置变换父对象:624EC0

  • 先存储 TransformParent。
  • 非空父对象请求挂到该对象的节点。
  • 清父对象时,除非已有挂接目标且固定字节等于 1,否则脱离并清挂接目标。
  • 最后翻转可见性,再经对象虚表 +56 恢复原值。

最后一步是虚调用,与 624D30 中的直接基类调用不同。

设置所属场景:624DA0

  • 先存储 Owner。
  • 仅当前 TransformParent 为空时,经虚表 +8 设置变换父对象为 Owner。
  • Owner 非空时仍请求其节点作为挂接目标。
  • Owner 为空且当前没有挂接目标时,请求 SceneManager 根节点作为目标。

此函数没有修改 ParentRuntimeId。

Room Piece 专用虚表不能漏掉

重新读取原 EXE 的主虚表:

槽位CPositionableObject:217BDDCRoom Piece:217C48C
+8 变换父对象624EC0624EC0
+12 父运行时 ID464CF0622280
+20 所属场景624DA0624DA0
+56 可见性624B80622330

此前仅查看 464CF0 不能推导 Room Piece 的完整父 ID 行为。

622330 检查 Owner+388 指向对象的 byte+16;非零则不调用基类可见性入口。本批将该值作为明确输入,不将其猜成通用“编辑器模式”。

这意味着 624EC0 先翻转可见性后,如果虚调用被 Room 覆盖阻止,翻转后的字节会保留下来。624D30 的直接基类调用不受这个覆盖阻止。该差异已由发行机器码参考验证。

622280:父 ID 与随机组祖先

基类 464CF0 只写入两个 32 位 ID 字段;Room Piece 的 622280 还执行:

  • 请求父 ID 等于自身 RuntimeId 时,将父 ID 改为 -1,然后返回。
  • 其他情况下先保存请求;无 Owner 或请求为 -1 时不遍历。
  • 从 Owner 的运行时对象查询入口 4D6D10 获取父对象。
  • 检查该对象能否转为 CRandomGroup,且其 +280 字段是否非零。
  • 命中时设置 Room byte+405 为 1。
  • 否则继续查该对象的父 ID;空对象或立即指回自身时终止。

byte+405 在该 setter 内只会置 1,不会清零。 更换父 ID 不应自动清掉此标记。

本批保留 +280 的原字段含义边界,不猜为“子节点数量”,也不将 byte+405 当成 621F60 的完整条件细节判断或 Group 接受结果。

原函数对超过一个节点的祖先环没有保护。移植端遇到此类畸形输入明确报错,避免挂死;这是诊断保护,不是原版会拒绝该环的声明。

节点创建与装载顺序证据

624F30 仅在自身 Node 为空时调用 SceneManager 虚表 +256 创建节点;若 +92 有已存附着对象则交给 624C30,随后翻转可见性并直接经 624B80 强制传播。

NativeSceneAttachment.EnsureSceneNode 保留这些调用边界。624C30 的附着模型处理仍由后端承担,测试使用显式 spy;没有把它声明为完整模型附着实现。

624FA0 构造路径先建立 SceneManager 关系并调用 624F30,再直接保存 SceneManager 的根节点到 +96。它不是把根节点当作已调用 AddChild 的证据。

继续核对的装载路径为:

  • 4D7B30 创建对象并设置 descriptor/所属场景,处理运行时 ID 唯一性、默认快照和注册。
  • 50B680 的二进制读取在对象创建后设置父运行时 ID、源 ID 和属性,将接受对象加入加载列表,再递归读取子项。
  • 50BA10 处理记录的扩展数据,然后依加载列表顺序调用 descriptor 初始化槽 +76。
  • Room 初始化槽为 4B9A00 → 6234B0,接前批资源刷新生命周期。

以上装载顺序是取证结果;本批没有实现全套 4D7B30/50B680/50BA10 工厂、随机组选择或扩展数据执行,也未将 Godot 的 _ready 顺序作为它们的替代。

验证与范围

  • 原 EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e
  • tools/build_native_scene_attachment_oracle.py 将原版相关代码页映射进隔离 32 位辅助进程;没有 attach 原游戏。
  • 17,280 组节点设置案例覆盖节点存在/缺失、Owner/TransformParent、可见性、Room 覆盖、场景抑制、固定标记 0/1/2、不同挂接目标及当前实际父节点。
  • 256 组父 ID 案例覆盖基类/Room、Owner、粘滞标记、随机组祖先、空查询、自身 ID、立即自环和多层祖先。
  • 所有受测最终字段和有序节点调用与移植代码一致:17,536 组,零差异
  • Ogre 分配/节点虚调用、运行时查询和 RTTI 使用明确 spy;不宣称原 Ogre SceneManager/Godot 渲染后端已完成。
  • 非规范 Visible 字节、释放/销毁、并发回调、长环原生挂起、完整对象构造和世界注册未纳入等价性声明。
  • core_sim.tests:1232 条核心断言通过。
  • 相关 Python 回归:149 项通过。
  • Godot C# 构建零警告、零错误;coresimlint 通过。

产物:build/native_scene_attachment_oracle/results.json、build/native_scene_attachment_core.log、build/native_scene_attachment_python.log、build/native_scene_attachment_godot.log。

NativeSceneAttachmentState 目前是隔离/分阶段装载的临时数据,没有加入持久世界所有者或存档。正式运行时集成前必须明确所有权和快照/重建策略;本批不升级快照 v29。

按 godot-master 的数据/表现分层,本批只补核心关联控制,不改变正式场景画面,无新增视觉验收声明。

main 原地工作,未提交;默认 warp_autosave.json 未修改,SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f

下一批继续对象工厂/分阶段初始化与实际视觉后端,再接真实世界注册/分区和攻击型触发技能。289 个场景候选仍缺全局随机上下文,没有因此计为已解析。