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 / 0x189 | Collideable | 1 | COLLIDEABLE,默认 true |
| +394 / 0x18A | Blocks | 0 | BLOCK,默认 false |
| +395 / 0x18B | OccupiesNodes | 1 | OCCUPIESNODES,默认 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 的写入规则为:
if collideable_byte != 0:
active_byte = supplied_byteCollideable 为零时不写入,不自动清零;调用方已有的非规范活动字节也保留。原生参考覆盖 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 |
| 5E7A90 | CCharacter 头插到 level+148 与 level+140;PLAYER 还进 +136 |
| 5E8190 | CItem 头插到 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=true | 44 |
| COLLIDEABLE=true | 7,512 |
| OCCUPIESNODES=true | 7,542 |
每行保留完整来源链、每个值的来源文件或 default:515F90;另有读取器、生成器和原始数据文件 SHA256。
本冻结集里,这三个属性没有出现“最终获胜值来自父文件”的行。因此实际数据回归检查本文件值/缺省;子项显式默认覆盖父项、缺项继承及未验证类型拒绝,另用独立合成用例覆盖。没有用没有出现的继承案例夸大真实样本范围。
例子:
- CATACOMB_DOOR_01.DAT:COLLIDEABLE=true、BLOCK=true。
- LEGENDARY2_PISTOL06.DAT:COLLIDEABLE=true、BLOCK=false。
普通掉落武器可碰撞,并不意味着它能阻挡 block-only 视线/扫掠。
查询接线验证
测试使用真实门 DAT 的碰撞默认值,并明确提供一个受控三角形模型:
- 注册到 CItem 链,活动位开启,block-only 查询命中。
- 关闭活动位,查询不命中。
- 活动位开启但 BLOCK=false,block-only 不命中,普通查询命中。
- 同一输入显式注册到角色链,block-only 跳过,即使 BLOCK=true。
- 相同数值 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。