Scriptname _00E_Phasmalist_NewApparitionAlias extends ReferenceAlias
; script that controls the apparition stats calculation and initialization. Additionally, it is responsible for the control of the apparition communication while it is summoned

; we prefer to place new actors instead of using only one actor
; due to the problem of keeping track of all apparitions, 
; which might create memory leaks because of persistence;
; Additionally, temporary or accidental changes do not cumulate
; and lead to problems

; balancing data
Float Property fApparitionManaRate = 1.0 AutoReadOnly

Float Property fPlayerStatsMult = 0.20 AutoReadOnly
Float Property fPlayerConjSkillMult = 0.10 AutoReadOnly
Float Property fPlayerEnchSkillMult = 0.15 AutoReadOnly

Float Property fArcaneFeverLevel01 = 7.00 AutoReadOnly
Float Property fArcaneFeverLevel02 = 6.00 AutoReadOnly
Float Property fArcaneFeverLevel03 = 5.00 AutoReadOnly

Float Property fArcaneFeverModWarrior = -1.0 AutoReadOnly
Float Property fArcaneFeverModRanger = 0.0 AutoReadOnly
Float Property fArcaneFeverModHybrid = 0.0 AutoReadOnly
Float Property fArcaneFeverModMage = 1.0 AutoReadOnly

Int Property iGhostlyMageBoostDestructionPowerMod1 = 7 AutoReadOnly
Int Property iGhostlyMageBoostDestructionPowerMod2 = 15 AutoReadOnly
Int Property iGhostlyMageBoostDestructionPowerMod3 = 25 AutoReadOnly

Int Property iGhostlyMageBoostMagicka1 = 15 AutoReadOnly
Int Property iGhostlyMageBoostMagicka2 = 40 AutoReadOnly
Int Property iGhostlyMageBoostMagicka3 = 75 AutoReadOnly

Int Property iGhostlyRangerBoostArchery1 = 7 AutoReadOnly
Int Property iGhostlyRangerBoostArchery2 = 17 AutoReadOnly
Int Property iGhostlyRangerBoostArchery3 = 30 AutoReadOnly

Int Property iGhostlyRangerBoostCritChance1 = 7 AutoReadOnly
Int Property iGhostlyRangerBoostCritChance2 = 15 AutoReadOnly
Int Property iGhostlyRangerBoostCritChance3 = 25 AutoReadOnly

Int Property iGhostlyWarriorBoostMelee1 = 7 AutoReadOnly
Int Property iGhostlyWarriorBoostMelee2 = 17 AutoReadOnly
Int Property iGhostlyWarriorBoostMelee3 = 30 AutoReadOnly

Int Property iGhostlyWarriorBoostArmorSkill1 = 7 AutoReadOnly
Int Property iGhostlyWarriorBoostArmorSkill2 = 17 AutoReadOnly
Int Property iGhostlyWarriorBoostArmorSkill3 = 30 AutoReadOnly

Float Property DEFAULT_HEAL_RATE = 50.0 AutoReadOnly

Explosion Property _00E_Phasmalist_EnterWorldExp Auto

Perk Property _00E_Class_Phasmalist_P03_Talent_SummonApparation_01 Auto
Perk Property _00E_Class_Phasmalist_P03_Talent_SummonApparation_02 Auto
Perk Property _00E_Class_Phasmalist_P03_Talent_SummonApparation_03 Auto

Static Property XMarker Auto

Perk Property _00E_Class_Phasmalist_P08_A_EssenceSplit Auto

; Boost perks
Perk Property _00E_Class_Phasmalist_P05_A_Shamanism_01 Auto
Perk Property _00E_Class_Phasmalist_P05_A_Shamanism_02 Auto
Perk Property _00E_Class_Phasmalist_P05_A_Shamanism_03 Auto

Perk Property _00E_Class_Phasmalist_P05_B_Mischief_01 Auto
Perk Property _00E_Class_Phasmalist_P05_B_Mischief_02 Auto
Perk Property _00E_Class_Phasmalist_P05_B_Mischief_03 Auto

Perk Property _00E_Class_Phasmalist_P05_C_Violence_01 Auto
Perk Property _00E_Class_Phasmalist_P05_C_Violence_02 Auto
Perk Property _00E_Class_Phasmalist_P05_C_Violence_03 Auto

Message Property _00E_Phasmalist_CurrentlyEquipped Auto

ImageSpaceModifier Property _00E_ArkanistenfieberIMOD Auto
ImageSpaceModifier Property _00E_Phasmalist_ApparitionEnterWorldIMOD Auto

Sound Property _00E_FS_IncreaseArcaneFeverM Auto

