67 lines
2.8 KiB
GDScript
67 lines
2.8 KiB
GDScript
extends SceneTree
|
|
var failures := 0
|
|
var checks := 0
|
|
func _initialize() -> void: call_deferred("run")
|
|
func check(ok: bool, message: String) -> void:
|
|
checks += 1
|
|
if not ok: failures += 1
|
|
print("PASS " if ok else "FAIL ", message)
|
|
func run() -> void:
|
|
var audio := ArenaAudio.new()
|
|
root.add_child(audio)
|
|
for local in [true,false]:
|
|
for i in 8: audio.play_shot("shot_0", Vector3.ZERO, local)
|
|
check(audio.shot_players.size() == 2, "shot players reused rather than allocated per shot")
|
|
for player in audio.shot_players.values():
|
|
check(player.max_polyphony == 3 and player.pitch_scale >= 0.95 and player.pitch_scale <= 1.05, "polyphony and pitch limits")
|
|
var limited := false
|
|
for i in AudioServer.get_bus_effect_count(0): limited = limited or AudioServer.get_bus_effect(0,i) is AudioEffectLimiter
|
|
check(limited, "master limiter installed")
|
|
for profile in WeaponHandling.DATA:
|
|
var states := []
|
|
for rate in [60,240]:
|
|
var state: Array[Vector2] = [Vector2.ZERO, Vector2(profile.kick,profile.rise)*profile.spring*2.1]
|
|
for tick in rate: state = WeaponHandling.settle(state[0],state[1],profile.spring,1.0/rate)
|
|
states.append(state)
|
|
check(states[0][0].distance_to(states[1][0]) < 0.00001, "recoil trajectory endpoint matches at 60/240 Hz")
|
|
var stop := HitStop.new()
|
|
root.add_child(stop)
|
|
var started := Time.get_ticks_usec()
|
|
stop.trigger()
|
|
check(Engine.time_scale == 0.05, "hit-stop applies five percent")
|
|
while stop.running: await process_frame
|
|
var elapsed := (Time.get_ticks_usec()-started)/1000.0
|
|
check(Engine.time_scale == 1.0 and elapsed >= 29 and elapsed < 100, "hit-stop restores on real-time deadline; ms="+str(elapsed))
|
|
stop.trigger()
|
|
stop.restore()
|
|
check(Engine.time_scale == 1.0, "leaving restores time immediately")
|
|
await create_timer(0.5, true, false, true).timeout
|
|
var stage := Node3D.new()
|
|
root.add_child(stage)
|
|
var fighter := Fighter.new()
|
|
fighter.add_child(fighter.movement)
|
|
fighter.add_child(fighter.weapon_controller)
|
|
fighter.add_child(fighter.replication_state)
|
|
fighter.set_process(false)
|
|
fighter.set_physics_process(false)
|
|
stage.add_child(fighter)
|
|
fighter.process_mode = Node.PROCESS_MODE_DISABLED
|
|
var target := StaticBody3D.new()
|
|
target.position = Vector3(0,0,-5)
|
|
var box := BoxShape3D.new()
|
|
var collider := CollisionShape3D.new()
|
|
collider.shape = box
|
|
target.add_child(collider)
|
|
stage.add_child(target)
|
|
await physics_frame
|
|
await physics_frame
|
|
var hit := HitscanRay.cast(fighter, Vector3.ZERO, Vector3(0,0,-10), 1)
|
|
check(hit.get("collider") == target and fighter.get_node("HitscanRay") is RayCast3D, "RayCast3D hits world cover immediately")
|
|
check(HitscanRay.cast(fighter, Vector3.ZERO, Vector3(0,5,-10), 1).is_empty(), "RayCast3D clears previous hit on miss")
|
|
stage.queue_free()
|
|
audio.queue_free()
|
|
stop.queue_free()
|
|
await process_frame
|
|
print("POLISH RESULT ", checks-failures,"/",checks)
|
|
quit(1 if failures else 0)
|