Scriptname _00E_BardAudienceScript extends Actor

Faction Property BardAudienceStateFaction Auto
_00E_BardDialogueFunctions Property BardDialogue Auto
Package Property CrossedArmsListenPackage Auto
Bool Property NoApplaud Auto
Bool Property NoHeadtrackingBard Auto

Int Property STATE_NONE    = -1 AutoReadOnly
Int Property STATE_APPLAUD = 0 AutoReadOnly
Int Property STATE_LISTEN  = 1 AutoReadOnly

Bool bBardIsPlaying = False


;=====================================================================================
;              							EVENTS                  					 
;=====================================================================================

Event OnLoad()
	If IsDead() == False
		GoToState("Spectating")
		RegisterForModEvent("BardSongStarted", "OnBardSongStarted")
		RegisterForModEvent("BardSongApplaud", "OnBardSongApplaud")
		RegisterForModEvent("BardSongEnded", "OnBardSongEnded")
	EndIf
EndEvent

State Spectating
	Event OnUnload()
		Terminate()
	EndEvent

	Event OnDying(Actor akKiller)
		Terminate()
	EndEvent

	Event OnDeath(Actor akKiller)
		Terminate()
	EndEvent
EndState

Event OnBardSongStarted(string a_eventName, string a_strArg, float a_numArg, Form a_sender)
	If Is3DLoaded() == False
		Terminate()
		Return
	EndIf

	bBardIsPlaying = True

	If TryLockStateUpdate()
		WaitRandom(1.0, 2.0)
		SetAudienceState(STATE_LISTEN)
		UnlockStateUpdate()
	EndIf
EndEvent

Event OnBardSongApplaud(string a_eventName, string a_strArg, float a_numArg, Form a_sender)
	If Is3DLoaded() == False
		Terminate()
		Return
	EndIf

	bBardIsPlaying = False

	If TryLockStateUpdate()
		If NoApplaud == False
			If CrossedArmsListenPackage != None && GetCurrentPackage() == CrossedArmsListenPackage
				SetAudienceState(STATE_LISTEN + 1)
				Debug.SendAnimationEvent(self, "IdleOffsetStop") ; This is the way to exit OffsetArmsCrossedStart idle
				Utility.Wait(1.0)
			Else
				WaitRandom(0.01, 0.25)
			EndIf
			SetAudienceState(STATE_APPLAUD)
			WaitRandom(7.0, 7.5)
		Else ; No applauding
			WaitRandom(5.0, 5.5)
		EndIf
		If Is3DLoaded() && bBardIsPlaying
			SetAudienceState(STATE_LISTEN)
		Else
			SetAudienceState(STATE_NONE)
		EndIf
		UnlockStateUpdate()
	EndIf
EndEvent

Event OnBardSongEnded(string a_eventName, string a_strArg, float a_numArg, Form a_sender)
	If Is3DLoaded() == False
		Terminate()
		Return
	EndIf

	bBardIsPlaying = False

	If TryLockStateUpdate()
		WaitRandom(1.0, 2.0)
		SetAudienceState(STATE_NONE)
		UnlockStateUpdate()
	EndIf
EndEvent


;=====================================================================================
;              							FUNCTIONS                  					 
;=====================================================================================

Function Terminate()
	GoToState("")
	bBardIsPlaying = False
	UnregisterForAllModEvents()
	RemoveFromFaction(BardAudienceStateFaction)
EndFunction

Function WaitRandom(Float fMinWaitSecs, Float fMaxWaitSecs)
	Utility.Wait(Utility.RandomFloat(fMinWaitSecs, fMaxWaitSecs))
EndFunction

Function SetAudienceState(Int iNewState)
	If iNewState >= 0
		If iNewState == STATE_LISTEN
			iNewState = Utility.RandomInt(1, 100)
		EndIf
		SetFactionRank(BardAudienceStateFaction, iNewState)
		EvaluatePackage()

		; Look at
		If NoHeadtrackingBard == False
			ObjectReference lookTarget = BardDialogue.GetSingingBardActor()
			If lookTarget
				SetLookAt(lookTarget)
			Else
				ClearLookAt()
			EndIf
		EndIf
	Else
		RemoveFromFaction(BardAudienceStateFaction)
		EvaluatePackage()
		If NoHeadtrackingBard == False
			ClearLookAt()
		EndIf
		; The NPC might not switch from their _00E_AnimTest_SC package if they are in combat, so they need an additional kick here
		_00E_AnimTest_SC myAnimTest = ((Self as Actor) as _00E_AnimTest_SC)
		If myAnimTest
			myAnimTest.CombatResetAnimations()
		EndIf
	EndIf
EndFunction

Bool bStateUpdateLocked = False

Bool Function TryLockStateUpdate()
	If bStateUpdateLocked
		Return False
	EndIf

	bStateUpdateLocked = True
	Return True
EndFunction

Function UnlockStateUpdate()
	bStateUpdateLocked = False
EndFunction