Scriptname _FS_Phasmalist_ControlQuest extends Quest
; the main phasmalist quest that aggregates references to the aliases who do the main work and provides a global interface for some utility functions

_00E_Phasmalist_ApparationAlias Property apparationAlias Auto ; Old version of the apparition alias
_00E_Phasmalist_NewApparitionAlias Property NewApparitionAlias Auto
_00E_Phasmalist_DespectralizeCont Property despectralizeContainerAlias Auto
_00E_Phasmalist_ApparationInventory Property spectralizeContainer Auto
_00E_Phasmalist_PlayerAliasScript Property PlayerAlias Auto

Quest Property FS_NQ08 Auto
Perk Property _00E_Class_Phasmalist_P02_CraftTier1 Auto

Shout Property _00E_Phasmalist_A3_MoveApparationToPlayerShout Auto
WordofPower Property _00E_A3_Phasmalist_MoveApparationToPlayer1 Auto

MiscObject Property _00E_UpdateInventoryItem auto
MiscObject Property _00E_Phasmalist_Invis_EnchantmentRequirement Auto

GlobalVariable Property _00E_AchievementsEnabled Auto
GlobalVariable Property _00E_PhasmalistAchievementUnlocked Auto
GlobalVariable Property _00E_Phasmalist_TankMode Auto

Message Property _00E_Phasmalist_NoApparitionCurrentlySummoned Auto

Perk Property _00E_Class_Phasmalist_P05_C_Violence_01 Auto

Actor Property PlayerREF Auto

Bool bCreatedFirstTrinket = false

Int iScriptVersion = 0
Int Property CURRENT_SCRIPT_VERSION = 2 AutoReadOnly


;=====================================================================================
;              						GLOBAL FUNCTIONS
;=====================================================================================

; use only if necessary, if possible add _FS_Phasmalist_ControlQuest as a property!
_FS_Phasmalist_ControlQuest Function getControlQuest() Global
	Return Game.GetFormFromFile(0x0101ec71, "Enderal - Forgotten Stories.esm") as _FS_Phasmalist_ControlQuest
EndFunction

Function UnsummonApparitionIfExists() Global
	getControlQuest().UnsummonApparition()
EndFunction

Function MoveApparitionToPlayer() Global
	getControlQuest().TeleportApparitionToPlayer()
EndFunction


;=====================================================================================
;              					HELPER EVENTS & FUNCTIONS
;=====================================================================================

Event OnInit()
	iScriptVersion = CURRENT_SCRIPT_VERSION
EndEvent

; called on loadgame by _00E_Phasmalist_LoadGameFailsave, attached to an alias of quest abilities
Function LoadGameFailsave()
	Bool bDoApparitionOnLoadGame = True

	; Version update
	If iScriptVersion < CURRENT_SCRIPT_VERSION
		If iScriptVersion < 1
			apparationAlias.UnregisterForAllKeys()
			EquippedTrinket = apparationAlias.getEquippedTrinket()
			iTeleportApparitionKeyCode = apparationAlias.iCallApparitionKeyCode
			PlayerAlias.ForceRefTo(PlayerREF)
			If apparationAlias.GetRef() != None
				apparationAlias.unsummon(True)
				If IsBadApparitionLocation(PlayerREF.GetCurrentLocation()) == False
					SummonApparition(PlayerREF, False, True)
				EndIf
			EndIf
			apparationAlias.onTrinketEquipped(None)
			_00E_Phasmalist_TankMode.SetValue(0)
			RegisterTeleportKey()
			((self as Quest) as _FS_Phasmalist_AffinityControlQuest).OnGameLoad() ; Obsolete, not needed anymore, so shut it down
			bDoApparitionOnLoadGame = False
		Else
			If iScriptVersion < 2 && PlayerREF.HasPerk(_00E_Class_Phasmalist_P05_C_Violence_01) && IsApparitionSpawned()
				SummonApparition(PlayerREF, False, True)
				bDoApparitionOnLoadGame = False
			EndIf
		EndIf

		iScriptVersion = CURRENT_SCRIPT_VERSION
	EndIf

	If bDoApparitionOnLoadGame
		NewApparitionAlias.OnLoadGame()
	EndIf

	If FS_NQ08.getStage() == 7
		RegisterForModEvent("Phasmalist_Learn_Soulsmith1", "OnUnlockSoulsmithNovice")
	EndIf
