extends SceneTree const Zombie = preload("res://scripts/zombie.gd") var world: Node3D var player: Node3D func _initialize() -> void: call_deferred("run_test") func _freeze_others(actor: Node = null) -> void: for zombie in get_nodes_in_group("live_zombies"): if zombie != actor: zombie.set_physics_process(false) func run_test() -> void: world = load("res://scenes/main.tscn").instantiate() root.add_child(world) player = world.player player.set_physics_process(false) player.touch_mode = true _freeze_others() player.hp = 20 player.ammo = 3 world.hud.update_status(player, 4, 1200) assert(world.hud.health_value.text == "20") assert(world.hud.ammo_value.text == "03 / 30") assert(world.hud.health_value.get_theme_color("font_color") == world.hud.DANGER) assert(not world.hud.help.visible) print("PASS: HUD numeri, vita bassa, caricatore e modalita' touch") player.hp = 100 player.ammo = 1 player.shoot() assert(player.ammo == 0 and player.reloading) var release_count := 0 for speaker in player.get_children(): if speaker is AudioStreamPlayer and speaker.stream.resource_path.ends_with("cliprelease1.wav"): release_count += 1 player.start_reload() # must not add another reload var repeated_count := 0 for speaker in player.get_children(): if speaker is AudioStreamPlayer and speaker.stream.resource_path.ends_with("cliprelease1.wav"): repeated_count += 1 assert(repeated_count == release_count) await create_timer(1.7).timeout assert(player.ammo == 30 and not player.reloading) print("PASS: ultimo colpo avvia auto-reload e completa a 30") # Shared grid route: actual physics movement, not just returned waypoints. Engine.time_scale = 4 var cases := [ [Vector3(-5, 0.1, -6.5), Vector3(-5, 0.1, 0)], [Vector3(5, 0.1, 1.5), Vector3(5, 0.1, 7.5)], [Vector3(-6, 0.1, 4), Vector3(-2, 0.1, 4)], [Vector3(6.8, 0.1, -6), Vector3(6.8, 0.1, -2)], ] player.hp = 10000 for positions in cases: player.position = positions[1] var actor := CharacterBody3D.new() actor.set_script(Zombie) actor.player = player actor.navigation = world.navigation actor.position = positions[0] world.add_child(actor) actor.entering = false actor.speed = 3 var reached := false for i in 420: _freeze_others(actor) await physics_frame var delta: Vector3 = actor.position - player.position delta.y = 0 if delta.length() < 1.8: reached = true break assert(reached, "Zombie bloccato: %s -> %s, finale %s" % [positions[0], positions[1], actor.position]) actor.queue_free() await process_frame print("PASS: inseguimento fisico attorno a entrambe le scrivanie, sedie e bidone") Engine.time_scale = 1 # A melee ray across the left desk must not hit through it. player.position = Vector3(-5, 0.1, -3.2) var actor := CharacterBody3D.new() actor.set_script(Zombie) actor.player = player actor.position = Vector3(-5, 0.1, -4.8) world.add_child(actor) actor.entering = false actor.set_physics_process(false) await physics_frame await physics_frame assert(not actor._attack_clear()) var hp_before: float = player.hp actor._attack() assert(player.hp == hp_before) print("PASS: nessun attacco attraverso la scrivania") _freeze_others() player.hp = 100 player.ammo = 1 player.shoot() assert(player.reloading) player.take_damage(1000) var flatlines := 0 for speaker in player.get_children(): if speaker is AudioStreamPlayer and speaker.stream.resource_path.ends_with("flatline.wav"): flatlines += 1 assert(flatlines == 1) player.take_damage(10) flatlines = 0 for speaker in player.get_children(): if speaker is AudioStreamPlayer and speaker.stream.resource_path.ends_with("flatline.wav"): flatlines += 1 assert(flatlines == 1) await create_timer(1.8).timeout assert(player.hp == 0 and player.ammo == 0 and not player.reloading) print("PASS: flatline una volta e auto-reload annullato dalla morte") world.queue_free() await process_frame quit()