Scriptname _00E_CombatMusicControl extends Quest Hidden 

MusicType[] ActiveCombatMusics
MusicType[] StoppingCombatMusics
Int StoppingCombatMusicCounter = 0

Int Property COMBAT_MUSIC_LOCK_NONE     = 0 AutoReadOnly
Int Property COMBAT_MUSIC_LOCK_START    = 1 AutoReadOnly
Int Property COMBAT_MUSIC_LOCK_STOP     = 2 AutoReadOnly
Int Property COMBAT_MUSIC_LOCK_FAILSAFE = 3 AutoReadOnly

Int CombatMusicLockLevel = 0 ; = COMBAT_MUSIC_LOCK_NONE

Event OnInit()
	; Yes, OnInit() runs in idle quests as well
	InitCombatMusic() ; also called from OnPlayerLoadGame() in _00E_PlayerFunctions
EndEvent

Function InitCombatMusic()
	RegisterForModEvent("Enderal_CombatStateChanged", "OnCombatStateChange")

	If ActiveCombatMusics.Length == 0
		ActiveCombatMusics = _NewMusicArray()
	EndIf

	If StoppingCombatMusics.Length == 0
		StoppingCombatMusics = _NewMusicArray()
	EndIf

	If StoppingCombatMusicCounter > 0 || ActiveCombatMusics[0]
		GoToState("CombatMusicTracking")
	EndIf
EndFunction

function IsStartingBrawling()
	bIsBrawling = true
	RegisterForSingleUpdate(1.0)
endfunction

event OnUpdate()
	bIsBrawling = false
endevent

Event OnCombatStateChange(string eventName, string bTargetIsPlayer, float fCombatState, Form sender)
	return
	if bTargetIsPlayer ; start combat or searching
		if fCombatState as int == 1 ; combat
			if bIsBrawling
				bIsBrawling = false
				if GetState() == ""
					UnregisterForUpdate()
				else
					StopCombatMusic()
				endif
				return
			endif
		
			; start music
			Actor sourceActor = sender as Actor
			Int encounterLevel = sourceActor.GetLevel()
			
			If encounterLevel >= 20 || (encounterLevel >= PlayerLevel.GetValue() - 15) || HasKeyword(ActorTypeBoss)
				MusicType combatMusic
				if _00E_ActorsCombatMusicEpic.HasForm(sourceActor.GetActorBase())
					combatMusic = _00E_Music_Combat_Epic
				elseif EnderalFunctions.IsInRegion(Wueste)
					combatMusic = _00E_Music_Combat_Exotic
				else
					combatMusic = _00E_Music_Combat_Regular
				endif
			
				if StartCombatMusic(combatMusic)
					RegisterForSingleUpdate(5.0)
				EndIf
			EndIf
		elseif ! PlayerRef.IsInCombat()
			if GetState() == ""
				GoToState("CombatMusicTracking")
			endif
			RegisterForSingleUpdate(2.0)
		endif
	elseif ! PlayerRef.IsInCombat()
		if GetState() == ""
			GoToState("CombatMusicTracking")
		endif
		RegisterForSingleUpdate(2.0)
	endif
endEvent

Bool Function StartCombatMusic(MusicType newMusic)
	; Debug.Trace(self + ", StartCombatMusic, newMusic = " + newMusic)

	Bool result = False

	While CombatMusicLockLevel > COMBAT_MUSIC_LOCK_NONE
		Utility.Wait(0.03) ; wait for two frames
	EndWhile
	CombatMusicLockLevel = COMBAT_MUSIC_LOCK_START

	Int Index = _AddUniqueMusicToArray(ActiveCombatMusics, newMusic)
	If Index >= 0
		ActiveCombatMusics[Index].Add()
		result = (Index == 0) ; Is this the first combat music in this fight?
	EndIf

	StoppingCombatMusicCounter = 0

	GoToState("CombatMusicTracking")

	CombatMusicLockLevel = COMBAT_MUSIC_LOCK_NONE

	Return result