EndFunction

; Event called by _00E_Phasmalist_Soul
Function OnPlayerSoulCollected()
	If FS_NQ08.getStage() == 5 || FS_NQ08.getStage() == 1
		If PlayerREF.hasPerk(_00E_Class_Phasmalist_P02_CraftTier1)
			FS_NQ08.setStage(10)
		Else
			FS_NQ08.setStage(7)
			RegisterForModEvent("Phasmalist_Learn_Soulsmith1", "OnUnlockSoulsmithNovice")
		EndIf
	EndIf
EndFunction

; Event called by _00E_Phasmalist_Workbench
Function OnPlayerItemSpectralized()
	If FS_NQ08.getStage() == 15
		FS_NQ08.setStage(20)
	EndIf
EndFunction

; Event called by _00E_Phasmalist_TrinketSC
Function OnPlayerTrinketCreated()
	If bCreatedFirstTrinket == False
		bCreatedFirstTrinket = True		
		If !PlayerREF.HasSpell(_00E_Phasmalist_A3_MoveApparationToPlayerShout)
			PlayerREF.AddShout(_00E_Phasmalist_A3_MoveApparationToPlayerShout)
			Game.unlockWord(_00E_A3_Phasmalist_MoveApparationToPlayer1)
		EndIf
	
		If FS_NQ08.getStage() == 10
			FS_NQ08.setStage(15)
		EndIf
	
		If _00E_AchievementsEnabled.GetValueInt() == 1 && _00E_PhasmalistAchievementUnlocked.GetValueInt() == 0
			_00E_PhasmalistAchievementUnlocked.SetValueInt(1)
			Game.UnlockAchievement("END_APPARITION_01")
		EndIf
	EndIf
	RemoveEnchantmentItem()
	AddEnchantmentItem()
EndFunction

Event OnUnlockSoulsmithNovice(string eventName, string strArg, float numArg, Form sender)
	If FS_NQ08.getStage() == 7
		FS_NQ08.setStage(10)
		UnRegisterForModEvent("Phasmalist_Learn_Soulsmith1")
	EndIf
EndEvent

Function ForceInventoryContainerLoad(ObjectReference cont)
	cont.additem(_00E_UpdateInventoryItem)
	cont.removeitem(_00E_UpdateInventoryItem)
EndFunction

Function AddEnchantmentItem()
	PlayerREF.AddItem(_00E_Phasmalist_Invis_EnchantmentRequirement, PlayerREF.GetAV("Enchanting") as int, True)
EndFunction

Function RemoveEnchantmentItem(Int _iDefault = 0)
	If _iDefault > 0
		PlayerREF.RemoveItem(_00E_Phasmalist_Invis_EnchantmentRequirement, _iDefault, true)
	Else
		PlayerREF.RemoveItem(_00E_Phasmalist_Invis_EnchantmentRequirement, PlayerREF.GetItemCount(_00E_Phasmalist_Invis_EnchantmentRequirement), true)
	EndIf
EndFunction


;=====================================================================================
;              						EQUIPPED TRINKET
;=====================================================================================

_00E_Phasmalist_TrinketSC EquippedTrinket

Function OnTrinketEquipped(_00E_Phasmalist_TrinketSC trinket)
	EquippedTrinket = Trinket
EndFunction

