CBaseUnit 碰撞属性与角色/物品链表接线

本批结果与更正

旧的 native-registration-los 文档已经记录 COLLIDEABLE、BLOCK 和两条查询链的归属。本次重新对照汇编确认,并把这些结论接入新几何查询接口。前一批 native-initial-collision 把三个位和两类链表重新列为未知,不准确;该段已经更正,不再把旧结论当成新的研究缺口。

实际新增:

  • 7,731 份本地提取 UNIT DAT 的三项碰撞属性导出,全部有明确结果,无待解析行。
  • C# 数据读取器及碰撞标志值模型。
  • 可供原几何查询读取的实时角色/CItem 链表 staging,头插、首个摘除和引用身份语义保留。
  • 64 组原活动位操作对照、1,536 个原链表操作对照一致。
  • 累计 1,420 条核心断言、246 项相关 Python 回归通过;Godot C# 构建零警告、零错误,coresimlint 通过。

这些数据行不是 7,731 个活场景对象;staging 也没有因此获得“完整原版世界”的资格。

标志语义

偏移含义514160 构造值515F90 DAT 读取
+393 / 0x189Collideable1COLLIDEABLE,默认 true
+394 / 0x18ABlocks0BLOCK,默认 false
+395 / 0x18BOccupiesNodes1OCCUPIESNODES,默认 true
+411 / 0x19B运行时碰撞活动开关0不是 DAT 属性

BLOCK 不是盾牌 BLOCK_CHANCE,也不是人物详细属性里的格挡率。COLLIDEABLE 不是可见性、AI 活跃标志或 Room Piece 的碰撞/烘焙字段;CBaseUnit 和 Room 的字段布局不能混用。

原接口更新为 Collideable/CollisionActive/Blocks,对应旧 Flag189/Flag19B/Flag18A。改变名称没有改变既有几何分支,其全部原生对照继续通过。

活动位不能直接由 DAT 推出

5116D0 返回 Collideable && CollisionActive。

516E60 的写入规则为:

text
if collideable_byte != 0:
    active_byte = supplied_byte

Collideable 为零时不写入,不自动清零;调用方已有的非规范活动字节也保留。原生参考覆盖 0、1、2、255 的组合,验证不是只能存 bool 0/1。

515F90 加载 COLLIDEABLE=false 时明确把 active 清零;加载 true 不主动置为 1,而是保留已有活动值。因此:

  • 构造 → 加载 BLOCK=true、COLLIDEABLE=true 后仍可能 active=0。
  • “此物件 DAT 可碰撞”不等于“当前参与碰撞”。
  • NativeBaseCollisionFlags 是不可变的输入字节副本;SetActive/LoadAuthored 返回新副本,真实状态仍必须由对象生命周期拥有者提交。
  • 没有给所有导入对象自动 SetActive(1)。

运行时写入来源包括:

  • 5E7A90 的正常角色加入阶段:通过 alive 与 COLLIDEABLE 门后置 active=1。
  • 517F70 在执行技能期间临时关闭,再依据调用前状态恢复,仍受 COLLIDEABLE 门约束。
  • 516FD0/517030、角色死亡及其他对象生命周期分支也有写入。

本批没有把这几个例子缩成一条全类型通用激活公式,也没有宣称全部状态生产者已经绑定到正式世界。

两条链的真实类型与入口

流程实际行为
5EAA80 加入分派从 CBaseUnit 先动态转 CCharacter,再转 CItem
5E7A90CCharacter 头插到 level+148 与 level+140;PLAYER 还进 +136
5E8190CItem 头插到 level+152 与 level+144
5E8010从角色相关列表找到首个对应结点后摘除
5E8560从物品相关列表找到首个对应结点后摘除
5EAC70根据 CBaseUnit+388 的类型值分派移除:0→角色,1→物品

第二条是 CItem 链,不仅是 CEquipment。5E8190 内部还会把 CItem 动态转 CEquipment,说明不能把两者当作同一个集合。道具、金币等是否属于这个原类型,要由实际工厂和创建路径给出。

不能按 DAT 文件夹或 UNITTYPE 文本简单猜 C++ 运行时类。 本次表中有 BLOCK=true 的 DAT 位于 UNITS/MONSTERS/THORNS;表只保存属性,不擅自给它们贴 CCharacter/CItem 标签。

头插与摘除

66EC50 分配 12 字节结点 value/next/previous,并插入表头。重复注册同一个对象会产生多个结点,不自动去重。

5E14C0 修复前后链接,并把被摘除结点的 next/previous 清零。外层移除函数只找第一个匹配的对象指针。

