Scriptname _00E_BardDialogueFunctions extends Quest Conditional

Scene Property BardDialogue_Instrumental20 Auto
Scene Property BardDialogue_LastSunset Auto
Scene Property BardDialogue_LastSunsetInstrumental Auto
Scene Property BardDialogue_Song06 Auto
Scene Property BardDialogue_Song08 Auto
Scene Property BardDialogue_Song09 Auto
Scene Property BardDialogue_Song10 Auto
Scene Property BardDialogue_Song11 Auto
Scene Property BardDialogue_TheAgedMan Auto
Scene Property BardDialogue_TheAgedManInstrumental Auto
Scene Property BardDialogue_Vatyra Auto
Scene Property BardDialogue_VatyraInstrumental Auto
Scene Property BardDialogue_WaywardWanderer Auto
Scene Property BardDialogue_WaywardWandererInstrumental Auto
Scene Property BardDialogue_WelcomeYouDrunkards Auto
Scene Property BardDialogue_Whisperwood Auto
Scene Property BardDialogue_SongWaageScene Auto ; Added in FS

ReferenceAlias Property SingingBard Auto
LocationAlias Property Location_Alias Auto
Quest Property BardAudience Auto 

Location Property _00E_DunevilleInteriorLocation auto
Location Property _00E_FlusshaimTaverneLocation auto
Location Property _00E_SuntempleQuarters auto
Location Property _00E_UEEntr_FalseDog_Location auto
Location Property BauernkuesteRoterOchseLocation auto
Location Property CapitalCityTheFatLeoranLocation auto
Location Property SchneefelstaverneInteriorLocation auto

Idle Property IdleStop Auto

Scene[] BardSongList
Int nBardSongsTotal

Bool bSongHasQuestAudience = False
Bool bQuestAudienceCheering = False
Scene NextSongScene = None
Scene Property LastSongScene1 Auto Conditional Hidden
Scene LastSongScene2 = None
Scene LastSongScene3 = None

Int Property QUEST_AUDIENCE_CHANCE = 67 AutoReadOnly


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

Function Setup(Actor akBard, Scene _LastSongScene)
	bSongHasQuestAudience = False
	bQuestAudienceCheering = False
	NextSongScene = None
	LastSongScene1 = _LastSongScene
	LastSongScene2 = None
	LastSongScene3 = None
	SingingBard.ForceRefTo(akBard)
	
	PlayRandomSong(0.1)
EndFunction

Function PrepareAudience(Int iAudienceInitialStage) ; A song is about to start, prepare the audience. Called from the scenes.
	SendModEvent("BardSongStarted")

	If IsQuestAudienceLocation() && iAudienceInitialStage > 0
		bSongHasQuestAudience = True

		If BardAudience.IsRunning() == False && bQuestAudienceCheering == False
			If iAudienceInitialStage == 10
				Int iAudienceRoll = Utility.RandomInt(1, 100)
				If iAudienceRoll <= QUEST_AUDIENCE_CHANCE
					If iAudienceRoll < (QUEST_AUDIENCE_CHANCE / 2) ; Roughly a 50% chance it will be the more lively audience
						iAudienceInitialStage = 20
					EndIf
				Else
					iAudienceInitialStage = 0
				EndIf
			EndIf

			If iAudienceInitialStage > 0
				BardAudience.SetStage(iAudienceInitialStage)
			EndIf
		EndIf
	EndIf

	bQuestAudienceCheering = False
EndFunction

Function OnSongStart(Scene songScene) ; The bard is actually starting to sing or make instrumental sounds. Called from the scenes.
	NextSongScene = None
	LastSongScene3 = LastSongScene2
	LastSongScene2 = LastSongScene1
	LastSongScene1 = songScene
EndFunction

Function CheerAudience() ; When a song ends normally, time for the audience to cheer. Called from the scenes.
	SendModEvent("BardSongApplaud")

	If IsQuestAudienceLocation() &&  BardAudience.IsRunning()
		Int iCurrentAudienceStage = BardAudience.GetStage()
		If iCurrentAudienceStage >= 10 && iCurrentAudienceStage <= 20
			BardAudience.SetStage(100)
			bQuestAudienceCheering = True
		EndIf
	EndIf
EndFunction

Function OnSongEnd() ; When a song ends normally or is interrupted. Called from the scenes.
	Actor akBard = SingingBard.GetActorRef() ; If akBard is None in OnSongEnd, most likely it's the player just left the cell and the quest is stopping
	Bool bLongDelay = False
	If akBard
		akBard.PlayIdle(IdleStop)
	EndIf
	SendModEvent("BardSongEnded")
	If bSongHasQuestAudience == False && BardAudience.IsRunning() && BardAudience.GetStage() < 100
		BardAudience.Stop()
	EndIf
	If akBard
		Float fNextSongDelay
		If bQuestAudienceCheering
			; A longer gap between songs is needed if the quest audience is applauding
			; because otherwise the bard fails to properly pick up the "play intrument" animation at the start of the next song
			fNextSongDelay = Utility.RandomFloat(5.0, 6.0)
		Else
			fNextSongDelay = Utility.RandomFloat(1.5, 3.0)
		EndIf
		PlayRandomSong(fNextSongDelay)
	EndIf
EndFunction

Function PlayRandomSong(Float fDelay)
	LockNextSongUpdate()
	If NextSongScene == None
		BuildSongList(False)
		PickRandomSongFromSongList()
	EndIf
	UnlockNextSongUpdate()

	RegisterForSingleUpdate(fDelay)
EndFunction

Function PlayRequestedSong(Scene requestedSongScene)
	LockNextSongUpdate()
	If requestedSongScene
		NextSongScene = requestedSongScene
	Else
		BuildSongList(True)
		PickRandomSongFromSongList()
	EndIf
	UnlockNextSongUpdate()
	RegisterForSingleUpdate(0.1)
