extends SceneTree ## Regression test for world chunk alignment and ground positioning. var _passed: int = 0 var _failed: int = 0 func _check(label: String, condition: bool) -> void: if condition: print(" PASS: %s" % label) _passed += 1 else: printerr(" FAIL: %s" % label) _failed += 1 func _init() -> void: call_deferred("_run_tests") func _run_tests() -> void: print("--- World Chunk & Ground Alignment Test ---") await process_frame var packed: PackedScene = load("res://scenes/game.tscn") var result := change_scene_to_packed(packed) if result != OK: printerr("SMOKE: failed to change scene, error ", result) quit(1) return await scene_changed var game: Node3D = current_scene as Node3D _check("game scene ready", game != null) # Initially, ground is at default test level position (0, -0.5, 0) var ground: StaticBody3D = game.get_node_or_null("Level/Ground") as StaticBody3D _check("Ground initially exists", ground != null) _check("Ground initially at (0, -0.5, 0)", ground.position.is_equal_approx(Vector3(0, -0.5, 0))) # Create a ChunkScene for chunk (0, 0, 0) with corner objects var chunk_scene := ChunkScene.new() chunk_scene.coord = Vector3i(0, 0, 0) chunk_scene.name = "chunk_0_0_0" game.add_child(chunk_scene) # Add lamp post at corner (0.5, 0.5) and corner (31.5, 31.5) var obj_corner1 := WorldObject.new() obj_corner1.id = "lamp1" obj_corner1.type = "prop" obj_corner1.position = Vector3(0.5, 0.5, 0.0) obj_corner1.properties = {"catalog_id": "lamp_post_01"} chunk_scene.add_object(obj_corner1) var obj_corner2 := WorldObject.new() obj_corner2.id = "lamp2" obj_corner2.type = "prop" obj_corner2.position = Vector3(31.5, 31.5, 0.0) obj_corner2.properties = {"catalog_id": "lamp_post_01"} chunk_scene.add_object(obj_corner2) # Trigger _on_chunk_loaded game.call("_on_chunk_loaded", Vector3i(0, 0, 0), chunk_scene) # Verify Ground has moved to center of chunk: (16.0, -0.5, 16.0) _check("Ground moved to chunk center (16, -0.5, 16)", ground.position.is_equal_approx(Vector3(16.0, -0.5, 16.0))) var shape_node := ground.find_child("CollisionShape3D", true, false) as CollisionShape3D _check("Ground CollisionShape3D size >= 40x40", shape_node != null and (shape_node.shape as BoxShape3D).size.x >= 40.0 and (shape_node.shape as BoxShape3D).size.z >= 40.0) var mesh_node := ground.find_child("MeshInstance3D", true, false) as MeshInstance3D _check("Ground MeshInstance3D size >= 40x40", mesh_node != null and (mesh_node.mesh as BoxMesh).size.x >= 40.0 and (mesh_node.mesh as BoxMesh).size.z >= 40.0) # Verify corners are inside ground bounds var half_x: float = (shape_node.shape as BoxShape3D).size.x * 0.5 var half_z: float = (shape_node.shape as BoxShape3D).size.z * 0.5 var min_gx: float = ground.position.x - half_x var max_gx: float = ground.position.x + half_x var min_gz: float = ground.position.z - half_z var max_gz: float = ground.position.z + half_z _check("Corner (0.5, 0.5) is on ground", 0.5 >= min_gx and 0.5 <= max_gx and 0.5 >= min_gz and 0.5 <= max_gz) _check("Corner (31.5, 31.5) is on ground", 31.5 >= min_gx and 31.5 <= max_gx and 31.5 >= min_gz and 31.5 <= max_gz) # Verify PlayerSpawn defaulted to chunk center (16, 1.5, 16) var spawn_marker := game.get_node_or_null("Level/PlayerSpawn") as Node3D _check("PlayerSpawn marker is at (16, 1.5, 16)", spawn_marker != null and spawn_marker.global_position.is_equal_approx(Vector3(16.0, 1.5, 16.0))) # Now test placed spawn_point object var chunk_scene2 := ChunkScene.new() chunk_scene2.coord = Vector3i(0, 0, 0) chunk_scene2.name = "chunk_with_spawn" game.add_child(chunk_scene2) var spawn_obj := WorldObject.new() spawn_obj.id = "spawn1" spawn_obj.type = "spawn_point" spawn_obj.position = Vector3(8.5, 12.5, 0.0) spawn_obj.properties = {"catalog_id": "spawn_point"} chunk_scene2.add_object(spawn_obj) game.call("_on_chunk_loaded", Vector3i(0, 0, 0), chunk_scene2) _check("PlayerSpawn moved to placed spawn_point at (8.5, 1.5, 12.5)", spawn_marker != null and spawn_marker.global_position.is_equal_approx(Vector3(8.5, 1.5, 12.5))) print("\n=== SMOKE RESULT: %d/%d passed ===" % [_passed, _passed + _failed]) if _failed > 0: quit(1) else: quit(0)