NativeCollisionListStaging 提供显式 RegisterCharacter/RegisterItem、RemoveCharacter/RemoveItem,比较 ReferenceEquals 而非数据值相等。它实现新几何查询的实时链表接口,不先复制成按 ID 排序的数组。

现有 NativeLevelList/SimState 的字符顺序快照继续保留,并没有被新 staging 替换。两者用途不同:

  • 现有 SimState 表是已绑定战斗角色的权威顺序记录,仍报告 UniverseComplete=false。
  • 新 staging 是从明确原注册事件建立的场景加载/查询适配器,包含对象引用;不作为持久化世界成员表。

完整 add/remove-unit 入口还涉及场景指针、物理代理、网格占用、角色目标、材质和事件。这里只接两条查询链,没有把完整对象生命周期伪装成两个简单列表操作。

DAT 导出

tools/build_native_unit_collision.py 使用已验证的 UNIT 标量 BASEFILE 合并语义:子项存在就覆盖父项,包括子项写了 false 或写了恰好等于引擎默认值的 true。

只处理这三个布尔标量,不将它扩展成通用 DAT 解析器。未取证的非 BOOL 类型别名会形成明确的 pending 记录;C# 读取 pending 或缺失路径时抛出输入缺失诊断。

当前本地提取树统计:

项目数量
UNIT DAT 文件7,731
成功解析7,731
待解析0
BLOCK=true44
COLLIDEABLE=true7,512
OCCUPIESNODES=true7,542

每行保留完整来源链、每个值的来源文件或 default:515F90;另有读取器、生成器和原始数据文件 SHA256。

本冻结集里,这三个属性没有出现“最终获胜值来自父文件”的行。因此实际数据回归检查本文件值/缺省;子项显式默认覆盖父项、缺项继承及未验证类型拒绝,另用独立合成用例覆盖。没有用没有出现的继承案例夸大真实样本范围。

例子:

  • CATACOMB_DOOR_01.DAT:COLLIDEABLE=true、BLOCK=true。
  • LEGENDARY2_PISTOL06.DAT:COLLIDEABLE=true、BLOCK=false。

普通掉落武器可碰撞,并不意味着它能阻挡 block-only 视线/扫掠。

查询接线验证

测试使用真实门 DAT 的碰撞默认值,并明确提供一个受控三角形模型:

  1. 注册到 CItem 链,活动位开启,block-only 查询命中。
  2. 关闭活动位,查询不命中。
  3. 活动位开启但 BLOCK=false,block-only 不命中,普通查询命中。
  4. 同一输入显式注册到角色链,block-only 跳过,即使 BLOCK=true。
  5. 相同数值 ID 的两个不同对象保持引用身份区分;重复注册只移除首个相同引用。

三角形是验证输入,不是门的完整原模型。这里证明 DAT 属性、实时链表与原几何算法的组合,不证明门动画、开门事件及完整动态碰撞资源已经复原。

证据范围与产物

机器码参考实际执行 5116D0/516E60 与 66EC50/5E14C0。分配存储是明确边界;参考调用方按原移除入口已核实的首个匹配规则选结点。没有声称执行了整个 5E7A90/5E8190/5E8010/5E8560 生命周期。

  • core_sim/NativeBaseCollisionFlags.cs、NativeCollisionListStaging.cs。
  • core_sim/NativeInitialCollision.cs 的语义字段接线。
  • core_sim.io/F2NativeUnitCollision.cs。
  • core_sim.tests/NativeCollisionMembershipChecks.cs。
  • tools/build_native_unit_collision.py。
  • tools/build_native_collision_membership_oracle.py、native_collision_membership_oracle.c。
  • tests/test_native_collision_membership.py。
  • build/f2/native_unit_collision.json。
  • build/native_collision_membership_oracle/results.json。
  • build/native_collision_membership_core.log、native_collision_membership_python.log、native_collision_membership_godot.log。

EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e

剩余

后续进展:单位主虚表、模型来源与动态包围盒已确认真实 getter 槽并实现 world bounds 刷新及查询绑定;完整资源装载/局部范围初始化和地图生命周期仍需继续。

角色/物品链的类型、头插/摘除规则和基础 DAT 属性不再是未知项。继续实际对象模型/bounds 生产、运行时活动位的生命周期绑定、原关卡 bounds 来源和完整地图世界宿主,再接正式攻击遮挡。

武器可构造覆盖仍为 1,053/1,419,动画、攻击型 proc、缺导出资产与全武器实机验收未完成。部分角色列表的 UniverseComplete=false 没有放宽,不能拿新 staging 绕过这个保护。

本批按 godot-master 的状态归属要求,DAT 默认与活动字节分开,模型/场景数据由宿主明确提供。main 原地工作,未提交、未 attach 原游戏、未改正式场景与 UI,快照 v29 未改。默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f