Message Property _00E_Player_sArcaneFeverIncreased Auto
GlobalVariable Property _00E_Phasmalist_IsApparitionSummoned Auto

; The apparition should not be able to learn summon creature and mystical/mythical spells
FormList Property _00E_PhasmalistApparition_SpellBooks Auto
FormList Property _00E_PhasmalistApparition_Spells Auto

Int iCurrentBoostMagicka ; this is set in adjustBoost() to have the attribute set correctly in the stats calculation

Actor Property PlayerREF Auto

FormList Property _00E_AllAmmos Auto

_00E_Phasmalist_TrinketSC SummonTrinket

; Stores the reference of the current apparition while it's properly spawned, not being unsummoned, not dying, etc.
; Needed for the failsafe in OnLoadGame() and some other shenanigans
Actor Property AliasFailsafeRef = None Auto Hidden

Bool bTeleportHealRate = False


;=====================================================================================
;              						SUMMON & SETUP
;=====================================================================================

Function Summon(_00E_Phasmalist_TrinketSC trinket, ObjectReference moveToRef, Bool bPhasmalismTankMode, Bool bSilent)
	Actor akSelf = Self.GetActorReference()

	SummonTrinket = trinket
	bActivationBusy = bPhasmalismTankMode ; Reset just in case
	bTeleportHealRate = False ; Reset just in case
	_00E_Phasmalist_IsApparitionSummoned.SetValue(1)

	Setup() ; init stats before any armor buffs are applied

	If bSilent == False
		_00E_Phasmalist_ApparitionEnterWorldIMOD.Apply()
	EndIf

	(akSelf.GetActorBase() as _00E_Phasmalist_ApparationSC).inventoryContainer.removeAllItems(akSelf, false, true)

	If bPhasmalismTankMode == False
		Int i = akSelf.GetNumItems()
		While i > 0
			i -= 1
			Form item = akSelf.GetNthForm(i)
			If item as Book
				_RegisterSpellBook(akSelf, item)
			EndIf
		EndWhile
	EndIf

	akSelf.BlockActivation()

	If bPhasmalismTankMode
		akSelf.SetActorValue("Variable01", 1.0)
		akSelf.EnableAI(False)
	Else
		If moveToRef == PlayerREF
			_MoveToPlayer(akSelf, False)
		EndIf

		GoToState("Working")
	EndIf
	
	akSelf.Enable()
	AliasFailsafeRef = akSelf

	If bPhasmalismTankMode
		; MoveTo needs to be done after Enable or the apparition ends on the neareast navmesh instead of the tank
		akSelf.MoveTo(moveToRef)
	EndIf

	If bSilent == False
		PlaySummonExplosion(akSelf)
	EndIf

	SendModEvent("Phasmalist_ApparationSummon")
EndFunction

Function PlaySummonExplosion(ObjectReference markerRef)
	ObjectReference expMarker = _PlaceExplosionEx(_00E_Phasmalist_EnterWorldExp, markerRef, 15.0)
	_DeleteMarker(expMarker)
EndFunction

Int Property BOOST_CLASS_MAGE    = 0 AutoReadOnly
Int Property BOOST_CLASS_RANGER  = 1 AutoReadOnly
Int Property BOOST_CLASS_WARRIOR = 2 AutoReadOnly

