- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
123 lines
4.1 KiB
GDScript
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)
|