- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
113 lines
4.8 KiB
GDScript
113 lines
4.8 KiB
GDScript
# test_guild_war_observer_arena_parity.gd —— Metin2 40250 公会战备观战与武斗擂台传送 1:1 测试套件
|
|
extends SceneTree
|
|
|
|
const GuildWarObserverArenaSystem = preload("res://guild_war_observer_arena_system.gd")
|
|
|
|
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_guild_war_observer_arena_parity (Direction 4: Guild War Observer 1:1) ===")
|
|
|
|
test_active_wars_and_display()
|
|
test_observer_entry_and_ghost_mode()
|
|
test_manual_leave_observer_mode()
|
|
test_war_ending_auto_evacuation()
|
|
test_serialization()
|
|
|
|
print("\n=======================================================")
|
|
print("Guild War Observer Tests: %d Passed, %d Failed" % [_passed, _failed])
|
|
print("=======================================================\n")
|
|
|
|
if _failed > 0:
|
|
quit(1)
|
|
else:
|
|
quit(0)
|
|
|
|
func test_active_wars_and_display() -> void:
|
|
print("\n--- Test 1: Active Wars Registration & Town Guard List (40250 1:1) ---")
|
|
var system = GuildWarObserverArenaSystem.new()
|
|
|
|
var wid = system.register_active_war(101, "王者之师", 202, "龙城铁骑", 1, 50)
|
|
_assert(wid > 0, "Registered active war")
|
|
|
|
var list = system.get_active_wars_for_display()
|
|
_assert(list.size() == 1, "Listed 1 active war for guard display")
|
|
_assert(list[0]["guild1_name"] == "王者之师", "Guild 1 name correct")
|
|
_assert(list[0]["guild2_name"] == "龙城铁骑", "Guild 2 name correct")
|
|
_assert(list[0]["map_index"] == GuildWarObserverArenaSystem.MAP_GUILD_ARENA, "Map index is 110 (Arena)")
|
|
|
|
func test_observer_entry_and_ghost_mode() -> void:
|
|
print("\n--- Test 2: Observer Entry & Ghost Mode (40250 1:1) ---")
|
|
var system = GuildWarObserverArenaSystem.new()
|
|
var wid = system.register_active_war(101, "王者之师", 202, "龙城铁骑", 1, 50)
|
|
|
|
var orig_pos = Vector2(800.0, 900.0)
|
|
var res = system.enter_as_observer(999, wid, "主城辛秀", orig_pos)
|
|
_assert(res["ok"] == true, "Entered observer mode")
|
|
_assert(res["observer_pos"] == GuildWarObserverArenaSystem.OBSERVER_PLATFORM_POS, "Warps to high platform")
|
|
_assert(system.is_player_observing(999) == true, "Player 999 is in observer state")
|
|
# 幽灵模式免疫战斗与禁止出招
|
|
_assert(system.can_player_attack_or_be_attacked(999) == false, "Player cannot attack or be attacked in observer mode")
|
|
|
|
func test_manual_leave_observer_mode() -> void:
|
|
print("\n--- Test 3: Manual Leave Observer Mode (40250 1:1) ---")
|
|
var system = GuildWarObserverArenaSystem.new()
|
|
var wid = system.register_active_war(101, "王者之师", 202, "龙城铁骑", 1, 50)
|
|
|
|
var orig_pos = Vector2(1200.0, 1500.0)
|
|
system.enter_as_observer(999, wid, "主城辛秀", orig_pos)
|
|
|
|
# 主动离开
|
|
var leave_res = system.leave_observer_mode(999)
|
|
_assert(leave_res["ok"] == true, "Left observer mode")
|
|
_assert(leave_res["return_map"] == "主城辛秀", "Returned to previous map")
|
|
_assert(leave_res["return_pos"] == orig_pos, "Returned to previous coordinates")
|
|
_assert(system.is_player_observing(999) == false, "No longer observer")
|
|
_assert(system.can_player_attack_or_be_attacked(999) == true, "Combat privileges restored")
|
|
|
|
func test_war_ending_auto_evacuation() -> void:
|
|
print("\n--- Test 4: War Ending Auto Evacuation (40250 1:1) ---")
|
|
var system = GuildWarObserverArenaSystem.new()
|
|
var wid = system.register_active_war(101, "王者之师", 202, "龙城铁骑", 1, 50)
|
|
|
|
var left_signals := []
|
|
system.observer_left.connect(func(pid, map, pos):
|
|
left_signals.append({"pid": pid, "map": map, "pos": pos})
|
|
)
|
|
|
|
# 2 位观战者入场
|
|
system.enter_as_observer(801, wid, "主城辛秀", Vector2(100.0, 100.0))
|
|
system.enter_as_observer(802, wid, "主城镇奴", Vector2(200.0, 200.0))
|
|
|
|
# 公会 1 战胜达成 50 分,终局自动结算
|
|
system.update_war_score(wid, 50, 30)
|
|
|
|
# 检查全部观战者被安全自动遣散
|
|
_assert(left_signals.size() == 2, "Both observers evacuated")
|
|
_assert(system.is_player_observing(801) == false, "Observer 801 cleared")
|
|
_assert(system.is_player_observing(802) == false, "Observer 802 cleared")
|
|
_assert(system.get_active_wars_for_display().is_empty(), "War is marked ended and cleared from active list")
|
|
|
|
func test_serialization() -> void:
|
|
print("\n--- Test 5: Serialization & Restoration ---")
|
|
var system = GuildWarObserverArenaSystem.new()
|
|
var wid = system.register_active_war(101, "公会A", 202, "公会B")
|
|
system.enter_as_observer(555, wid, "主城", Vector2(300.0, 400.0))
|
|
|
|
var data = system.serialize()
|
|
_assert(data.has("observers") and data["observers"].has(555), "Serialized observer state")
|
|
|
|
var restored = GuildWarObserverArenaSystem.new()
|
|
restored.deserialize(data)
|
|
_assert(restored.is_player_observing(555) == true, "Restored observer state")
|
|
_assert(restored.can_player_attack_or_be_attacked(555) == false, "Restored combat immunity")
|