extends AudioStreamPlayer2D

# =====================================================================================
#  MUSIC  --  the background track, with a couple of "pro" touches.
#
#  Instead of slamming on at full volume and playing flat forever, it:
#    * fades in when a level starts, and
#    * "ducks" on death — dropping in volume AND muffling through a low-pass filter
#      (an "underwater" feel) — so the slow-motion death beat lands quiet and heavy,
#      then swells back as you respawn.
#
#  Registered as the `Music` autoload, so it survives scene reloads and keeps playing
#  seamlessly while the level restarts. The level's GameManager calls restore() on
#  start, which doubles as the first fade-in and the post-death recovery; the player
#  calls duck() when it dies.
# =====================================================================================

const NORMAL_VOLUME = 0.0      # resting volume (dB) on this node (the bus adds its own)
const DUCK_VOLUME   = -18.0    # how far the volume drops while ducked
const START_VOLUME  = -40.0    # near-silent, so the very first fade-in has room to swell

const OPEN_CUTOFF = 20000.0    # low-pass wide open = no muffle
const DUCK_CUTOFF = 400.0      # low-pass clamped down = muffled, distant music

const FADE_IN_TIME = 1.2       # how long the swell-in / recovery takes
const DUCK_TIME    = 0.25      # how fast the music drops away on death

var _lowpass: AudioEffectLowPassFilter
var _tween: Tween


func _ready():
	# Add a low-pass filter to the Music bus that we can sweep for the muffle.
	var bus := AudioServer.get_bus_index("Music")
	if bus != -1:
		_lowpass = AudioEffectLowPassFilter.new()
		_lowpass.cutoff_hz = OPEN_CUTOFF
		AudioServer.add_bus_effect(bus, _lowpass)
	volume_db = START_VOLUME   # GameManager.restore() swells us in from here


# Swell back to full, open volume. Called when a level starts, so it serves as both
# the initial fade-in and the recovery after a death duck.
func restore(duration := FADE_IN_TIME):
	_sweep(NORMAL_VOLUME, OPEN_CUTOFF, duration)


# Drop + muffle the music for a dramatic beat (the death slow-mo).
func duck():
	_sweep(DUCK_VOLUME, DUCK_CUTOFF, DUCK_TIME)


# Tween the node volume and the low-pass cutoff together. ignore_time_scale keeps
# the sweep crisp even while the death slow-mo has the rest of the game at 0.4x.
func _sweep(target_volume: float, target_cutoff: float, duration: float):
	if _tween and _tween.is_valid():
		_tween.kill()
	_tween = create_tween().set_parallel(true)
	_tween.set_ignore_time_scale(true)
	_tween.tween_property(self, "volume_db", target_volume, duration)
	if _lowpass:
		_tween.tween_property(_lowpass, "cutoff_hz", target_cutoff, duration)
