extends GutTest ## Movement is the one function the client is allowed to run ahead of the ## server, so client prediction is only correct while these hold. var map: MapGrid func before_each() -> void: # A plain walled room, big enough that these tests are about movement # rather than about walls. map = MapGrid.new(42, 24, MapGrid.Kind.WALL) map.fill_rect(Rect2i(1, 1, 40, 22), MapGrid.Kind.FLOOR) map.centre_on_origin() func test_diagonal_is_not_faster_than_cardinal() -> void: var straight := Movement.step_player(Vector2.ZERO, Vector2(1, 0), SimConfig.PLAYER_SPEED, map) var diagonal := Movement.step_player(Vector2.ZERO, Vector2(1, 1), SimConfig.PLAYER_SPEED, map) assert_almost_eq(diagonal.length(), straight.length(), 0.001, "a diagonal must cover the same distance as a cardinal move") func test_oversized_input_vector_is_clamped() -> void: # The wire format cannot express this, but a patched client could try. var cheated := Movement.step_player(Vector2.ZERO, Vector2(1000, 0), SimConfig.PLAYER_SPEED, map) var honest := Movement.step_player(Vector2.ZERO, Vector2(1, 0), SimConfig.PLAYER_SPEED, map) assert_eq(cheated, honest, "an over-long move vector must buy no extra speed") func test_speed_matches_config() -> void: var moved := Movement.step_player(Vector2.ZERO, Vector2.RIGHT, SimConfig.PLAYER_SPEED, map) assert_almost_eq(moved.x, SimConfig.PLAYER_SPEED * SimConfig.TICK_DELTA, 0.001) func test_a_player_cannot_walk_through_the_map_edge() -> void: var far := Vector2(map.world_rect().end.x - MapGrid.TILE * 1.5, 0.0) var out := Movement.step_player(far, Vector2.RIGHT, 10000.0, map) assert_lt(out.x, map.world_rect().end.x - MapGrid.TILE, "the border wall has to stop even an absurd step") func test_circles_overlap_at_the_boundary() -> void: assert_true(Movement.circles_overlap(Vector2.ZERO, 5.0, Vector2(9.9, 0.0), 5.0)) assert_false(Movement.circles_overlap(Vector2.ZERO, 5.0, Vector2(10.1, 0.0), 5.0)) func test_outside_map_respects_the_cull_margin() -> void: var edge := map.world_rect().end.x + SimConfig.BULLET_CULL_MARGIN assert_false(Movement.outside_map(Vector2(edge - 1.0, 0.0), map)) assert_true(Movement.outside_map(Vector2(edge + 1.0, 0.0), map)) ## Out-of-map death is derivable from the map's dimensions, which every client ## is told; wall death is not, because tiles are streamed. Only the second one ## needs announcing, and conflating them would either desync bullets or double ## the despawn traffic. func test_map_bounds_and_wall_deaths_are_distinguishable() -> void: var outside := Vector2(map.world_rect().end.x + 100.0, 0.0) assert_true(Movement.outside_map(outside, map)) var in_wall := map.tile_centre(0, 12) assert_false(Movement.outside_map(in_wall, map), "a border wall is inside the map") assert_true(Movement.bullet_stopped(in_wall, map))