extends SceneTree ## Boundary test: tries to escape the valley in several directions by walking ## and spamming jump at the mountain rim, and fails if the player ever gets past ## the invisible wall, falls off the map, or ends up stuck below the waterline ## (in a pit near the edge). ## ## Then the same on a worldlet with a beach (W2, tests/worldlets/testbay.json): ## off the beach you swim out into the sea, and the current must turn you back ## before the wall (being afloat in the open sea there is fine). ## ## Run: godot --headless --path . --fixed-fps 30 --script res://tests/test_boundary.gd const DIRECTIONS := 24 const BAY_DIRECTIONS := 8 # (spread over the beach and its headlands) const SECONDS_PER_TRY := 25.0 var _failures := 0 var _tries := 0 func _initialize() -> void: _run.call_deferred() func _run() -> void: var S: Node = root.get_node("Saves") S.root_dir = "user://test_boundary_worlds" for w in S.list_worlds(): S.delete_world(w.id) print("the Valley") await _escape_all(DIRECTIONS, -1.0) print("a beach (testbay)") S.current_id = S.create_world("Bay", 1337, {"worldlet": "testbay"}) await _escape_all(BAY_DIRECTIONS, 90.0) print("the Cove (all round)") S.current_id = S.create_world("Cove", 1337, {"worldlet": "cove"}) await _escape_all(DIRECTIONS, -1.0) for w in S.list_worlds(): S.delete_world(w.id) DirAccess.remove_absolute(S.root_dir) print("RESULT: %s (%d/%d directions held)" % ["PASS" if _failures == 0 else "FAIL", _tries - _failures, _tries]) quit(1 if _failures > 0 else 0) ## Walks out in `count` directions: all round, or (beach_deg >= 0) across the ## beach's arc and a little past it either side. func _escape_all(count: int, beach_deg: float) -> void: var world: Node3D = load("res://scenes/world.tscn").instantiate() root.add_child(world) for _i in range(10): await physics_frame var terrain: Node3D = world.get_node("Terrain") var player: CharacterBody3D = world.get_node("Player") var tick := 1.0 / Engine.physics_ticks_per_second var pulled := 0 for i in range(count): var ang := TAU * i / count if beach_deg >= 0.0: ang = deg_to_rad(beach_deg + lerpf(-65.0, 65.0, float(i) / (count - 1))) var out := Vector3(cos(ang), 0.0, sin(ang)) var start: Vector3 = out * (terrain.play_radius - 12.0) start.y = maxf(terrain.sample_height(start.x, start.z), terrain.water_level) + 1.0 player.global_position = start player.velocity = Vector3.ZERO player.rotation = Vector3(0.0, atan2(-out.x, -out.z), 0.0) Input.action_press("move_forward") var escaped := false var max_d := 0.0 var in_current := false var frames := int(SECONDS_PER_TRY / tick) for f in range(frames): if f % 12 == 0: Input.action_press("jump") elif f % 12 == 1: Input.action_release("jump") await physics_frame var p := player.global_position max_d = maxf(max_d, Vector2(p.x, p.z).length()) in_current = in_current or terrain.current_at(p.x, p.z) > 0.2 if not terrain.inside_boundary(p.x, p.z, -1.5) or p.y < terrain.min_height - 5.0: escaped = true break Input.action_release("move_forward") Input.action_release("jump") var p := player.global_position var at_sea: bool = world.water_at(p.x, p.z).get("type", "") == "sea" if not escaped and p.y < terrain.water_level - 0.5 and not at_sea: escaped = true # stuck in a pit below the waterline if in_current: pulled += 1 print("dir %2d (%3d deg): %s max radius %.1f final (%.1f, %.1f, %.1f)%s" % [ i, int(rad_to_deg(ang)), "FAILED" if escaped else "held", max_d, p.x, p.y, p.z, " (the current turned them back)" if in_current else ""]) _tries += 1 if escaped: _failures += 1 if beach_deg >= 0.0 and pulled == 0: print("FAILED: nobody swam far enough out to meet the current") _failures += 1 _tries += 1 world.queue_free() for _i in range(3): await process_frame