EndFunction

Function StopCombatMusic()
	; Debug.Trace(self + ", StopCombatMusic")

	While CombatMusicLockLevel > COMBAT_MUSIC_LOCK_NONE
		If CombatMusicLockLevel == COMBAT_MUSIC_LOCK_START || CombatMusicLockLevel == COMBAT_MUSIC_LOCK_STOP
			Return
		EndIf
		Utility.Wait(0.03)
	EndWhile
	CombatMusicLockLevel = COMBAT_MUSIC_LOCK_STOP

	Int Index = 0
	While Index < ActiveCombatMusics.Length && ActiveCombatMusics[Index]
		ActiveCombatMusics[Index].Remove()
		_AddUniqueMusicToArray(StoppingCombatMusics, ActiveCombatMusics[Index])
		Index += 1
	EndWhile

	ActiveCombatMusics = _NewMusicArray()

	StoppingCombatMusicCounter = 8

	GoToState("CombatMusicTracking")

	CombatMusicLockLevel = COMBAT_MUSIC_LOCK_NONE
EndFunction


State CombatMusicTracking

	Event OnBeginState()
		RegisterForSingleUpdate(0.1)
	EndEvent

	Event OnEndState()
		UnregisterForUpdate()
	EndEvent

	Event OnUpdate()

		RegisterForSingleUpdate(3.5)

		If CombatMusicLockLevel > COMBAT_MUSIC_LOCK_NONE || PlayerRef.IsInCombat()
			Return
		EndIf

		If StoppingCombatMusicCounter > 0

			CombatMusicLockLevel = COMBAT_MUSIC_LOCK_FAILSAFE

			; Debug.Trace(self + ", OnUpdate: failsaving")

			Int Index = 0
			While Index < StoppingCombatMusics.Length && StoppingCombatMusics[Index]
				StoppingCombatMusics[Index].Add()
				StoppingCombatMusics[Index].Remove()
				Index += 1
			EndWhile

			StoppingCombatMusicCounter -= 1
			If StoppingCombatMusicCounter <= 0
				StoppingCombatMusics = _NewMusicArray()
			EndIf 

			CombatMusicLockLevel = COMBAT_MUSIC_LOCK_NONE

		ElseIf ActiveCombatMusics[0] ; Failsafe in case StopCombatMusic is not called for whatever reason

			StopCombatMusic()

		Else

			GoToState("")

		EndIf

	EndEvent

EndState

MusicType[] Function _NewMusicArray()
	Return New MusicType[10]
EndFunction

Int Function _AddUniqueMusicToArray(MusicType[] musics, MusicType newMusic)
	Int n = musics.Length
	Int Index = 0

	; Loop through array until we reach the end or newMusic or an empty entry
	While Index < n && musics[Index] != newMusic && musics[Index] != None
		Index += 1
	EndWhile

	If Index < n && musics[Index] == None
		musics[Index] = newMusic
		Return Index
	Else
		Return -1
	EndIf
EndFunction

Function RemoveCombatSoundtracks()

	int iIndex = _00E_MUS_AllCombatSoundtracks.GetSize()
	
	while iIndex > 0
		iIndex -= 1
		MusicType musicToRemove = _00E_MUS_AllCombatSoundtracks.GetAt(iIndex) as MusicType
		musicToRemove.Remove()
	endwhile

EndFunction

bool bIsBrawling = false

Keyword Property ActorTypeBoss  Auto  

GlobalVariable Property PlayerLevel  Auto  

MusicType Property _00E_Music_Combat_Regular  Auto  
MusicType Property _00E_Music_Combat_Exotic  Auto  
MusicType Property _00E_Music_Combat_Epic  Auto  

FormList Property _00E_MUS_AllCombatSoundtracks  Auto  
FormList Property _00E_ActorsCombatMusicEpic  Auto  

Form Property Wueste Auto ; Powder Desert

Actor Property PlayerRef  Auto