EndFunction

Event OnUpdate()
	Actor akBard = SingingBard.GetActorRef()
	If akBard == None
		Stop()
		Return
	EndIf

	If akBard.IsInDialogueWithPlayer()
		RegisterForSingleUpdate(1.0)
		Return
	EndIf

	bSongHasQuestAudience = False
	If NextSongScene
		NextSongScene.ForceStart()
	EndIf
EndEvent

Function BuildSongList(Bool bOnlyVocalSongs)
	BardSongList = New Scene[16]
	nBardSongsTotal = 0

	Location bardLoc = Location_Alias.GetLocation()
	If bardLoc == CapitalCityTheFatLeoranLocation || bardLoc == _00E_DunevilleInteriorLocation || bardLoc == _00E_UEEntr_FalseDog_Location ; VT_Gerril
		AddSongToList(BardDialogue_WaywardWanderer)
		AddSongToList(BardDialogue_TheAgedMan)
		AddSongToList(BardDialogue_LastSunset)
		AddSongToList(BardDialogue_Whisperwood)
		AddSongToList(BardDialogue_Song06) ; Black Guardian
		AddSongToList(BardDialogue_Vatyra)
		AddSongToList(BardDialogue_Song08) ; Wild Mages
		AddSongToList(BardDialogue_Song09) ; Lost Ones
		AddSongToList(BardDialogue_Song10) ; Crimson Star
		AddSongToList(BardDialogue_Song11) ; Winter Sky (Firmament)
		AddSongToList(BardDialogue_WelcomeYouDrunkards)
	ElseIf bardLoc == _00E_FlusshaimTaverneLocation || bardLoc == _00E_SuntempleQuarters ; VT_Liliath
		AddSongToList(BardDialogue_WaywardWanderer)
		AddSongToList(BardDialogue_TheAgedMan)
		AddSongToList(BardDialogue_LastSunset)
		AddSongToList(BardDialogue_Whisperwood)
		AddSongToList(BardDialogue_Song06) ; Black Guardian
		AddSongToList(BardDialogue_Vatyra)
		AddSongToList(BardDialogue_Song08) ; Wild Mages
		AddSongToList(BardDialogue_Song09) ; Lost Ones
		AddSongToList(BardDialogue_Song10) ; Crimson Star
	ElseIf bardLoc == BauernkuesteRoterOchseLocation ; VT_FS_Bard
		AddSongToList(BardDialogue_Vatyra)
		AddSongToList(BardDialogue_Song11) ; Winter Sky (Firmament)
		AddSongToList(BardDialogue_TheAgedMan)
		AddSongToList(BardDialogue_SongWaageScene)
		AddSongToList(BardDialogue_Song10) ; Crimson Star
		AddSongToList(BardDialogue_WelcomeYouDrunkards)
	ElseIf bardLoc == SchneefelstaverneInteriorLocation ; VT_Lara
		If bOnlyVocalSongs
			LastSongScene3 = None ; The list is too short
		EndIf
		AddSongToList(BardDialogue_WaywardWanderer)
		AddSongToList(BardDialogue_Vatyra)
		AddSongToList(BardDialogue_Song11) ; Winter Sky (Firmament)
	Else
		If bardLoc
			Debug.Notification("ERROR BardDialogue: unsupported location!")
			Debug.Trace("ERROR BardDialogue: unsupported location (" + bardLoc + ")")
		EndIf
		Return
	EndIf

	If bOnlyVocalSongs == False
		AddSongToList(BardDialogue_VatyraInstrumental)
		AddSongToList(BardDialogue_WaywardWandererInstrumental)
		AddSongToList(BardDialogue_TheAgedManInstrumental)
		AddSongToList(BardDialogue_LastSunsetInstrumental)
		AddSongToList(BardDialogue_Instrumental20) ; Drum Instrumental
	EndIf
EndFunction

Function AddSongToList(Scene songScene)
	If songScene == LastSongScene1 || songScene == LastSongScene2 || songScene == LastSongScene3
		Return ; Has been already played recently
	EndIf

	If nBardSongsTotal >= BardSongList.Length
		Debug.Notification("ERROR BardDialogue: out of free slots for songs!")
		Debug.Trace("ERROR BardDialogue: out of free slots for songs")
		Return
	EndIf

	BardSongList[nBardSongsTotal] = songScene
	nBardSongsTotal += 1
EndFunction

Function PickRandomSongFromSongList()
	If nBardSongsTotal == 1
		NextSongScene = BardSongList[0]
	ElseIf nBardSongsTotal > 1
		NextSongScene = BardSongList[Utility.RandomInt(0, nBardSongsTotal - 1)]
	Else ; nBardSongsTotal < 1
		NextSongScene = None
	EndIf
EndFunction

Actor Function GetSingingBardActor()
	Return SingingBard.GetActorRef()
EndFunction

Bool Function IsQuestAudienceLocation()
	Location bardLoc = Location_Alias.GetLocation()
	If bardLoc == _00E_FlusshaimTaverneLocation || bardLoc == _00E_DunevilleInteriorLocation || bardLoc ==  _00E_UEEntr_FalseDog_Location
		Return True
	Else
		Return False
	EndIf
EndFunction


;=====================================================================================
;              						NEXT SONG LOCK
;=====================================================================================

Bool bNextSongUpdateLocked = False

Function LockNextSongUpdate()
	While bNextSongUpdateLocked
		Utility.WaitMenuMode(0.1)
	EndWhile
	bNextSongUpdateLocked = True
EndFunction

Function UnlockNextSongUpdate()
	bNextSongUpdateLocked = False
EndFunction