extends SceneTree var detonation_time := -1 func _initialize() -> void: call_deferred("run_test") func run_test() -> void: var world := Node3D.new() root.add_child(world) var room := Node3D.new() room.set_script(load("res://scripts/room.gd")) world.add_child(room) var player := CharacterBody3D.new() player.set_script(load("res://scripts/player.gd")) player.position = Vector3(0, 0.1, 4) world.add_child(player) player.hp = 10000 Input.mouse_mode = Input.MOUSE_MODE_VISIBLE await physics_frame var grenade: RigidBody3D = player.throw_grenade() assert(grenade != null) assert(player.throw_grenade() == null) assert(is_equal_approx(player.grenade_cooldown, 15)) assert(grenade.physics_material_override.bounce > 0) grenade.detonated.connect(func(): detonation_time = Time.get_ticks_msec()) var started := Time.get_ticks_msec() var start := grenade.position await create_timer(0.5).timeout assert(grenade.position.distance_to(start) > 1) assert(grenade.linear_velocity.y < 3) var targets: Array[Node3D] = [] for pos in [Vector3(0, 0, -2), Vector3(2, 0, 0), Vector3(0, 0, -7)]: var target := Node3D.new() target.set_script(load("res://tools/grenade_test_target.gd")) target.position = pos world.add_child(target) targets.append(target) var wall := StaticBody3D.new() wall.position = Vector3(1, 1.5, 0) var shape := CollisionShape3D.new() var box := BoxShape3D.new() box.size = Vector3(0.2, 3, 2) shape.shape = box wall.add_child(shape) world.add_child(wall) # Isolate explosion coverage from the projectile's random rotation/bounces. grenade.freeze = true grenade.global_position = Vector3(0, 0.3, 0) await create_timer(3.15).timeout assert(is_instance_valid(grenade) and not grenade.exploded) await create_timer(0.6).timeout assert(detonation_time >= 0) var seconds := (detonation_time - started) / 1000.0 assert(seconds >= 3.8 and seconds <= 4.3) assert(targets[0].damage_taken > 0) assert(targets[1].damage_taken == 0) # behind a wall assert(targets[2].damage_taken == 0) # beyond blast radius assert(player.hp < 10000) assert(player.throw_grenade() == null) # Wait until the real cooldown expires, without changing the game timer. await create_timer(player.grenade_cooldown + 0.1).timeout assert(player.grenade_cooldown == 0) assert(player.throw_grenade() != null) var main = load("res://scripts/main.gd").new() assert(main.zombies_for_wave(1) == 6) assert(main.zombies_for_wave(2) == 8) assert(main.zombies_for_wave(11) == 26) main.free() print("PASS: thrown physics/gravity, 4s fuse (", seconds, "), 15s cooldown, blast LOS/range/self-damage, wave growth beyond 24") world.queue_free() await process_frame quit()