Scriptname _00E_BardPlayInstrumentScript extends Actor Sound Property MusicPiece Auto Package Property PlayPackage Auto { The package must have HasLoaded3D == 1 condition and optionally 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