Function Setup()
	Actor akSelf = Self.GetActorReference()

	SetupBehaviour(akSelf)
	SetupNotPersistentStats(akSelf)
		
	Float fAttributeMult = (fPlayerConjSkillMult * PlayerREF.GetActorValue("Conjuration") + fPlayerEnchSkillMult * PlayerREF.GetActorValue("Enchanting")) / 100.0 + 1.0

	ApplyClassBoost(akSelf, BOOST_CLASS_MAGE, _00E_Class_Phasmalist_P05_A_Shamanism_01, _00E_Class_Phasmalist_P05_A_Shamanism_02, _00E_Class_Phasmalist_P05_A_Shamanism_03)
	ApplyClassBoost(akSelf, BOOST_CLASS_RANGER, _00E_Class_Phasmalist_P05_B_Mischief_01, _00E_Class_Phasmalist_P05_B_Mischief_02, _00E_Class_Phasmalist_P05_B_Mischief_03)
	ApplyClassBoost(akSelf, BOOST_CLASS_WARRIOR, _00E_Class_Phasmalist_P05_C_Violence_01, _00E_Class_Phasmalist_P05_C_Violence_02, _00E_Class_Phasmalist_P05_C_Violence_03)

	If PlayerREF.HasPerk(_00E_Class_Phasmalist_P08_A_EssenceSplit)
		Float fMaxAttributeValue = PlayerREF.GetBaseAV("Health")
		Float fPlayerMagicka = PlayerREF.GetBaseAV("Magicka")
		If fPlayerMagicka > fMaxAttributeValue
			fMaxAttributeValue = fPlayerMagicka
		EndIf
		Float fPlayerStamina = PlayerREF.GetBaseAV("Stamina")
		If fPlayerStamina > fMaxAttributeValue
			fMaxAttributeValue = fPlayerStamina
		EndIf
		akSelf.ForceActorValue("Health", ((akSelf.GetBaseAV("Health") + fMaxAttributeValue * fPlayerStatsMult) * fAttributeMult) as int)
		akSelf.ForceActorValue("Magicka", (((akSelf.GetBaseAV("Magicka") + fMaxAttributeValue * fPlayerStatsMult) * fAttributeMult) as int) + iCurrentBoostMagicka)
		akSelf.ForceActorValue("Stamina", ((akSelf.GetBaseAV("Stamina") + fMaxAttributeValue * fPlayerStatsMult) * fAttributeMult) as int)

	Else
		akSelf.ForceActorValue("Health", ((akSelf.GetBaseAV("Health") + PlayerREF.GetBaseAV("Health") * fPlayerStatsMult) * fAttributeMult) as int)
		akSelf.ForceActorValue("Magicka", (((akSelf.GetBaseAV("Magicka") + PlayerREF.GetBaseAV("Magicka") * fPlayerStatsMult) * fAttributeMult) as int) + iCurrentBoostMagicka)
		akSelf.ForceActorValue("Stamina", ((akSelf.GetBaseAV("Stamina") + PlayerREF.GetBaseAV("Stamina") * fPlayerStatsMult) * fAttributeMult) as int)
	EndIf
EndFunction	

Function SetupBehaviour(Actor akSelf)
	Int iRelationship = akSelf.GetRelationshipRank(PlayerREF)
	If iRelationship < 3 && iRelationship >= 0
		akSelf.SetRelationshipRank(PlayerREF, 3)
	EndIf

	akSelf.SetPlayerTeammate()
	akSelf.SetActorValue("Confidence", 4) ; the apparition should never flee
	akSelf.SetActorValue("Aggression", 0) ; the apparition should not initiate combat (-> stealth playstyle)
	akSelf.SetActorValue("Sneak", 100)
	akSelf.SetActorValue("Invisibility", 1) ; make the apparition undetectable when sneaking (-> stealth playstyle)
	akSelf.IgnoreFriendlyHits() ; make the apparition ignore friendly fire from the PC
EndFunction

Function SetupNotPersistentStats(Actor akSelf)
	akSelf.GetActorBase().SetCombatStyle(SummonTrinket.GetUsedCombatStyle())

	If bTeleportHealRate == False
		akSelf.SetActorValue("HealRate", DEFAULT_HEAL_RATE)
	Else
		akSelf.SetActorValue("HealRate", 0.0)
	EndIf
	akSelf.SetActorValue("MagickaRate", fApparitionManaRate)

	;failsafe since setav doesn't work sometimes
	If bTeleportHealRate == False
		akSelf.ForceActorValue("HealRate", DEFAULT_HEAL_RATE)
	Else
		akSelf.ForceActorValue("HealRate", 0.0)
	EndIf
	akSelf.ForceActorValue("MagickaRate", fApparitionManaRate)
EndFunction

Function ApplyClassBoost(Actor akSelf, Int iBoostClass, Perk boostPerk1, Perk boostPerk2, Perk boostPerk3)
	If PlayerREF.HasPerk(boostPerk1)
		If PlayerREF.HasPerk(boostPerk3)
			_AddClassBoost(akSelf, iBoostClass, 3)
		ElseIf PlayerREF.HasPerk(boostPerk2)
			_AddClassBoost(akSelf, iBoostClass, 2)
		Else
			_AddClassBoost(akSelf, iBoostClass, 1)
		EndIf
	EndIf
EndFunction

