Files
mtgodot-poc/project/test_target_board_parity.gd
T
shenandshen 66d217b313 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 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

123 lines
4.1 KiB
GDScript

# test_target_board_parity.gd —— 40250 目标信息板 TargetBoard 1:1 回归测试套件
# 运行方式:
# /opt/homebrew/bin/godot --headless --path metin2-client/project --script test_target_board_parity.gd
extends SceneTree
const TargetUI = preload("res://ui/target_ui.gd")
const NetPlay = preload("res://net_play.gd")
class MockClient extends Node:
var cleared_target_count := 0
func clear_target() -> void:
cleared_target_count += 1
func get_entity(vid: int) -> Dictionary:
if vid == 1001:
return {"vid": 1001, "name": "兽人首领", "level": 50, "grade": 4, "kind": 2, "dead": false}
if vid == 1002:
return {"vid": 1002, "name": "狂暴野狼", "level": 12, "grade": 0, "kind": 2, "dead": false}
return {}
var _passed := 0
var _failed := 0
func _assert(cond: bool, msg: String) -> void:
if cond:
_passed += 1
print(" [PASS] %s" % msg)
else:
_failed += 1
printerr(" [FAIL] %s" % msg)
func _init() -> void:
print("\n=== Running test_target_board_parity (Direction 1: TargetBoard 1:1) ===")
_test_board_sizing_and_naming()
_test_hp_gauge_and_closing()
_test_net_play_integration()
_finish()
func _test_board_sizing_and_naming() -> void:
print("\n--- Test 1: Sizing & Name/Grade Prefix (uitarget.py:301) ---")
var client := MockClient.new()
get_root().add_child(client)
var target_ui := TargetUI.new()
get_root().add_child(target_ui)
target_ui.setup(client, get_root())
_assert(target_ui.is_open() == false, "Target board initially hidden")
# Open Boss target (Lv 50, Grade 4 = 5阶首领)
target_ui.open(1001, "兽人首领", 50, 4, false)
_assert(target_ui.is_open() == true, "Target board opened")
_assert(target_ui.get_target_name() == "[Lv. 50] (5阶首领) 兽人首领", "Name prefix formatted correctly with Lv and Grade")
# Verify dynamic width formula: 200 + 7 * len("[Lv. 50] (5阶首领) 兽人首领")
# len is 19 chars -> 200 + 7 * 19 = 333
var expected_w := 200.0 + 7.0 * float("[Lv. 50] (5阶首领) 兽人首领".length())
_assert(target_ui._board.size.x == expected_w, "Board width auto-sizes to %.1f (200 + 7*len)" % expected_w)
target_ui.queue_free()
client.queue_free()
func _test_hp_gauge_and_closing() -> void:
print("\n--- Test 2: HP Gauge 130px & Close Button (ClearTarget) ---")
var client := MockClient.new()
get_root().add_child(client)
var target_ui := TargetUI.new()
get_root().add_child(target_ui)
target_ui.setup(client, get_root())
target_ui.open(1002, "野狼", 12, 0, false)
target_ui.set_hp(60)
_assert(target_ui.get_hp_percentage() == 60, "HP set to 60%")
# Gauge width = 130.0 * 0.6 = 78.0
_assert(absf(target_ui._hp_gauge_fill.size.x - 78.0) < 0.01, "HP fill width is 78px (130 * 0.6)")
_assert(target_ui._hp_text.text == "60%", "HP text displays '60%'")
# Close button click
target_ui._on_close_button_pressed()
_assert(target_ui.is_open() == false, "Target board closed after clicking close button")
_assert(client.cleared_target_count == 1, "client.clear_target() called upon close")
target_ui.queue_free()
client.queue_free()
func _test_net_play_integration() -> void:
print("\n--- Test 3: NetPlay _on_target_info Dispatching ---")
var client := MockClient.new()
get_root().add_child(client)
var net_play := NetPlay.new()
get_root().add_child(net_play)
net_play.client = client
var target_ui := TargetUI.new()
get_root().add_child(target_ui)
target_ui.setup(client, get_root())
net_play.target_ui = target_ui
# Simulate RecvTargetPacket for vid 1001 with 85% HP
net_play._on_target_info(1001, 85)
_assert(target_ui.is_open() == true, "NetPlay _on_target_info opened target_ui")
_assert(target_ui.get_target_vid() == 1001, "Target VID is 1001")
_assert(target_ui.get_hp_percentage() == 85, "Target HP percentage is 85%")
# Clear target
net_play._clear_target()
_assert(target_ui.is_open() == false, "NetPlay _clear_target closed target_ui")
target_ui.queue_free()
net_play.queue_free()
client.queue_free()
func _finish() -> void:
print("\n=== SUMMARY: %d passed, %d failed ===" % [_passed, _failed])
if _failed == 0:
print("ALL TESTS PASSED!\n")
quit(0)
else:
printerr("SOME TESTS FAILED!\n")
quit(1)