# test_death_restart_parity.gd —— 死亡结算与复活面板 1:1 回归测试套件 # 运行方式: # /opt/homebrew/bin/godot --headless --path metin2-client/project --script test_death_restart_parity.gd extends SceneTree const DeathUI = preload("res://ui/death_ui.gd") class MockClient extends Node: signal entity_dead(vid: int) signal vitals_changed(vid: int) signal phase_changed(phase: String) var sent_chat_commands: Array[String] = [] var entity_data := {"hp": 0, "dead": true} func get_main_vid() -> int: return 1 func get_entity(vid: int) -> Dictionary: return entity_data if vid == 1 else {} func say(type: int, text: String) -> bool: sent_chat_commands.append(text) return true 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_death_restart_parity (Direction 4: RestartDialog 1:1) ===") _test_dialog_layout() _test_10s_cooldown_countdown() _test_restart_commands() _test_auto_close_on_revive() _finish() func _test_dialog_layout() -> void: print("\n--- Test 1: 40250 200x88 Geometry & Button Placement ---") var client := MockClient.new() get_root().add_child(client) var death_ui := DeathUI.new() get_root().add_child(death_ui) death_ui.setup(client, get_root()) _assert(death_ui.is_open() == false, "Restart dialog initially closed") death_ui.show_dialog() _assert(death_ui.is_open() == true, "Restart dialog opened") _assert(death_ui._board.size == Vector2(200, 88), "Board size is 200x88 (restartdialog.py)") _assert(death_ui._btn_restart_here.position == Vector2(10, 17), "Restart here button position is (10, 17)") _assert(death_ui._btn_restart_town.position == Vector2(10, 47), "Restart town button position is (10, 47)") death_ui.queue_free() client.queue_free() func _test_10s_cooldown_countdown() -> void: print("\n--- Test 2: 10s Restart Here Cooldown Protection (cmd_general.cpp) ---") var client := MockClient.new() get_root().add_child(client) var death_ui := DeathUI.new() get_root().add_child(death_ui) death_ui.setup(client, get_root()) death_ui.show_dialog() _assert(death_ui._btn_restart_here.disabled == true, "Restart here button initially disabled") _assert("10 秒" in death_ui._btn_restart_here.text, "Displays initial 10s cooldown") # Advance 5.0 seconds death_ui._process(5.0) _assert(death_ui._btn_restart_here.disabled == true, "Still disabled at 5 seconds") _assert("5 秒" in death_ui._btn_restart_here.text, "Countdown shows 5 seconds remaining") # Advance remaining 5.0 seconds death_ui._process(5.1) _assert(death_ui._btn_restart_here.disabled == false, "Enabled once countdown reaches 0") _assert(death_ui._btn_restart_here.text == "原地复活", "Text becomes '原地复活' with no cooldown suffix") death_ui.queue_free() client.queue_free() func _test_restart_commands() -> void: print("\n--- Test 3: Restart Chat Commands (/restart_town & /restart_here) ---") var client := MockClient.new() get_root().add_child(client) var death_ui := DeathUI.new() get_root().add_child(death_ui) death_ui.setup(client, get_root()) death_ui.show_dialog() # Restart town button is clickable immediately death_ui._btn_restart_town.pressed.emit() _assert(client.sent_chat_commands.size() == 1, "Town button sent command") _assert(client.sent_chat_commands[0] == "/restart_town", "Sent command is '/restart_town'") # Restart here button while in cooldown should NOT send packet death_ui._here_cooldown_remaining = 8.0 death_ui._on_restart_here_pressed() _assert(client.sent_chat_commands.size() == 1, "No command sent while restart_here in cooldown") # Restart here button after cooldown sends /restart_here death_ui._here_cooldown_remaining = 0.0 death_ui._on_restart_here_pressed() _assert(client.sent_chat_commands.size() == 2, "Restart here sent command after cooldown") _assert(client.sent_chat_commands[1] == "/restart_here", "Sent command is '/restart_here'") death_ui.queue_free() client.queue_free() func _test_auto_close_on_revive() -> void: print("\n--- Test 4: Auto-Close on Revive ---") var client := MockClient.new() get_root().add_child(client) var death_ui := DeathUI.new() get_root().add_child(death_ui) death_ui.setup(client, get_root()) death_ui.show_dialog() _assert(death_ui.is_open() == true, "Death dialog open") # Simulate player revived (HP > 0, dead = false) client.entity_data = {"hp": 100, "dead": false} client.vitals_changed.emit(1) _assert(death_ui.is_open() == false, "Death dialog automatically hides when player is revived") death_ui.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)