# test_no_auto_move_regression.gd # Regression test suite verifying that the character does not move automatically # without user input under any circumstances (target picking, skill casting, attack key release). extends SceneTree const PlayerCtl = preload("res://player_controller.gd") const NetPlay = preload("res://net_play.gd") const NetWorld = preload("res://net_world.gd") var _failed := 0 func _check(condition: bool, msg: String) -> void: if condition: print(" [PASS] %s" % msg) else: print(" [FAIL] %s" % msg) _failed += 1 class MockClient extends Node: signal entity_main_set(vid: int) signal entity_moved(vid: int) signal points_changed(points: Dictionary) signal vitals_changed(vid: int) signal target_info(vid: int, hp_percent: int) signal entity_despawned(vid: int) signal entity_dead(vid: int) var ents: Dictionary = {} func get_entity(vid: int) -> Dictionary: return ents.get(vid, {}) func is_in_game() -> bool: return true func set_target(_vid: int) -> void: pass func move(_func_id: int, _arg: int, _rot: int, _x: int, _y: int) -> void: pass func fishing(_rot: float) -> bool: return false func _init() -> void: print("=== Running test_no_auto_move_regression ===") _test_pick_actor_engages_auto_attack_and_can_cancel() _test_skill_cast_clears_movement_and_reservation() _test_spacebar_attack_release_stops_movement() _test_manual_movement_clears_reservation() _test_clear_target_stops_pc() if _failed == 0: print("=== ALL NO-AUTO-MOVE REGRESSION TESTS PASSED (0 failures) ===") quit(0) else: printerr("=== TESTS FAILED: %d errors ===" % _failed) quit(1) func _setup_rig() -> Dictionary: var client := MockClient.new() var net_world := NetWorld.new() var pc := PlayerCtl.new() var player_node := Node3D.new() player_node.position = Vector3(0, 0, 0) pc.player = player_node pc.active = true var np := NetPlay.new() np.setup(client, pc, net_world) # Main player entity client.ents[1000] = { "vid": 1000, "kind": 0, "empire": 1, "x": 0, "y": 0, "z": 0, "dead": false, "in_safe": false } np._main_vid = 1000 # Mob entity at (20, 0, 0) var mob_node := Node3D.new() mob_node.position = Vector3(20, 0, 0) mob_node.set_meta("vid", 2000) net_world._by_vid[2000] = mob_node client.ents[2000] = { "vid": 2000, "kind": 2, # KIND_MONSTER "empire": 0, "x": 2000, "y": 0, "z": 0, "dead": false, "in_safe": false } return { "client": client, "net_world": net_world, "pc": pc, "player_node": player_node, "np": np, "mob_node": mob_node } func _cleanup_rig(rig: Dictionary) -> void: rig.mob_node.free() rig.player_node.free() rig.pc.free() rig.np.free() rig.net_world.free() rig.client.free() # 1. Clicking an enemy sets _auto_attack_vid to target (40250 __SetAutoAttackTargetActorID) func _test_pick_actor_engages_auto_attack_and_can_cancel() -> void: print("\n--- Test 1: Actor pick engages auto attack (40250 parity) and can be cancelled ---") var rig := _setup_rig() var np: NetPlay = rig.np var mob_node: Node3D = rig.mob_node # Simulate clicking the mob np._on_pick(mob_node) _check(np._target_vid == 2000, "Target VID is set to picked mob") _check(np._auto_attack_vid == 2000, "_auto_attack_vid is set to mob VID for auto attack") # Manual movement (WASD) cancels auto-attack np.on_manual_move() _check(np._auto_attack_vid == 0, "_auto_attack_vid cleared on manual move") _cleanup_rig(rig) # 2. Casting a skill must stop character movement, clear reservations, and not resume after finish func _test_skill_cast_clears_movement_and_reservation() -> void: print("\n--- Test 2: Skill cast clears movement and does not resume after skill ends ---") var rig := _setup_rig() var np: NetPlay = rig.np var pc: PlayerCtl = rig.pc var player_node: Node3D = rig.player_node var mob_node: Node3D = rig.mob_node # Player targets mob and begins moving towards it np._on_pick(mob_node) pc.walk_to(Vector3(15, 0, 0)) _check(pc.is_going() == true, "pc is walking before skill cast") # Cast skill (e.g. warrior mental skill) np.start_skill_cast(0.5, false) _check(pc.is_going() == false, "pc.is_going() is immediately stopped on skill cast") _check(np._reserved_mode == NetPlay.ReservedMode.NONE, "np._reserved_mode cleared on skill cast") _check(np._auto_attack_vid == 0, "np._auto_attack_vid is 0 on skill cast") var initial_pos := player_node.position # Simulate frames during skill lock for i in 5: np._process(0.1) pc._process(0.1) _check(player_node.position == initial_pos, "player remains stationary during skill cast") # Simulate skill timeout / unlocking np._local_time += 1.0 _check(np.is_lock() == false, "np is unlocked after skill duration") # Simulate frames AFTER skill completes (no player input) for i in 10: np._process(0.1) pc._process(0.1) _check(pc.is_going() == false, "pc.is_going() remains false after skill completion") _check(np._reserved_mode == NetPlay.ReservedMode.NONE, "reservation remains NONE after skill completion") _check(np._auto_attack_vid == 0, "_auto_attack_vid remains 0 after skill completion") _check(player_node.position == initial_pos, "player position unchanged (no spontaneous movement!)") _cleanup_rig(rig) # 3. Spacebar attack release stops movement and clears auto-attack func _test_spacebar_attack_release_stops_movement() -> void: print("\n--- Test 3: Releasing attack key (Spacebar) cancels auto-attack and stops movement ---") var rig := _setup_rig() var np: NetPlay = rig.np var pc: PlayerCtl = rig.pc np._target_vid = 2000 np.set_attack_key(true) np._update_auto_attack() _check(np._auto_attack_vid == 2000, "_auto_attack_vid set while holding space with target") # Releasing attack key np.set_attack_key(false) _check(np._auto_attack_vid == 0, "_auto_attack_vid cleared on spacebar release") _check(np._reserved_mode == NetPlay.ReservedMode.NONE, "_reserved_mode cleared on spacebar release") _check(pc.is_going() == false, "pc stopped on spacebar release") _cleanup_rig(rig) # 4. Manual movement (WASD) cancels any pending reserved actions and auto-attack func _test_manual_movement_clears_reservation() -> void: print("\n--- Test 4: Manual movement cancels reservations and auto-attack ---") var rig := _setup_rig() var np: NetPlay = rig.np var pc: PlayerCtl = rig.pc np._reserve_click_actor(2000) np._auto_attack_vid = 2000 _check(np._reserved_mode == NetPlay.ReservedMode.CLICK_ACTOR, "Reserved mode is CLICK_ACTOR") # Player presses WASD -> on_manual_move() called np.on_manual_move() _check(np._reserved_mode == NetPlay.ReservedMode.NONE, "on_manual_move cleared reserved action") _check(np._auto_attack_vid == 0, "on_manual_move cleared auto-attack") _cleanup_rig(rig) # 5. Clearing target stops pc func _test_clear_target_stops_pc() -> void: print("\n--- Test 5: Clearing target stops player controller ---") var rig := _setup_rig() var np: NetPlay = rig.np var pc: PlayerCtl = rig.pc np._target_vid = 2000 pc.walk_to(Vector3(10, 0, 0)) _check(pc.is_going() == true, "pc is going towards destination") np._clear_target() _check(np._target_vid == 0, "target vid is cleared") _check(pc.is_going() == false, "pc.is_going() cleared on target clear") _check(np._auto_attack_vid == 0, "_auto_attack_vid is 0") _check(np._reserved_mode == NetPlay.ReservedMode.NONE, "reserved mode is NONE") _cleanup_rig(rig)