Function _AddClassBoost(Actor akSelf, Int iBoostClass, Int iBoostLevel)
	If iBoostClass == BOOST_CLASS_MAGE
		Int iDestructionPowerMod = 0
		If iBoostLevel == 3
			iDestructionPowerMod = iGhostlyMageBoostDestructionPowerMod3
			iCurrentBoostMagicka = iGhostlyMageBoostMagicka3 ; since magicka is boosted later again, we have to save this boost and apply it later to prevent overwriting old boosts
		ElseIf iBoostLevel == 2
			iDestructionPowerMod = iGhostlyMageBoostDestructionPowerMod2
			iCurrentBoostMagicka = iGhostlyMageBoostMagicka2
		Else
			iDestructionPowerMod = iGhostlyMageBoostDestructionPowerMod1
			iCurrentBoostMagicka = iGhostlyMageBoostMagicka1
		EndIf
		;akSelf.ForceActorValue("DestructionPowerMod", akSelf.GetActorValue("DestructionPowerMod") + iDestructionPowerMod)
		akSelf.ModActorValue("DestructionPowerMod", iDestructionPowerMod)
	ElseIf iBoostClass == BOOST_CLASS_RANGER
		Int iBoostArchery = 0
		Int iBoostCritChance = 0
		If iBoostLevel == 3
			iBoostArchery = iGhostlyRangerBoostArchery3
			iBoostCritChance = iGhostlyRangerBoostCritChance3
		ElseIf iBoostLevel == 2
			iBoostArchery = iGhostlyRangerBoostArchery2
			iBoostCritChance = iGhostlyRangerBoostCritChance2
		Else
			iBoostArchery = iGhostlyRangerBoostArchery1
			iBoostCritChance = iGhostlyRangerBoostCritChance1
		EndIf

		;akSelf.ForceActorValue("Marksman", akSelf.GetActorValue("Marksman") + iBoostArchery)
		akSelf.ModActorValue("Marksman", iBoostArchery)
		;akSelf.ForceActorValue("CritChance", akSelf.GetActorValue("CritChance") + iBoostCritChance)
		akSelf.ModActorValue("CritChance", iBoostCritChance)
	ElseIf iBoostClass == BOOST_CLASS_WARRIOR
		Int iBoostMelee = 0
		Int iBoostArmor = 0
		If iBoostLevel == 3
			iBoostMelee = iGhostlyWarriorBoostMelee3
			iBoostArmor = iGhostlyWarriorBoostArmorSkill3
		ElseIf iBoostLevel == 2
			iBoostMelee = iGhostlyWarriorBoostMelee2
			iBoostArmor = iGhostlyWarriorBoostArmorSkill2
		Else
			iBoostMelee = iGhostlyWarriorBoostMelee1
			iBoostArmor = iGhostlyWarriorBoostArmorSkill1
		EndIf

		;akSelf.ForceActorValue("HeavyArmor", akSelf.GetActorValue("HeavyArmor") + iBoostArmor)
		akSelf.ModActorValue("HeavyArmor", iBoostArmor)
		;akSelf.ForceActorValue("LightArmor", akSelf.GetActorValue("LightArmor") + iBoostArmor)
		akSelf.ModActorValue("LightArmor", iBoostArmor)
		; MeleeDamage AV seems to be not working, it's actually always zero
		;akSelf.ForceActorValue("MeleeDamage", akSelf.GetActorValue("MeleeDamage") + iBoostMelee)
		;akSelf.ModActorValue("MeleeDamage", iBoostMelee)
		akSelf.ModActorValue("OneHanded", iBoostMelee)
		akSelf.ModActorValue("TwoHanded", iBoostMelee)
	Else
		Debug.Notification("Phasmalist Control Quest has issues: Unsupported boost class " + iBoostClass)
	EndIf
Endfunction


;=====================================================================================
;              						UNSUMMON & CLEANUP
;=====================================================================================

Function Unsummon(Bool bSilent)
	Actor akSelf = self.GetActorReference()

	HealthBarManager.Hide(akSelf)

	GoToState("")
	AliasFailsafeRef = None

	TransformFromWerewolf(akSelf, True, bSilent)

	If bSilent == False
		If akSelf.Is3DLoaded()
			akSelf.PlaceAtMe(_00E_Phasmalist_EnterWorldExp)
		EndIf
		AddArcaneFever()
	EndIf

	_00E_Phasmalist_IsApparitionSummoned.SetValue(0)
	akSelf.RemoveAllItems((akSelf.GetActorBase() as _00E_Phasmalist_ApparationSC).inventoryContainer, False, True)
	akSelf.Disable()
	Self.Clear()
	SummonTrinket = None

	Utility.wait(1.0)
	akSelf.Delete()
	
	SendModEvent("Phasmalist_ApparationUnSummon")
	
EndFunction