Function OnTrinketUnequipped(_00E_Phasmalist_TrinketSC trinket)
	If trinket == EquippedTrinket
		EquippedTrinket = None
	EndIf
	UnsummonApparition()
EndFunction

_00E_Phasmalist_TrinketSC Function GetEquippedTrinket()
	Return EquippedTrinket
EndFunction

Bool Function IsTrinketEquipped()
	Return (EquippedTrinket != None)
EndFunction


;=====================================================================================
;              						APPARITION CONTROL
;=====================================================================================

Keyword Property _00E_Phasmalist_NoSummonLocation Auto
Keyword Property _00E_Phasmalist_NoSummonLocationTown Auto
Perk Property _00E_Class_Phasmalist_P08_B_PhasmalicVeil Auto

Int CurrentSummonState = 0

Int Property SUMMON_STATE_IDLE = 0 AutoReadOnly
Int Property SUMMON_STATE_SUMMONING = 1 AutoReadOnly
Int Property SUMMON_STATE_UNSUMMONING = 2 AutoReadOnly
Int Property SUMMON_STATE_BUSY = 3 AutoReadOnly

Bool Function IsBadApparitionLocation(Location loc)
	If loc 
		If loc.HasKeyword(_00E_Phasmalist_NoSummonLocation)
			Return True
		ElseIf loc.HasKeyword(_00E_Phasmalist_NoSummonLocationTown) && PlayerREF.HasPerk(_00E_Class_Phasmalist_P08_B_PhasmalicVeil) == False
			Return True
		EndIf
	EndIf

	Return False
EndFunction

Actor Function GetApparitionRef()
	Return NewApparitionAlias.GetActorRef()
EndFunction

Actor Function GetApparitionFailsafeRef()
	If CurrentSummonState == SUMMON_STATE_UNSUMMONING
		Return None
	EndIf
	Return NewApparitionAlias.AliasFailsafeRef
EndFunction

Bool Function IsApparitionSpawned()
	If NewApparitionAlias.GetActorRef()
		Return (CurrentSummonState != SUMMON_STATE_UNSUMMONING) ; It exists and is not being unsummoned
	Else 
		Return (CurrentSummonState == SUMMON_STATE_SUMMONING) ; It's not there yet but spawning
	EndIf
EndFunction

Bool Function IsApparitionChangingSummonStatus()
	Return (CurrentSummonState == SUMMON_STATE_SUMMONING || CurrentSummonState == SUMMON_STATE_UNSUMMONING)
EndFunction

Function SummonApparition(ObjectReference placeAtRef, Bool bPhasmalismTankMode = False, Bool bSilent = False)
	If _TryLockSummonState(SUMMON_STATE_SUMMONING) == False
		Return
	EndIf

	UnregisterTeleportKey()

	If bPhasmalismTankMode && bSilent == False
		bSilent = True
		NewApparitionAlias.PlaySummonExplosion(placeAtRef)
	EndIf

	; Unsummon previous apparition
	If NewApparitionAlias.GetRef() != None
		NewApparitionAlias.Unsummon(bSilent)
	EndIf

	; Create new apparition
	_00E_Phasmalist_TrinketSC trinket = GetEquippedTrinket()
	If trinket
		Actor summonedRef = placeAtRef.placeAtMe(trinket.connectedApparation, abInitiallyDisabled = True) as Actor
		If summonedRef
			NewApparitionAlias.ForceRefTo(summonedRef)
			NewApparitionAlias.Summon(trinket, placeAtRef, bPhasmalismTankMode, bSilent)
		EndIf
	EndIf

	RegisterTeleportKey()

	_UnlockSummonState()
EndFunction

Function UnsummonApparition(Bool bSilent = False)
	If _TryLockSummonState(SUMMON_STATE_UNSUMMONING)
		UnregisterTeleportKey()
		If NewApparitionAlias.GetRef() != None
			NewApparitionAlias.Unsummon(bSilent)
		EndIf
		RegisterTeleportKey()

		_UnlockSummonState()	
	EndIf
