class_name MusicDirector extends Node static var volume := 0.65 var layers: Array[AudioStreamPlayer] = [] var state := "" var transition: Tween var game: Node var cue: AudioStreamPlayer const ROOT := "res://assets/audio/assault/" func _ready() -> void: if DisplayServer.get_name() == "headless": set_process(false) return if AudioServer.get_bus_index("Music") < 0: AudioServer.add_bus() AudioServer.set_bus_name(AudioServer.bus_count-1,"Music") for instrument in ["Synth","Strings","Percussion","Guitar"]: var path := ROOT+"Stems/Incoming/MUS_Combat_Incoming_Loop_%s_100BPM.wav" % instrument if not ResourceLoader.exists(path): continue var stream := load(path).duplicate() as AudioStreamWAV stream.loop_mode = AudioStreamWAV.LOOP_FORWARD stream.loop_begin = 0 stream.loop_end = int(round(stream.get_length()*stream.mix_rate)) var player := AudioStreamPlayer.new() player.stream = stream player.bus = "Music" player.volume_db = -60 add_child(player) layers.append(player) # All stems use the same audio mix boundary and keep playing through UI changes. for player in layers: player.play() cue = AudioStreamPlayer.new() cue.bus = "Music" cue.volume_db = -20 add_child(cue) func _process(_delta: float) -> void: AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Music"),linear_to_db(maxf(volume,0.0001))) AudioServer.set_bus_mute(AudioServer.get_bus_index("Music"),volume <= 0) var next := "menu" if is_instance_valid(game): if game.match_over: next = "results" elif game.active: next = "pause" if game.paused else "combat" elif game.connecting: next = "lobby" if next == state: return var previous := state state = next if state == "combat" and previous != "pause": play_cue("Loops/Combat_Incoming_Modular/MUS_Combat_Incoming_Intro_Transition_100BPM.wav") elif state == "results": var ending := "01" if not is_instance_valid(game) or game.winner != "DRAW" else "02" play_cue("Loops/Combat_Incoming_Modular/MUS_Combat_Incoming_Outro_Resolve_%s_100BPM.wav" % ending) if transition: transition.kill() transition = create_tween().set_parallel(true) var levels: Array = {"menu":[-16,-23,-60,-60],"lobby":[-16,-20,-28,-60],"combat":[-23,-23,-23,-26],"pause":[-25,-30,-60,-60],"results":[-19,-18,-60,-60]}[state] for i in layers.size(): transition.tween_property(layers[i],"volume_db",float(levels[i]),1.5) func play_cue(relative: String) -> void: if cue and ResourceLoader.exists(ROOT+relative): cue.stream = load(ROOT+relative) cue.play()