Function AddArcaneFever()
	Actor akSelf = Self.GetActorReference()
	Float fHealthLostPercentage = 0
	Float fMaxArcaneFeverAdd = 0
	
	If akSelf.IsDead()
		fHealthLostPercentage = 1.0
	Else
		fHealthLostPercentage = 1.0 - akSelf.GetActorValuePercentage("health")
		If fHealthLostPercentage > 1.0
			fHealthLostPercentage = 1.0
		EndIf
	EndIf

	Int iTalentLevel = _00E_TalentLibrary.GetPlayerTalentLevel(_00E_Class_Phasmalist_P03_Talent_SummonApparation_01, _00E_Class_Phasmalist_P03_Talent_SummonApparation_02, _00E_Class_Phasmalist_P03_Talent_SummonApparation_03)
	If iTalentLevel == 1
		fMaxArcaneFeverAdd = fArcaneFeverLevel01
	ElseIf iTalentLevel == 2
		fMaxArcaneFeverAdd = fArcaneFeverLevel02
	Else
		fMaxArcaneFeverAdd = fArcaneFeverLevel03
	EndIf
	
	Int iTrinketType = SummonTrinket.type
	If iTrinketType == 0
		fMaxArcaneFeverAdd += fArcaneFeverModWarrior
	ElseIf iTrinketType == 1
		fMaxArcaneFeverAdd += fArcaneFeverModRanger
	ElseIf iTrinketType == 2
		fMaxArcaneFeverAdd += fArcaneFeverModMage
	Else
		fMaxArcaneFeverAdd += fArcaneFeverModHybrid
	EndIf

	Float fArcaneFeverAdd = fMaxArcaneFeverAdd * fHealthLostPercentage
	If fArcaneFeverAdd > 0
		PlayerREF.ModActorValue("LastFlattered", -fArcaneFeverAdd)
		_00E_ArkanistenfieberIMOD.ApplyCrossFade()
		_00E_FS_IncreaseArcaneFeverM.Play(PlayerREF)
		_00E_Player_sArcaneFeverIncreased.Show(fArcaneFeverAdd, -1 * PlayerREF.GetActorValue("LastFlattered"))
	EndIf
EndFunction


;=====================================================================================
;              						OTHER ACTIONS
;=====================================================================================

Function OnLoadGame()
	; sometimes reloading a save with a summoned apparition from a game state without one can result in the apparationAlias (old alias) being cleared.
	; To fix this, the script additionally keeps track of its content in a variable and refills itself onGameLoad
	If AliasFailsafeRef
		Self.ForceRefTo(AliasFailsafeRef)
	EndIf

	Actor akSelf = GetActorReference()
	If akSelf
		SetupNotPersistentStats(akSelf)
		_ShowHealthBar()
	EndIf
EndFunction

Function TeleportToPlayer(Bool bTeleportInFront, Bool bSilent)
	Actor akSelf = Self.GetActorReference()
	If akSelf
		; Zero out-of-combat heal rate for teleport and a few seconds after it.
		; This is to prevent the apparition regenerating health IN combat on "Call Apparition" spam.
		UnregisterForUpdate()
		akSelf.ForceActorValue("HealRate", 0.0)
		bTeleportHealRate = True

		akSelf.Disable()
		_MoveToPlayer(akSelf, bTeleportInFront)
		akSelf.Enable()
		If bSilent == False
			akSelf.PlaceAtMe(_00E_Phasmalist_EnterWorldExp)
		EndIf
		
		RegisterForSingleUpdate(2.0)
	EndIf
EndFunction

Function SetCombatStyle(CombatStyle newCombatStyle)
	Actor akSelf = Self.GetActorReference()
	If akSelf
		_SetCombatStyle(akSelf, newCombatStyle)
	EndIf
EndFunction

Function ShowStatsMenu()
	Actor akSelf = Self.GetActorReference()
	If akSelf
		UIExtensions.initMenu("UIStatsMenu")
		UIExtensions.openMenu("UIStatsMenu", akSelf)
	EndIf
EndFunction

Function ShowEquipment()
	Actor akSelf = Self.GetActorReference()

	If akSelf
		String text = _00E_Phasmalist_CurrentlyEquipped.GetName() + "\n\n"

		Weapon EquippedWeapon = akSelf.GetEquippedWeapon()
		If EquippedWeapon
			text = text + EquippedWeapon.GetName() + "\n" ;weapon
			If EquippedWeapon.IsBow()
				Form[] arrows = _00E_AllAmmos.ToArray()
				Int i = 0
				Int nArrowCount
				While i < arrows.Length
					nArrowCount = akSelf.GetItemCount(arrows[i])
					If nArrowCount > 0
						text = text + (arrows[i].GetName() + " (" + nArrowCount +")\n")
					EndIf
					i += 1
				EndWhile
			EndIf
		EndIf
		text = _AddWornFormAsString(akSelf, 0x00000200, text) ;shield
		text = _AddWornFormAsString(akSelf, 0x00001000, text) ;circlet
		text = _AddWornFormAsString(akSelf, 0x00000004, text) ;body
		text = _AddWornFormAsString(akSelf, 0x00000008, text) ;hands
		text = _AddWornFormAsString(akSelf, 0x00000080, text) ;feet
		text = _AddWornFormAsString(akSelf, 0x00000020, text) ;amulet
		text = _AddWornFormAsString(akSelf, 0x00000040, text) ;ring

		Debug.MessageBox(text)
	EndIf	
