Scriptname _00E_BardPlayInstrumentScript extends Actor

Sound Property MusicPiece Auto

Package Property PlayPackage Auto
{ The package must have <subject> HasLoaded3D == 1 condition and optionally <subject> GetDisabled == 0 condition (if the reference can be disabled) }

Keyword Property LinkedPlayMarkerKeyword Auto
Spell Property BardPlayTrackerSP Auto
Bool Property DontRestartTrack Auto

Int Property PLAYSTATE_STOPPED = 0 AutoReadOnly
Int Property PLAYSTATE_PLAYING = 1 AutoReadOnly
Int Property PLAYSTATE_STOPPING_FADE = 2 AutoReadOnly

Int PlayState
Int MusicInstance
Float MusicVolume

Int WarmupCounter
Float LastWarmupDistance

Event OnPackageStart(Package akNewPackage)
	; Debug.Trace("_00E_BardPlayInstrumentScript, OnPackageStart, " + akNewPackage)

	If akNewPackage == PlayPackage
		AddSpell(BardPlayTrackerSP, False)
	Else
		RemoveSpell(BardPlayTrackerSP)
		StopMusic()
	EndIf
EndEvent

Event OnBardsLoadGame(string eventName, string strArg, float numArg, Form sender)
	; Debug.Trace("_00E_BardPlayInstrumentScript, OnBardsLoadGame...")

	If PlayState == PLAYSTATE_PLAYING
		StopMusicInstance()
		RegisterForSingleUpdate(0.25)
	Else ; PlayState == PLAYSTATE_STOPPING_FADE
		StopMusic()
	EndIf
EndEvent

Int Function TickWarmupCounter()
	ObjectReference PlayMarker = GetLinkedRef(LinkedPlayMarkerKeyword)
	If PlayMarker
		Float d = GetDistance(PlayMarker)
		; Is the NPC close to the play marker and kept the same distance from it since the previous tick (doesn't move)?
		If d < 128.0 && (WarmupCounter == 0 || Math.abs(d - LastWarmupDistance) < 1.0)
			LastWarmupDistance = d
			Return WarmupCounter + 1
		EndIf
	EndIf

	Return 0
EndFunction

Event OnUpdate()
	If PlayState == PLAYSTATE_STOPPED
		Return
	EndIf

	If MusicInstance != 0 ; Fading
		MusicVolume -= 0.025
		If MusicVolume >= 0.1
			Sound.SetInstanceVolume(MusicInstance, MusicVolume)
			RegisterForSingleUpdate(0.2)
		ElseIf PlayState == PLAYSTATE_PLAYING
			StopMusicInstance()
			RegisterForSingleUpdate(0.1)
		Else ; PlayState == PLAYSTATE_STOPPING_FADE
			StopMusic()
		EndIf
	Else 
		If PlayState == PLAYSTATE_PLAYING
			; Wait for the NPC to get close to its linked play marker and stay stationary (more or less) for some time
			WarmupCounter = TickWarmupCounter()
			If WarmupCounter > 4
				MusicInstance = MusicPiece.Play(self)
				MusicVolume = 1.0
				If DontRestartTrack == False
					RegisterForSingleUpdate(2 * 60 + 40) ; 2:40 - roughly the length of the longest "classical" track in Sound\FX\Enderal\Bards
				EndIf
			Else
				RegisterForSingleUpdate(0.25)
			EndIf
		Else ; PlayState == PLAYSTATE_STOPPING_FADE
			StopMusic()
		EndIf
	EndIf
EndEvent

Function StopMusicInstance()
	If MusicInstance != 0
		Int _tempInst = MusicInstance
		MusicInstance = 0
		Sound.StopInstance(_tempInst)
	EndIf
EndFunction

Function StartMusic()
	If PlayState != PLAYSTATE_PLAYING
		PlayState = PLAYSTATE_PLAYING
		RegisterForModEvent("BardsLoadGame", "OnBardsLoadGame")
		StopMusicInstance()
		RegisterForSingleUpdate(0.01)
	EndIf
EndFunction

Function StopMusic()
	If PlayState != PLAYSTATE_STOPPED
		PlayState = PLAYSTATE_STOPPED
		UnregisterForUpdate()
		StopMusicInstance()
		UnregisterForModEvent("BardsLoadGame")
	EndIf

	WarmupCounter = 0
EndFunction

Function FadeAndStopMusic()
	If PlayState != PLAYSTATE_STOPPED
		If MusicInstance != 0
			PlayState = PLAYSTATE_STOPPING_FADE
			RegisterForSingleUpdate(0.1)
			While PlayState == PLAYSTATE_STOPPING_FADE
				Utility.Wait(0.5)
			EndWhile
		Else
			StopMusic()
		EndIf
	EndIf
EndFunction