## Tests for camera anchor behavior — the camera must show the player's ## position from the very first rendered frame, with no visible drift from (0,0). ## ## These tests verify the full pipeline: SimBridge → GameState → Camera2D. ## The player starts at (10,10) in test mode, so the expected camera position ## is (10,10) * TILE_SIZE = (320, 320). class_name TestCameraAnchor extends GdUnitTestSuite const EXPECTED_PLAYER_POS := Vector2(10, 10) const EXPECTED_CAMERA_POS := Vector2(320, 320) # 10 * 32, 10 * 32 var _instance: Node = null func before_test() -> void: SimBridge.reset_test_state() GameState.current_tick = 0 GameState.player_position = Vector2.ZERO GameState.visible_entities = [] GameState.visible_tiles = [] GameState.visible_positions = {} GameState.current_monologue = null GameState.current_dialogue = null func after_test() -> void: if _instance and is_instance_valid(_instance): _instance.queue_free() _instance = null # --- Pipeline precondition tests --- func test_simbridge_connected_in_test_mode() -> void: SimBridge.state = SimBridge.ConnectionState.DISCONNECTED SimBridge.connect_to_sim() assert_that(SimBridge.state).is_equal(SimBridge.ConnectionState.CONNECTED) func test_poll_snapshot_returns_data_when_connected() -> void: SimBridge.connect_to_sim() var snapshot: Variant = SimBridge.poll_snapshot() assert_that(snapshot).is_not_null() assert_that(snapshot is Dictionary).is_true() func test_snapshot_has_player_at_10_10() -> void: var snap := SimBridge._test_snapshot() var player_x: float = -1.0 var player_y: float = -1.0 for entity in snap.entities: if entity.kind.variant == "Player": player_x = entity.x player_y = entity.y break assert_that(player_x).is_equal(10.0) assert_that(player_y).is_equal(10.0) func test_apply_snapshot_sets_player_position() -> void: var snap := SimBridge._test_snapshot() GameState.apply_snapshot(snap) assert_that(GameState.player_position).is_equal(EXPECTED_PLAYER_POS) # --- Camera anchor after _ready() --- func test_camera_position_after_ready() -> void: var scene := load("res://scenes/main.tscn") _instance = scene.instantiate() auto_free(_instance) add_child(_instance) var camera: Camera2D = _instance.get_node("Camera2D") assert_that(camera.global_position).is_equal(EXPECTED_CAMERA_POS) func test_camera_anchored_flag_after_ready() -> void: var scene := load("res://scenes/main.tscn") _instance = scene.instantiate() auto_free(_instance) add_child(_instance) assert_that(_instance._camera_anchored).is_true() func test_camera_smoothing_off_after_ready() -> void: # Camera smoothing must be disabled during init to prevent lerp from (0,0). # If this test fails, the camera will visibly drift from origin to player. var scene := load("res://scenes/main.tscn") _instance = scene.instantiate() auto_free(_instance) add_child(_instance) var camera: Camera2D = _instance.get_node("Camera2D") assert_that(camera.position_smoothing_enabled).is_false() func test_camera_smoothing_stays_off_with_manual_lerp() -> void: # #117: Manual lerp approach — Godot's built-in smoothing must stay OFF always. # CAMERA_SMOOTHING_SPEED is used as the lerp weight, not Godot's position_smoothing_speed. var scene := load("res://scenes/main.tscn") _instance = scene.instantiate() auto_free(_instance) add_child(_instance) _instance._process(0.016) var camera: Camera2D = _instance.get_node("Camera2D") assert_that(camera.position_smoothing_enabled).is_false() # --- Camera behavior across frames --- func test_camera_tracks_player_after_process() -> void: var scene := load("res://scenes/main.tscn") _instance = scene.instantiate() auto_free(_instance) add_child(_instance) # Simulate one game frame _instance._process(0.016) var camera: Camera2D = _instance.get_node("Camera2D") var expected := GameState.player_position * Constants.TILE_SIZE assert_that(camera.global_position).is_equal(expected) func test_camera_lerps_toward_player_movement() -> void: # #117: With manual lerp, camera moves TOWARD player position (not snapping). # After one 16ms frame the camera should be partway between old and new position. var scene := load("res://scenes/main.tscn") _instance = scene.instantiate() auto_free(_instance) add_child(_instance) var camera: Camera2D = _instance.get_node("Camera2D") var initial_pos := camera.global_position # anchored at (320, 320) # Move player north via SimBridge test mode SimBridge._test_input_queue.append("MoveNorth") _instance._process(0.016) var new_target := GameState.player_position * Constants.TILE_SIZE # (320, 288) # Camera should have moved north (lower y) but NOT reached the target yet assert_that(camera.global_position.y).is_less(initial_pos.y) assert_that(camera.global_position.y).is_greater(new_target.y)