# dungeon_state_test —— GC_DUNGEON 目的地状态回归。 # godot --headless --path project --script dungeon_state_test.gd extends SceneTree const DungeonState = preload("res://dungeon_state.gd") class FakeClient extends Node: signal dungeon_event(subheader: int, x: int, y: int, has_destination: bool) signal world_reset var _fail := 0 func _ck(condition: bool, message: String) -> void: if not condition: _fail += 1 printerr("FAIL: " + message) func _init() -> void: await _run() if _fail == 0: print("PASS: dungeon_state_test (GC_DUNGEON destination / arrival / reset)") quit(0) else: printerr("%d check(s) failed" % _fail) quit(1) func _run() -> void: MapCoord.set_base(Vector2.ZERO) var client := FakeClient.new() var player := Node3D.new() get_root().add_child(client) get_root().add_child(player) var state := DungeonState.new() get_root().add_child(state) state.setup(client, func() -> Node3D: return player) await process_frame var changes: Array = [] var alarms: Array = [] state.destination_changed.connect(func(active: bool, pos: Vector3): changes.append([active, pos])) state.alarm_requested.connect(func(pos: Vector3): alarms.append(pos)) client.dungeon_event.emit(state.TIME_ATTACK_START, 0, 0, false) _ck(not state.active and state.last_subheader == state.TIME_ATTACK_START, "TIME_ATTACK_START is consumed without inventing a timer") client.dungeon_event.emit(state.DESTINATION_POSITION, 3210, 6540, true) _ck(state.active, "destination packet activates route") _ck(state.destination_cm == Vector2(3210, 6540), "destination keeps server cm") _ck(state.destination_world.distance_to(Vector3(32.1, 0.0, 65.4)) < 0.01, "destination converts global cm through MapCoord") _ck(changes.size() == 1 and changes[0][0] == true, "destination_changed opens target") _ck(alarms.size() == 1, "destination alarms immediately") # Reference uses Manhattan < 10000 cm, so exactly 100m is not complete and # 99m is complete. Keep the player in the same global coordinate frame. player.global_position = Vector3(32.1, 0.0, 65.4 - 100.0) state._process(0.0) _ck(state.active, "Manhattan distance exactly 100m remains active") player.global_position = Vector3(32.1, 0.0, 65.4 - 99.0) state._process(0.0) _ck(not state.active, "Manhattan distance below 100m completes route") _ck(changes.size() == 2 and changes[1][0] == false, "arrival closes target") client.dungeon_event.emit(state.DESTINATION_POSITION, 10000, 20000, true) _ck(state.active, "second destination reopens route") client.world_reset.emit() _ck(not state.active and state.destination_cm == Vector2.ZERO and changes.size() == 4, "world reset clears dungeon destination") client.queue_free() player.queue_free() state.queue_free()