EndFunction


;=====================================================================================
;              						WEREWOLF
;=====================================================================================

FormList Property _00E_Phasmalist_CombatStyleWerewolfList Auto
Spell Property _00E_FS_Affinity_Soulcaller_ApparationWolfBoostSP Auto
Idle Property WerewolfTransformBack Auto
Idle Property IdleWerewolfTransformation Auto
Explosion Property _00E_FS_Theriantrophist_TransEXP Auto
Race Property _00E_Theriantrophist_PlayerWerewolfRace Auto
ObjectReference Property _00E_FS_Affinity_Soulcaller_RemoveAndAddApparationItemsChest Auto
Idle Property LooseFullBodyStagger Auto
Armor Property _00E_InvisibleHelmet_Armor Auto

Float Property WEREWOLF_TRANSFORM_UNARMED_DAMAGE_BOOST = 20.0 AutoReadOnly
Float Property WEREWOLF_TRANSFORM_HEALTH_BOOST = 40.0 AutoReadOnly

Race WerewolfTranform_OriginalRace = None

Bool Function IsTransformedToWerewolf()
	Return (WerewolfTranform_OriginalRace != None)
EndFunction

Function TransformToWerewolf(Actor akSpellTarget)
	ObjectReference expMarker

	Actor akSelf = GetActorReference()
	If (akSelf == None) || (akSelf != akSpellTarget)
		Return
	EndIf

	bActivationBusy = True

	akSelf.SetPlayerTeammate(False)
	akSelf.EvaluatePackage()
	WerewolfTranform_OriginalRace = akSelf.getRace()
	Utility.Wait(0.2)
	akSelf.PlayIdle(IdleWerewolfTransformation)
	Utility.Wait(0.8)

	_SetCombatStyle(akSelf, _00E_Phasmalist_CombatStyleWerewolfList.GetAt(0) as CombatStyle)
	_00E_FS_Affinity_Soulcaller_ApparationWolfBoostSP.SetNthEffectMagnitude(0, WEREWOLF_TRANSFORM_UNARMED_DAMAGE_BOOST)
	_00E_FS_Affinity_Soulcaller_ApparationWolfBoostSP.SetNthEffectMagnitude(1, WEREWOLF_TRANSFORM_HEALTH_BOOST)
	akSelf.AddSpell(_00E_FS_Affinity_Soulcaller_ApparationWolfBoostSP)
	expMarker = _PlaceExplosionEx(_00E_FS_Theriantrophist_TransEXP, akSelf, 100.0)
	Utility.Wait(0.4) ; Pause to synchronize the explosion with the transformation animation(s)
	akSelf.SetLookAt(akSelf) ; Reset headtracking to prevent the head getting stuck looking sideways after the tranformation
	akSelf.UnequipAll()
	akSelf.SetRace(_00E_Theriantrophist_PlayerWerewolfRace)

	_DeleteMarker(expMarker)
	Utility.Wait(0.2)
	akSelf.SetPlayerTeammate(True)
	akSelf.EvaluatePackage()

	bActivationBusy = False
EndFunction

Function TransformFromWerewolf(Actor akSpellTarget, Bool bIsUnsummoned, Bool bSilent)
	Actor akSelf = GetActorReference()
	If (IsTransformedToWerewolf() == False) || (akSelf == None) || (akSelf != akSpellTarget)
		Return
	EndIf

	bActivationBusy = True
	bTeleportHealRate = False

	If akSelf.IsDead()
		_TransformFromWerewolfDead(akSelf, bIsUnsummoned, bSilent)
	Else
		_TransformFromWerewolfAlive(akSelf, bIsUnsummoned, bSilent)
	EndIf

	WerewolfTranform_OriginalRace = None

	bActivationBusy = False
EndFunction

