feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复

- 桥梁与静态物体高度采样修复:
  - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight
  - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程
  - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题
  - 新增 test_bridge_height_parity.gd 自动化对拍测试
- 40250 怪物击杀经验动效:
  - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附
- 40250 客户端全系统功能对齐(Batches 1-31):
  - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试
- 文档沉淀:
  - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
This commit is contained in:
shen
2026-09-19 08:51:25 -07:00
parent 1db9e9a129
commit 66d217b313
650 changed files with 53046 additions and 1586 deletions
+121
View File
@@ -0,0 +1,121 @@
# test_map_name_shower_parity.gd
# 检验 40250 地图横幅与魔塔/恶魔陵墓楼层识别及渐显时间轴
extends SceneTree
const MapNameShowerSystem = preload("res://map_name_shower_system.gd")
func _init() -> void:
print("--- 开始测试 40250 地图横幅与魔塔/陵墓楼层识别系统 (test_map_name_shower_parity) ---")
var shower = MapNameShowerSystem.new()
var fails: int = 0
# 1. 普通野外/王城地图加载
var ok = shower.show_map_name("metin2_map_a1", 1000.0, 2000.0, 0.0)
if not ok:
printerr("FAIL: metin2_map_a1 横幅未成功触发!")
fails += 1
if shower.current_image_path != "locale/ui/mapname/a1.tga":
printerr("FAIL: a1 横幅路径不符: ", shower.current_image_path)
fails += 1
if shower.current_floor_number != 0 or shower.pos_x != 0:
printerr("FAIL: 普通地图不应有楼层号偏移: ", shower.current_floor_number, " pos_x: ", shower.pos_x)
fails += 1
# 2. 魔塔 (metin2_map_deviltower1) 9 层空间识别检验
# 40250 官方坐标:
# 1F: x(10000..25000), y(58000..72000) -> 选取 (15000, 65000)
# 3F: x(10000..25000), y(10000..25000) -> 选取 (18000, 18000)
# 6F: x(14000..43500), y(14000..24500) -> 选取 (30000, 20000)
# 9F: x(56000..68000), y(13000..23000) -> 选取 (60000, 18000)
shower.show_map_name("metin2_map_deviltower1", 15000.0, 65000.0, 0.0)
if shower.current_floor_number != 1:
printerr("FAIL: 恶魔之塔 1 层坐标判定错误,得到: ", shower.current_floor_number)
fails += 1
if shower.current_floor_image != "locale/ui/mapname/devil1_1f.tga":
printerr("FAIL: 1层楼层横幅图片不符: ", shower.current_floor_image)
fails += 1
if shower.pos_x != -60:
printerr("FAIL: 恶魔之塔主横幅 X 轴偏移应为 -60,实际: ", shower.pos_x)
fails += 1
var f3 = shower.get_devil_tower_floor(18000.0, 18000.0)
if f3 != 3:
printerr("FAIL: 恶魔之塔 3 层空间判定错误: ", f3)
fails += 1
var f6 = shower.get_devil_tower_floor(30000.0, 20000.0)
if f6 != 6:
printerr("FAIL: 恶魔之塔 6 层空间判定错误: ", f6)
fails += 1
var f9 = shower.get_devil_tower_floor(60000.0, 18000.0)
if f9 != 9:
printerr("FAIL: 恶魔之塔 9 层空间判定错误: ", f9)
fails += 1
# 3. 恶魔陵墓 (metin2_map_devilsCatacomb) 7 层空间识别检验
# 1F: x(3000..45000), y(4500..45000) -> (20000, 20000)
# 4F: x(3100..56400), y(54100..105800) -> (10000, 80000)
# 7F: x(5000..15000), y(104900..122000) -> (10000, 115000)
shower.show_map_name("metin2_map_devilsCatacomb", 20000.0, 20000.0, 0.0)
if shower.current_floor_number != 1 or shower.pos_x != -75:
printerr("FAIL: 恶魔地下陵墓 1 层判定错误,层: ", shower.current_floor_number, " pos_x: ", shower.pos_x)
fails += 1
var b4 = shower.get_devil_catacomb_base(10000.0, 80000.0)
if b4 != 4:
printerr("FAIL: 恶魔地下陵墓 4 层判定错误: ", b4)
fails += 1
var b7 = shower.get_devil_catacomb_base(10000.0, 115000.0)
if b7 != 7:
printerr("FAIL: 恶魔地下陵墓 7 层判定错误: ", b7)
fails += 1
# 4. 渐变状态机时间轴演进验证
shower.show_map_name("metin2_map_b1", 500.0, 500.0, 10.0)
if shower.state != MapNameShowerSystem.State.STATE_FADE_IN:
printerr("FAIL: 初始状态应为 STATE_FADE_IN")
fails += 1
# 10.5 秒 (处于 1.0s 延迟内)alpha 保持 0
shower.update_step(10.5)
if shower.cur_alpha != 0.0:
printerr("FAIL: 延迟期间 alpha 应为 0: ", shower.cur_alpha)
fails += 1
# 11.0 秒以后开始渐显,每次增加 0.05
for i in range(18):
shower.update_step(11.0 + i * 0.05)
if shower.cur_alpha < 0.9:
printerr("FAIL: 渐变后 alpha 应达到或超过 0.9,实际: ", shower.cur_alpha)
fails += 1
if shower.state != MapNameShowerSystem.State.STATE_SHOW:
printerr("FAIL: 达到 0.9 后应进入 STATE_SHOW: ", shower.state)
fails += 1
# 停留 5.0 秒检验
var hold_start = 11.0 + 18 * 0.05
shower.update_step(hold_start + 2.0)
if shower.state != MapNameShowerSystem.State.STATE_SHOW:
printerr("FAIL: 驻留期间应保持 STATE_SHOW: ", shower.state)
fails += 1
# 停留超时后转入 FADE_OUT
shower.update_step(hold_start + 5.1)
if shower.state != MapNameShowerSystem.State.STATE_FADE_OUT:
printerr("FAIL: 驻留超时后应转入 STATE_FADE_OUT: ", shower.state)
fails += 1
# 模拟淡出至 0
for i in range(25):
shower.update_step(hold_start + 5.1 + (i + 1) * 0.1)
if shower.state != MapNameShowerSystem.State.STATE_HIDE or shower.cur_alpha != 0.0:
printerr("FAIL: 淡出完成后应转入 STATE_HIDE,实际: ", shower.state, " alpha: ", shower.cur_alpha)
fails += 1
if fails == 0:
print("PASS: test_map_name_shower_parity (10/10 全部通过)")
quit(0)
else:
printerr("FAILED: test_map_name_shower_parity (", fails, " 项失败)")
quit(1)