Files
zombie-fps/tools/test_damage_feedback.gd
T
enne2 956a15a525 Add industrial metal soundtrack with seamless loop
- assets/audio/music/zombie_industrial_loop.ogg: 57.60 s Ogg Vorbis loop
  generated locally with YuE2-3B on the Tesla V100 (score written for this
  track; vocals removed with HTDemucs). The loop is spliced at the track's
  structural repeat with a 0.6 s crossfade: measured seam dip <= 1.6 dB.
- scripts/main.gd: "Music" AudioStreamPlayer at -11 dB, started at boot on
  desktop and on the start click on Web (browser autoplay policy). It forces
  stream.loop at runtime because the Web staging step re-imports the OGG with
  loop=false.
- tools/test_music.gd: soundtrack test (loop, length, volume, playback,
  still playing after player death).
- docs/MUSIC.md: source, loop construction, verification, licence.
- docs/WEB-DEPLOY.md: release ab6054111f5919a8 notes.

Initial commit of the project.
2026-10-10 23:11:13 +02:00

53 lines
2.0 KiB
GDScript

extends SceneTree
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 light := OmniLight3D.new()
light.position = Vector3(0, 4, 0)
light.omni_range = 24
light.light_energy = 4
world.add_child(light)
var player := CharacterBody3D.new()
player.set_script(load("res://scripts/player.gd"))
player.position.y = 0.1
world.add_child(player)
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
await create_timer(0.2).timeout
player.camera.rotation.x = 0.12
var aim: Vector3 = player.camera.rotation
player.take_damage(12)
assert(player.hp == 88)
assert(player.damage_feedback.blood_left > 0)
assert(player.damage_feedback.splats.size() == 7)
await create_timer(0.06).timeout
assert(player.damage_pivot.position.length() > 0.0001)
assert(player.camera.rotation.is_equal_approx(aim))
if DisplayServer.get_name() != "headless":
await RenderingServer.frame_post_draw
root.get_texture().get_image().save_png("/tmp/zombie-player-hit.png")
# Multiple hits reuse a bounded pool; no camera offset accumulates.
player.take_damage(12)
player.take_damage(12)
assert(player.damage_feedback.intensity <= 1)
assert(player.damage_feedback.splats.size() == 7)
await create_timer(1.35).timeout
assert(player.damage_pivot.transform.is_equal_approx(Transform3D.IDENTITY))
assert(player.camera.rotation.is_equal_approx(aim))
assert(player.damage_feedback.blood_left == 0)
player.take_damage(12)
player.take_damage(1000)
assert(not player.damage_feedback.active)
assert(player.damage_pivot.transform.is_equal_approx(Transform3D.IDENTITY))
await create_timer(3).timeout
assert(player.camera.position.y < 0.25)
assert(player.death_overlay.color.a > 0.99)
print("PASS: damage shake, screen blood, aim preserved, repeated hits bounded, camera reset, death fade intact")
world.queue_free()
await process_frame
quit()