Function _TransformFromWerewolfDead(Actor akSelf, Bool bIsUnsummoned, Bool bSilent)
	ObjectReference expMarker = None

	GoToState("")

	If bSilent == False
		expMarker = _PlaceExplosionEx(_00E_FS_Theriantrophist_TransEXP, akSelf, 15.0)
	EndIf
	; RemoveAllItems is needed here for _TriggerEquipmentUpdate to work
	akSelf.RemoveAllItems(_00E_FS_Affinity_Soulcaller_RemoveAndAddApparationItemsChest)
	akSelf.SetPlayerTeammate(False)
	akSelf.Resurrect()
	_00E_FS_Affinity_Soulcaller_RemoveAndAddApparationItemsChest.RemoveAllItems(akSelf)
	Utility.Wait(0.2) ; Pause for the ragdoll to settle. Without it the apparition appears in the air on SetRace
	_SetCombatStyle(akSelf, SummonTrinket.GetUsedCombatStyle())
	akSelf.RemoveSpell(_00E_FS_Affinity_Soulcaller_ApparationWolfBoostSP)
	If expMarker
		_DeleteMarker(expMarker)
		expMarker = None
	EndIf
	akSelf.SetRace(WerewolfTranform_OriginalRace)
	_TriggerEquipmentUpdate(akSelf, False)
	akSelf.ClearLookAt() ; Reenable headtracking

	If bIsUnsummoned == False
		GoToState("Working")
	EndIf
	akSelf.Kill()
EndFunction

Function _TransformFromWerewolfAlive(Actor akSelf, Bool bIsUnsummoned, Bool bSilent)
	ObjectReference expMarker = None

	akSelf.SetPlayerTeammate(False)
	akSelf.EvaluatePackage()
	akSelf.PlayIdle(WerewolfTransformBack)
	akSelf.RemoveSpell(_00E_FS_Affinity_Soulcaller_ApparationWolfBoostSP)
	_SetCombatStyle(akSelf, SummonTrinket.GetUsedCombatStyle())
	If bSilent == False
		expMarker = _PlaceExplosionEx( _00E_FS_Theriantrophist_TransEXP, akSelf, 100.0)
	EndIf
	akSelf.SetRace(WerewolfTranform_OriginalRace)
	_TriggerEquipmentUpdate(akSelf, True)
	If expMarker
		_DeleteMarker(expMarker)
		expMarker = None
	EndIf
	Utility.Wait(0.2)
	akSelf.ClearLookAt() ; Reenable headtracking
	If bIsUnsummoned == False
		SetupBehaviour(akSelf)
		SetupNotPersistentStats(akSelf)
		akSelf.EvaluatePackage()
	EndIf
EndFunction

Function _TriggerEquipmentUpdate(Actor akSelf, Bool bPlayStaggerIdle)
	akSelf.SetPlayerTeammate(True) ; Yes, the apparition has to be a player's teammate for AddItem-EquipItem below to trigger equipment update
	If bPlayStaggerIdle
		akSelf.PlayIdle(LooseFullBodyStagger)
	EndIf

	akSelf.AddItem(_00E_InvisibleHelmet_Armor, 1, True)
	akSelf.EquipItem(_00E_InvisibleHelmet_Armor, 1)
	akSelf.RemoveItem(_00E_InvisibleHelmet_Armor, 1)
EndFunction


;=====================================================================================
;              						HELPER FUNCTIONS
;=====================================================================================

String Function _AddWornFormAsString(Actor a, int slot, string s)
	Form wornForm = a.GetWornForm(slot)
	If wornForm
		Return s + wornForm.GetName() + "\n"
	EndIf
	Return s
EndFunction

Function _ShowHealthBar()
	Actor akSelf = Self.GetActorReference()
	If akSelf && akSelf.IsDead() == False
		HealthBarManager.Show(akSelf)
	EndIf
EndFunction

Float Property FRONT_TELEPORT_DISTANCE = 72.0 AutoReadOnly
Float Property BACK_TELEPORT_DISTANCE = 96.0 AutoReadOnly

Function _MoveToPlayer(Actor akSelf, Bool bTeleportInFront)
	Float fHeadingAngle = PlayerREF.GetAngleZ()
	If bTeleportInFront
		akSelf.MoveTo(PlayerREF, FRONT_TELEPORT_DISTANCE * Math.sin(fHeadingAngle), FRONT_TELEPORT_DISTANCE * Math.cos(fHeadingAngle), 0, True)
		akSelf.SetAngle(0, 0, fHeadingAngle + 180.0)
	Else
		fHeadingAngle += 180.0
		akSelf.MoveTo(PlayerREF, BACK_TELEPORT_DISTANCE * Math.sin(fHeadingAngle), BACK_TELEPORT_DISTANCE * Math.cos(fHeadingAngle), 0, True)
	EndIf
EndFunction

