- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
162 lines
5.6 KiB
GDScript
162 lines
5.6 KiB
GDScript
# 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_dead_phase_is_not_a_ui_entry()
|
|
_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_dead_phase_is_not_a_ui_entry() -> void:
|
|
print("\n--- Test 1b: PHASE_DEAD is consumed without a phase transition ---")
|
|
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())
|
|
# 40250 RecvPhasePacket(PHASE_DEAD) intentionally does not call a phase
|
|
# setter. The dialog must be entered by GC_DEAD/entity_dead instead.
|
|
client.phase_changed.emit("dead")
|
|
_assert(not death_ui.is_open(), "phase_changed('dead') does not open RestartDialog")
|
|
client.entity_dead.emit(1)
|
|
_assert(death_ui.is_open(), "GC_DEAD/entity_dead opens RestartDialog")
|
|
|
|
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)
|