- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
118 lines
13 KiB
Markdown
118 lines
13 KiB
Markdown
# world.terrain-water-collision
|
||
|
||
## Scope
|
||
|
||
比较 40250 的户外地图高度、对象高度、属性阻挡、水面层、地形/对象拾取射线、桥梁和地面贴合调用链;同时核对加载失败、区块边界、地图切换、资源清理以及水面动画时序。
|
||
|
||
## Reference call chain
|
||
|
||
- `GameLib/MapOutdoor.cpp::GetTerrainHeight` / `GetHeight`
|
||
- 以 200cm `CELLSCALE` 在 `height.raw` 的 131×131 样本上按 TL-BR 对角线做三角插值。
|
||
- `GetHeight` 在非 terrain-only 模式下通过 culling 的 `CAttributeInstance::GetHeight` 合并桥梁、楼梯和平台等 `.mdatr` 对象高度,取对象/地形最大值。
|
||
- `GameLib/AreaTerrain.cpp::CTerrain::GetHeight`
|
||
- 使用 `HeightScale`、跨区块边界高度样本和固定的左/右三角分支;地图外返回 0。
|
||
- `GameLib/MapOutdoor.cpp::isAttrOn` / `GetAttr` / `CMapManager::isPhysicalCollision`
|
||
- 世界厘米坐标转换为 100cm 属性格;bit flag 使用按位判断,16 以上的属性值使用高半字节相等规则。
|
||
- `isPhysicalCollision` 只判断 `ATTRIBUTE_BLOCK`,并把 Metin2 的 Y 取反后交给地图属性查询。
|
||
- `PRTerrainLib/Terrain.cpp::LoadWaterMapFile` / `GameLib/AreaTerrain.cpp::GetWaterHeight`
|
||
- 读取 magic=5426、128×128 水区块、layer count 和 u16/u32 水高;水 id 映射到具体层高,错误加载会清空水图并返回失败。
|
||
- `MapOutdoorWater.cpp` 按水深计算顶点 alpha,并以 70ms/帧循环 30 张水纹理;水面共享 0 到 -15cm 的 1000~3000ms 高度扰动。
|
||
- `GameLib/MapOutdoor.cpp::GetPickingPointWithRay`
|
||
- 先在 culling 对象上做 `CAttributeInstance::Picking`,再以 5/10/100cm 自适应步长和 5000/10000/100000cm 上限探测地形,返回离射线起点更近的对象或地形命中。
|
||
- `EterLib/AttributeInstance.cpp::GetHeight` / `Picking`
|
||
- 对 `.mdatr` 高度三角形做 2D 包围盒、三角形和法向求交;对象命中包含可拾取的静态桥梁/平台,而不是只有网络角色。
|
||
|
||
## Current call chain
|
||
|
||
- `formats/terrain_files.cpp::load_height_map` / `load_attr_map` / `load_water_map`
|
||
- 读取 `height.raw`、`attr.atr` 和 `water.wtr`,验证尺寸、magic、数据长度并支持 u16/u32 水高。
|
||
- `formats/terrain_mesh.cpp::terrain_height_at` / `build_terrain_mesh`
|
||
- 已按 200cm 格、TL-BR 三角形和 `HeightScale` 生成 Godot 高度网格;`Metin2World::sample_height` 对已加载区块插值。
|
||
- `extension/src/metin2_world.cpp::build_chunk`
|
||
- 为每区块建立地形 Mesh/HeightMapShape3D、属性对象 `.mdatr` 碰撞体、对象高度三角形和水面 Mesh;区块根节点统一卸载。
|
||
- `extension/src/metin2_world.cpp::sample_height`
|
||
- 合并裸地形和已加载对象 `.mdatr` 高度,桥梁/平台命中时取更高表面;边界支持邻接区块的已加载对象查询。
|
||
- `extension/src/metin2_world.cpp::sample_attribute` / `is_blocked`
|
||
- 读取 100cm 属性格;当前调用方主要使用 `ATTRIBUTE_BLOCK` 和 `ATTRIBUTE_WATER` 的 bit0/bit1。
|
||
- `extension/src/water_builder.cpp::build_chunk_water` / `water_depth.h` / `water_motion.h`
|
||
- 每个水格保留四角深度 alpha,使用 400×HeightScale 的透明度公式、30 帧 shader 和共享高度扰动。
|
||
- `project/player_controller.gd::_ray_ground`
|
||
- 通过每 1m 采样加二分寻找 `sample_height` 地表;实体点击单独使用网络实体 AABB/胶囊近似。
|
||
- `project/net_world.gd::_ground_world` / `_blocked` / `_pushed_position`
|
||
- 网络实体和主角贴 `sample_height`;移动/击退通过 `is_blocked` 截断。
|
||
|
||
## Audit findings (2026-09-20)
|
||
|
||
### 已对齐的部分
|
||
|
||
- `terrain_height_at` 的厘米单位、200cm 格、TL-BR 对角线、`xdist <= ydist` 三角分支和 `HeightScale` 已与 `CTerrain::GetHeight` 对齐;真实 A1/C1 桥面测试证明 `.mdatr` 高度会优先于河床。
|
||
- `attr.atr` 的 2634 magic、256×256 数据、100cm 属性格和 bit0 阻挡/bit1 水面查询已有实现;移动、击退和钓鱼入口都使用同一属性读取路径。
|
||
- `water.wtr` 的 5426 magic、128×128 id、layer count、u16/u32 高度兼容和失败尺寸检查已有实现;水面四角深度、浅滩透明度、动画纹理与共享高度扰动已有回归。
|
||
- 桥梁、平台等 `.mdatr` 高度三角形已进入 `sample_height`,区块卸载由根节点统一回收;A1/C1 全地图高度探针未发现桥面掉到河床的回归。
|
||
|
||
### 材料差异
|
||
|
||
- 40250 的 `GetPickingPointWithRay` 同时比较静态对象 `CAttributeInstance::Picking` 和地形,并采用 5/10/100cm 自适应步长;当前 `PlayerController::_ray_ground` 只比较 `sample_height`,静态建筑/桥梁的射线命中没有进入点击结果。网络实体点击也只是节点 AABB/胶囊近似,尚未复用统一的世界拾取合同。
|
||
- 40250 暴露 `GetWaterHeight(int,int)`,钓鱼抛竿会把 600cm 目标点的 Z 设置为具体水层高;当前端 `get_fishing_rotation` 只验证 `ATTRIBUTE_WATER`,`_new_fishing` 没有读取水层高来生成等价的鱼钩目标位置。水面可见且钓鱼方向可判定,不代表抛竿位置已等价。
|
||
- `CTerrain::isAttrOn` 对 `byAttr >= 16` 使用高半字节相等规则;当前 `sample_attribute` 只返回原始字节、`is_blocked` 只支持 bit0,尚没有通用的 `is_attr_on` 兼容接口,因此 `BANPK` 以外的高半字节调用方缺少等价证据。
|
||
- 40250 的 `GetHeight` 会根据 `m_bEnableTerrainOnlyForHeight` 选择是否合并 culling 对象,并且对象高度查询、地形加载状态、地图坐标边界有明确返回分支;当前 `sample_height` 依赖区块已被实际加载以及 `objects_enabled/registry_ok`,资源未加载或 streaming 队列中时会暂时回退为裸地形,缺少“查询触发加载/延迟重试”的同等状态契约。
|
||
- Godot `HeightMapShape3D`/`.mdatr` 碰撞体和逻辑 `is_blocked` 是两套并行碰撞来源;当前角色移动主要走属性逻辑,静态对象物理体主要服务射线/弹道,尚没有证明两者在斜坡、对象边缘、跨区块和卸载帧的结果与 40250 `isPhysicalCollision`/`CAttributeInstance` 顺序一致。
|
||
- 当前水面视觉测试覆盖 geometry/material 和真实 A1 水深,但没有验证 `water id -> layer height -> fishing target Z` 的运行时链,也没有覆盖缺失水图后水状态清零、边界水格和地图切换期间 shader/motion 状态的完整清理矩阵。
|
||
- `TerrainDecal` 的高度贴花三角选择、`MapOutdoor` 的 normal/shadow/visibility 依赖和当前地形 patch 视锥剔除尚未形成可执行的功能合同;它们不阻塞当前移动主链,但不能因地形网格测试通过而宣称渲染/拾取完全一致。
|
||
|
||
## Equivalence matrix
|
||
|
||
| 项目 | 状态 | 备注 |
|
||
|---|---|---|
|
||
| Preconditions | PARTIAL | 资源尺寸/magic/区块加载已有检查;对象拾取、水层目标、高半字节属性和 streaming 未闭合 |
|
||
| Branch structure | PARTIAL | 高度、属性、水面主分支已映射;对象/地形射线选择、地图外、延迟加载和水失败分支未全等价 |
|
||
| Algorithms/formulas | PARTIAL | 高度插值、桥面取高、水深 alpha 已对齐;拾取步长、对象 picking 和水高接口不同 |
|
||
| State transition order | PARTIAL | 区块构建→地形/碰撞/水面→对象高度基本一致;加载队列、点击命中优先级、钓鱼目标 Z 顺序未闭合 |
|
||
| Constants/units | PARTIAL | 200cm terrain cell、100cm attr cell、600cm fishing probe、水深/动画常量已记录;射线 5/10/100cm 与当前 1m 采样不同 |
|
||
| Timing/event sources | PARTIAL | 水纹 70ms 和扰动时序已有实现;streaming、地图卸载、拾取/钓鱼事件的端到端时序证据不足 |
|
||
| Resource/data sources | PARTIAL | height/attr/water/mdatr 可读取;对象 culling、water layer 查询、terrain normal/decal/shadow 统一资源入口未完成 |
|
||
| Protocol side effects | PARTIAL | 属性阻挡/钓鱼方向会影响移动和发包;水层高度未进入抛竿副作用,静态对象点击也未统一 |
|
||
| Interruption/failure/cleanup | PARTIAL | 区块根节点卸载和基本加载失败已有路径;水状态、待处理拾取、streaming 中断和地图切换边界仍缺完整矩阵 |
|
||
|
||
## Regression evidence
|
||
|
||
- `build/formats/formats_map_test`:高度插值、地形网格、地图格式和资源边界;当前无 `M2_ASSETS` 时跳过 live A1 断言。
|
||
- `project/test_bridge_height_parity.gd`:真实 A1 两座桥的 `.mdatr` 桥面高度、河床回退和跨度采样。
|
||
- `project/test_all_map_height_probe.gd`:A1/C1 真实地图桥面高度、地形回退、属性阻挡和跨跨度采样。
|
||
- `project/water_reference_test.gd`:真实 A1 水格、四角深度 alpha、浅滩和水材质/动画结构。
|
||
- `project/fishing_water_test.gd`:真实 A1 `ATTRIBUTE_WATER`、600cm 方向扫描和地图外拒绝。
|
||
- `project/test_click_target_effect_parity.gd`:地面命中信号、网络实体点选和静态弹道障碍;该测试的地面段使用 mock world,不能替代 40250 静态对象 ray picking。
|
||
- `project/player_move_test.gd`、`project/test_skill_ground_fix.gd`:移动阻挡和角色/技能贴地回归。
|
||
|
||
## Deep audit round 2026-09-21
|
||
|
||
### Fresh regression results
|
||
|
||
- `test_bridge_height_parity.gd`、`test_all_map_height_probe.gd`、`water_reference_test.gd`、`fishing_water_test.gd`、`player_move_test.gd`、`test_skill_ground_fix.gd` 的断言均通过;A1 水面为 `failures=0`,15 pieces、212587 vertices。
|
||
- `test_click_target_effect_parity.gd` 断言通过,但进程报告 3 个 ObjectDB 和 1 个资源未释放;`fly_target_bounds_test.gd` 断言通过但 exit 139,并泄漏 48 个 ObjectDB。
|
||
- `formats_map_test`、`net_bounds_test`、`net_entity_test`、`net_classic_session_test`、`net_classic_wire_test` 均退出码 0;formats live A1 断言因未设置 `M2_ASSETS` 跳过。
|
||
|
||
### Static parity conclusion
|
||
|
||
- 高度插值、桥面取高、属性 bit0/bit1、水图读取和局部水面渲染结构继续与参考接近;这部分测试不能证明完整拾取/碰撞等价。
|
||
- 当前 `_ray_ground` 主要走 1m 采样的 `sample_height`,没有实现 40250 对静态 `.mdatr` 对象先做 `Picking`、再以 5/10/100cm 自适应步长比较对象与地形命中的统一射线链。
|
||
- 水面方向检测尚未把 `water id -> layer height -> fishing target Z` 接入抛竿副作用;高半字节属性规则、streaming 中延迟重试和对象/地形双碰撞源也未闭合。
|
||
- 点击/飞行和真实地图测试的泄漏/exit 139 表明跨图、区块卸载和特效/目标清理仍不能视为稳定等价。
|
||
|
||
### Active GameLib/PRTerrainLib terrain-core review
|
||
|
||
- `GameLib/MapOutdoor.cpp` 除高度/水面外还负责 terrain/area 初始化、environment 绑定、视锥、对象和地形 ray picking;`MapOutdoorQuadtree.cpp` 建立/销毁四叉树,`MapOutdoorIndexBuffer.cpp` 为不同 patch/warp 方向生成索引,`TerrainPatch.cpp` 同时维护硬件/软件地形顶点、水顶点、光照和 patch proxy。
|
||
- `MapOutdoorCharacterShadow.cpp` 创建角色 shadow render target/depth surface、切换 viewport/render target 后恢复旧状态;当前 Godot terrain/splat/WorldEnvironment 有视觉替代,但没有同一设备资源、patch LOD、四叉树和 shadow pass 生命周期。
|
||
- `PRTerrainLib/Terrain.h`/`TerrainType.h`/`TextureSet.cpp`/`TextureSet.h` 补充 height/normal/water/texture-set 的底层数据和纹理层状态。当前格式测试与 A1 水面测试覆盖读取结果,但未证明 TextureSet、patch、quadtree、shadow 与 streaming 卸载保持同一资源边界。
|
||
- 本轮完成上述 GameLib/PRTerrainLib 活跃地图源码静态核对;静态网格与水面测试不能替代对象 picking、patch/culling、shadow 和资源清理等价性。
|
||
|
||
## Status
|
||
|
||
`PARTIAL`。地形插值、桥面高度、属性阻挡、水面渲染和钓鱼方向已经有较强实现与真实地图证据;静态对象射线优先级、具体水层高度、高半字节属性、streaming 边界和统一碰撞/清理时序仍未达到 40250 的一致实现。
|
||
|
||
### Active EterLib collision-core review
|
||
|
||
- `EterLib/AttributeData.cpp`/`AttributeInstance.h`/`CollisionData.cpp`/`CollisionData.h` 提供静态属性数据、碰撞形状、法线/命中信息和实例级查询;`CullingManager.cpp` 负责范围、射线、可见性注册和对象集合维护;`Decal.cpp` 还参与地表贴花的对象/高度依赖。
|
||
- 这些源码确认参考端的碰撞/拾取不是单纯高度采样:对象注册、射线、属性实例和清理顺序共同决定桥梁/平台/遮挡结果。当前端的 `sample_height`、Godot `HeightMapShape3D`、逻辑 `is_blocked` 和对象 AABB/胶囊是并行近似,未统一成同一 culling/attribute query 链。
|
||
- 因此本轮完成 `AttributeData`、`CollisionData`、`CullingManager`、`Decal` 的活跃源码静态核对,但没有把静态符号存在误记为 parity 通过;静态对象优先级、跨区块卸载和碰撞/贴花清理仍是 `PARTIAL`。
|