Function _SetCombatStyle(Actor akSelf, CombatStyle newCombatStyle)
	akSelf.StopCombat()
	akSelf.GetActorBase().SetCombatStyle(newCombatStyle)
EndFunction

Spell Function _GetSpellFromSpellBook(Form akBaseItem)
	; The apparition should not be able to learn summon creature and mystical/mythical spells
	Int iSpellBook = _00E_PhasmalistApparition_SpellBooks.Find(akBaseItem)
	If iSpellBook >= 0
		Return (_00E_PhasmalistApparition_Spells.GetAt(iSpellBook) as Spell)
	Else
		Return None
	EndIf
EndFunction

Function _RegisterSpellBook(Actor akSelf, Form akBaseItem)
	Spell bookSpell = _GetSpellFromSpellBook(akBaseItem)
	If bookSpell
		Self.GetActorReference().AddSpell(bookSpell, False)
	EndIf
EndFunction

Function _UnregisterSpellBook(Actor akSelf, Form akBaseItem)
	Spell bookSpell = _GetSpellFromSpellBook(akBaseItem)
	If bookSpell
		Self.GetActorReference().RemoveSpell(bookSpell)
	EndIf
EndFunction

ObjectReference Function _PlaceExplosionEx(Explosion exp, ObjectReference refSpot, Float fOffsetZ)
	ObjectReference expMarker = refSpot.PlaceAtMe(XMarker)
	expMarker.MoveTo(refSpot, 0.0, 0.0, fOffsetZ)
	expMarker.PlaceAtMe(exp)
	Return expMarker
EndFunction

Function _DeleteMarker(ObjectReference refMarker)
	refMarker.Disable()
	refMarker.Delete()
EndFunction


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

Bool bActivationBusy = False

State Working

	Event OnActivate(ObjectReference akActivator)
		If akActivator == PlayerREF && bActivationBusy == False
			bActivationBusy = True

			UIExtensions.initMenu("UIWheelMenu")
			UIExtensions.SetMenuPropertyIndexBool("UIWheelMenu", "optionEnabled", 0, true)
			UIExtensions.SetMenuPropertyIndexBool("UIWheelMenu", "optionEnabled", 1, true)
			UIExtensions.SetMenuPropertyIndexBool("UIWheelMenu", "optionEnabled", 4, true)
			UIExtensions.SetMenuPropertyIndexBool("UIWheelMenu", "optionEnabled", 5, true)
			
			UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionText", 0, "$View_Attributes")
			UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionLabelText", 0, "$View_Attributes")

			If IsTransformedToWerewolf() == False
				UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionText", 1, "$Adapt_Combat_Style")
				UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionLabelText", 1, "$Adapt_Combat_Style")

				UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionText", 4, "$Currently_Equipped")
				UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionLabelText", 4, "$Currently_Equipped")
			EndIf
			
			UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionText", 5, "$Quit")
			UIExtensions.SetMenuPropertyIndexString("UIWheelMenu", "optionLabelText", 5, "$Quit")
			
			Int iChoice = UIExtensions.OpenMenu("UIWheelMenu", Self.GetActorReference())
			If iChoice == 0
				ShowStatsMenu()
			ElseIf iChoice == 1
				SummonTrinket.ChooseApparitionCombatStyle()
			ElseIf iChoice == 4
				ShowEquipment()
			EndIf

			bActivationBusy = False
		EndIf
	EndEvent

	Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
		If akBaseItem as Book
			_RegisterSpellBook(Self.GetActorReference(), akBaseItem)
		EndIf
	EndEvent

	Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
		If akBaseItem as Book
			Actor akSelf = Self.GetActorReference()
			If akSelf.GetItemCount(akBaseItem) <= 0
				_UnregisterSpellBook(akSelf, akBaseItem)
			EndIf
		EndIf
	EndEvent

	Event OnLoad()
		bActivationBusy = False ; Safeguard
		_ShowHealthBar()
	EndEvent

	Event OnDying(Actor akKiller)
		AliasFailsafeRef = None
	EndEvent

	Event OnDeath(Actor akKiller)
		AliasFailsafeRef = None
		(GetOwningQuest() as _FS_Phasmalist_ControlQuest).UnsummonApparition()
	EndEvent

	Event OnCombatStateChanged(Actor akTarget, Int aeCombatState)
		If aeCombatState == 1
			_ShowHealthBar()
		EndIf
	EndEvent

	Event OnUpdate()
		If bTeleportHealRate
			bTeleportHealRate = False
			GetActorReference().ForceActorValue("HealRate", DEFAULT_HEAL_RATE)
		EndIf
	EndEvent

EndState