EndFunction

Function TeleportApparitionToPlayer(Bool bTeleportInFront = False, Bool bSilent = False)
	If _TryLockSummonState(SUMMON_STATE_BUSY)
		NewApparitionAlias.TeleportToPlayer(bTeleportInFront, bSilent)
		_UnlockSummonState()
	EndIf
EndFunction

Function SetApparitionCombatStyle(CombatStyle newCombatStyle)
	If _TryLockSummonState(SUMMON_STATE_BUSY)
		NewApparitionAlias.SetCombatStyle(newCombatStyle)
		_UnlockSummonState()
	EndIf
EndFunction

Function ShowApparitionStats()
	If _TryLockSummonState(SUMMON_STATE_BUSY)
		NewApparitionAlias.ShowStatsMenu()
		_UnlockSummonState()
	EndIf
EndFunction

Function ShowApparitionEquipment()
	If _TryLockSummonState(SUMMON_STATE_BUSY)
		NewApparitionAlias.ShowEquipment()
		_UnlockSummonState()
	EndIf
EndFunction

Function TransformApparitionToWerewolf(Actor akSpellTarget)
	If _TryLockSummonState(SUMMON_STATE_BUSY)
		NewApparitionAlias.TransformToWerewolf(akSpellTarget)
		_UnlockSummonState()
	EndIf
EndFunction

Function TransformApparitionFromWerewolf(Actor akSpellTarget)
	If _TryLockSummonState(SUMMON_STATE_BUSY)
		NewApparitionAlias.TransformFromWerewolf(akSpellTarget, False, False)
		_UnlockSummonState()
	EndIf
EndFunction

Bool Function _TryLockSummonState(Int newSummonState)
	While CurrentSummonState != SUMMON_STATE_IDLE
		If newSummonState == SUMMON_STATE_BUSY || newSummonState == SUMMON_STATE_UNSUMMONING
			; There's no point to do newSummonState if the apparition is being unsummoned
			If CurrentSummonState == SUMMON_STATE_UNSUMMONING 
				Return False
			EndIf
		EndIf
		Utility.WaitMenuMode(0.1)
	EndWhile

	CurrentSummonState = newSummonState
	Return True
EndFunction

Function _UnlockSummonState()
	CurrentSummonState = SUMMON_STATE_IDLE
EndFunction


;=====================================================================================
;              						CALL APPARITION KEY
;=====================================================================================

Int Property iTeleportApparitionKeyCode = 34 Auto Hidden

Location Property _00E_Dreamworld_Location Auto

Function ChangeTeleportKey(int iKeyCode)
	UnregisterTeleportKey()
	iTeleportApparitionKeyCode = iKeyCode
	RegisterTeleportKey()
EndFunction

Function RegisterTeleportKey()
	If iTeleportApparitionKeyCode != 0
		RegisterForKey(iTeleportApparitionKeyCode)
	EndIf
EndFunction

Function UnregisterTeleportKey()
	If iTeleportApparitionKeyCode != 0
		UnregisterForKey(iTeleportApparitionKeyCode)
	EndIf
EndFunction

Event OnKeyDown(int iKeyCode)
	If iKeyCode == iTeleportApparitionKeyCode
		UnregisterTeleportKey()
		If Utility.IsInMenuMode() == False && UI.IsTextInputEnabled() == False && Game.IsFightingControlsEnabled() && UI.IsMenuOpen("Dialogue Menu") == False && PlayerREF.GetCurrentLocation() != _00E_Dreamworld_Location
			If IsApparitionSpawned() == False || _00E_Phasmalist_TankMode.GetValue() != 0
				_00E_Phasmalist_NoApparitionCurrentlySummoned.Show()
			Else
				TeleportApparitionToPlayer(True)
			EndIf
		EndIf
		RegisterTeleportKey()
	EndIf
EndEvent