# Tests for res://scripts/player.gd (minimal tree: CharacterBody3D + NavigationAgent3D). extends GdUnitTestSuite const PLAYER_SCRIPT: Script = preload("res://scripts/player.gd") func _make_player() -> CharacterBody3D: var body := CharacterBody3D.new() var nav := NavigationAgent3D.new() nav.name = "NavigationAgent3D" body.add_child(nav) body.set_script(PLAYER_SCRIPT) auto_free(body) add_child(body) return body func test_snap_to_server_resets_goal_position_and_velocity() -> void: # Arrange var p := _make_player() var target := Vector3(1.0, 0.9, -2.0) # Act p.snap_to_server(target) # Assert assert_that(p.global_position).is_equal(target) assert_that(p.velocity).is_equal(Vector3.ZERO) assert_that(p.get("_has_walk_goal")).is_false() func test_set_authoritative_nav_goal_sets_goal_and_target() -> void: # Arrange var p := _make_player() p.set("_idle_anchor_active", true) var goal := Vector3(10.0, 0.0, 3.0) # Act p.set_authoritative_nav_goal(goal) # Assert assert_that(p.get("_has_walk_goal")).is_true() assert_that(p.get("_idle_anchor_active")).is_false() assert_that(p.get("_auth_walk_goal")).is_equal(goal) var nav: NavigationAgent3D = p.get_node("NavigationAgent3D") as NavigationAgent3D assert_that(nav.target_position).is_equal(goal) func test_set_locomotion_wish_world_xz_normalizes_horizontal() -> void: # Arrange var p := _make_player() # Act p.call("set_locomotion_wish_world_xz", Vector3(3.0, 9.0, 4.0)) # Assert var w: Vector3 = p.get("_locomotion_wish_world_xz") as Vector3 assert_that(w.y).is_equal(0.0) assert_that(absf(w.length_squared() - 1.0)).is_less(0.0001) func test_set_locomotion_wish_world_xz_zero_for_near_zero() -> void: # Arrange var p := _make_player() # Act p.call("set_locomotion_wish_world_xz", Vector3(0.0, 1.0, 0.0)) # Assert assert_that(p.get("_locomotion_wish_world_xz") as Vector3).is_equal(Vector3.ZERO) func test_clear_nav_goal_clears_velocity_and_nav_target() -> void: # Arrange var p := _make_player() p.velocity = Vector3(1.0, 0.0, 0.0) p.set_authoritative_nav_goal(Vector3(5.0, 0.0, 5.0)) # Act p.clear_nav_goal() # Assert assert_that(p.velocity).is_equal(Vector3.ZERO) assert_that(p.get("_has_walk_goal")).is_false() assert_that(p.get("_idle_anchor_active")).is_false() var nav: NavigationAgent3D = p.get_node("NavigationAgent3D") as NavigationAgent3D assert_that(nav.target_position).is_equal(p.global_position) func test_nav_goal_lifecycle_resets_vert_route_latch() -> void: # Arrange var p := _make_player() # Act p.set_authoritative_nav_goal(Vector3(1.0, 0.0, 2.0)) p.set("_walk_vert_route_latched", true) p.clear_nav_goal() # Assert assert_that(p.get("_walk_vert_route_latched")).is_false() # Act p.set("_walk_vert_route_latched", true) p.set_authoritative_nav_goal(Vector3(3.0, 0.0, 4.0)) # Assert assert_that(p.get("_walk_vert_route_latched")).is_false() # Act p.set("_walk_vert_route_latched", true) p.snap_to_server(Vector3(0.0, 0.9, 0.0)) # Assert assert_that(p.get("_walk_vert_route_latched")).is_false() func test_hold_idle_anchor_sets_anchor_first_then_clamps_xz() -> void: # Arrange var p := _make_player() p.global_position = Vector3(1.0, 0.5, 2.0) # Act p.call("_hold_idle_anchor") # Assert assert_that(p.get("_idle_anchor_active")).is_true() assert_that(p.get("_idle_anchor_xz")).is_equal(Vector2(1.0, 2.0)) # Act p.global_position = Vector3(1.2, 0.7, 2.3) p.call("_hold_idle_anchor") # Assert assert_that(p.global_position).is_equal(Vector3(1.0, 0.5, 2.0)) func test_snap_to_server_clears_idle_anchor() -> void: # Arrange var p := _make_player() p.set("_idle_anchor_active", true) p.set("_idle_anchor_xz", Vector2(9.0, 9.0)) # Act p.snap_to_server(Vector3(3.0, 0.9, 4.0)) # Assert assert_that(p.get("_idle_anchor_active")).is_false() func test_capsule_feet_y_uses_body_origin_minus_half_height() -> void: # Arrange # Act var feet_y: float = PLAYER_SCRIPT.capsule_feet_y(0.545678, 0.5) # Assert assert_that(absf(feet_y - 0.045678)).is_less(0.000001) func test_vertical_arrival_error_uses_capsule_feet_height() -> void: # Arrange var goal_y := 0.045678 var body_origin_y := 0.545678 # Act var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_origin_y, 0.5) # Assert assert_that(absf(err)).is_less(0.000001) func test_vertical_arrival_error_stays_large_for_origin_height_only_match() -> void: # Arrange var goal_y := 0.545678 var body_origin_y := 0.545678 # Act var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_origin_y, 0.5) # Assert assert_that(absf(err - 0.5)).is_less(0.000001) func test_arrival_vert_err_large_for_step_goal_from_floor() -> void: # Arrange # Player at floor height (body Y=0.9). Goal at step surface (Y=0.3). # Using actual capsule bottom (CAPSULE_HALF_HEIGHT + PLAYER_CAPSULE_RADIUS) the error # must exceed VERT_ARRIVE_EPS so the arrival check does NOT fire prematurely. var goal_y := 0.3 var body_y := 0.9 var full_half: float = ( ( PLAYER_SCRIPT.get("CAPSULE_HALF_HEIGHT") as float + PLAYER_SCRIPT.get("PLAYER_CAPSULE_RADIUS") ) as float ) # Act var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_y, full_half) # Assert assert_that(err).is_greater(PLAYER_SCRIPT.get("VERT_ARRIVE_EPS") as float) func test_arrival_vert_err_zero_when_standing_on_step() -> void: # Arrange # Player body centred at goal_y + total_half_height (i.e. actually standing on the step). # Error must be within VERT_ARRIVE_EPS so arrival fires correctly once the player is up. var goal_y := 0.3 var full_half: float = ( ( PLAYER_SCRIPT.get("CAPSULE_HALF_HEIGHT") as float + PLAYER_SCRIPT.get("PLAYER_CAPSULE_RADIUS") ) as float ) var body_y: float = goal_y + full_half # Act var err: float = PLAYER_SCRIPT.vertical_arrival_error(goal_y, body_y, full_half) # Assert assert_that(err).is_less_equal(PLAYER_SCRIPT.get("VERT_ARRIVE_EPS") as float) func test_flat_floor_goal_is_not_below_feet_height_margin() -> void: # Arrange var goal_y := 0.0 var body_origin_y := 0.4 var descend_margin := PLAYER_SCRIPT.get("DESCEND_GOAL_Y_MARGIN") as float # Act var below_feet_margin: bool = ( goal_y < PLAYER_SCRIPT.capsule_feet_y(body_origin_y, 0.5) - descend_margin ) # Assert assert_that(below_feet_margin).is_false() func test_step_assist_max_surface_delta_matches_navigation_mesh_agent_max_climb() -> void: # Arrange # Keep in sync with NavigationMesh `agent_max_climb` in `scenes/main.tscn`. # Act var delta: float = PLAYER_SCRIPT.get("WALK_STEP_ASSIST_MAX_SURFACE_DELTA") as float # Assert assert_that(delta).is_equal(0.35)