diff --git a/scenes/worlds/test_level.tscn b/scenes/worlds/test_level.tscn index 9935db9..f9ab25b 100644 --- a/scenes/worlds/test_level.tscn +++ b/scenes/worlds/test_level.tscn @@ -1,4 +1,6 @@ -[gd_scene load_steps=1 format=3] +[gd_scene load_steps=2 format=3] + +[ext_resource type="PackedScene" path="res://scenes/player.tscn" id="1_player"] [node name="TestLevel" type="Node3D"] @@ -44,6 +46,10 @@ environment = SubResource("env_bg") [node name="PlayerSpawn" type="Marker3D" parent="."] position = Vector3(0, 1.0, 0) +[node name="Player" type="CharacterBody3D" parent="."] +position = Vector3(0, 1.0, 0) +script = ExtResource("1_player") + [sub_resource type="BoxMesh" id="mesh_ground"] size = Vector3(40, 1, 40) diff --git a/scripts/smoke_test_player.gd b/scripts/smoke_test_player.gd new file mode 100644 index 0000000..04d63b2 --- /dev/null +++ b/scripts/smoke_test_player.gd @@ -0,0 +1,75 @@ +extends SceneTree +## Smoke test for the player character scene and its spawn behavior. +## +## Run with: +## godot --headless --path . --script res://scripts/smoke_test_player.gd +## +## Loads the test level, verifies the player is present, spawned at the +## PlayerSpawn marker, and has the expected CharacterBody3D structure. + +var passed := 0 +var failed := 0 + + +func _initialize() -> void: + _run() + + +func _run() -> void: + await process_frame + + # Load the test level scene (which now includes the player). + var packed: PackedScene = load("res://scenes/worlds/test_level.tscn") + var level: Node3D = packed.instantiate() + root.add_child(level) + await process_frame + + # 1. Verify the player node exists in the scene tree. + var player := level.get_node("Player") + _check("Player node exists in test level", player != null) + _check("Player is a CharacterBody3D", player is CharacterBody3D) + + # 2. Verify the player has the expected child structure. + var collision := player.get_node("CollisionShape3D") + _check("Player has CollisionShape3D", collision != null) + _check("CollisionShape3D has a shape", collision.shape != null) + + var mesh := player.get_node("CapsuleMesh") + _check("Player has CapsuleMesh", mesh != null) + _check("CapsuleMesh has a mesh", mesh.mesh != null) + + # 3. Verify the player spawned at the PlayerSpawn marker position. + var spawn_marker := level.get_node("PlayerSpawn") + _check("PlayerSpawn marker exists", spawn_marker != null) + + # The player's _ready() should have moved it to the spawn marker's + # global position. Since both are direct children of the root, their + # global positions equal their local positions. + var expected_pos := spawn_marker.global_position + _check("Player spawned at PlayerSpawn position", + player.global_position == expected_pos) + + # 4. Verify the player script class_name is correct. + var script := player.get_script() + _check("Player has a script attached", script != null) + _check("Player script is Player class", script is GDScript) + + # 5. Verify the player has movement constants defined. + _check("Player has WALK_SPEED constant", + script.get_constant("WALK_SPEED") != null) + _check("Player has GRAVITY constant", + script.get_constant("GRAVITY") != null) + _check("Player has JUMP_VELOCITY constant", + script.get_constant("JUMP_VELOCITY") != null) + + print("\n=== SMOKE RESULT: %d/%d passed ===" % [passed, passed + failed]) + quit(0 if failed == 0 else 1) + + +func _check(description: String, condition: bool) -> void: + if condition: + print(" PASS: ", description) + passed += 1 + else: + printerr(" FAIL: ", description) + failed += 1