ScriptName MacDIHOImmortality extends ActiveMagicEffect
{Reacts when the player is in bleedout; also acts as controller for the quest which dynamically attaches the rebind script to shrines of the Divines.}

import StorageUtil

Actor Property PlayerRef auto
Armor Property pAmuletOfStendarr auto
Armor Property pBeggarClothes auto
EffectShader Property decayEffect auto
EffectShader Property decayEffect2 auto
MiscObject Property pGold auto
Quest Property pMQSovngarde auto
Quest Property pMQSovngarde2 auto
Quest Property pMacDIHOController auto
Quest Property pMacDIHOSoulBinding auto
Sound Property pDeathFXSound auto
ObjectReference Property fallBackSovn auto
ObjectReference Property pSpawnPoint auto
ObjectReference Property pDeathStorage auto
ObjectReference Property pPanicStorage auto
ObjectReference Property pSoulShard auto
ObjectReference Property pDeathMarkFX auto
Weapon Property pBattleAxe auto
MacDIHOCommonFunctions Property Common auto

Event OnEffectStart(Actor akTarget, Actor akCaster)
    ; Ensure polling interval is valid
    float fPollingInterval = Common.ValidatePollingInterval()

    ; Start "death watch" heartbeat
    RegisterForSingleUpdate(fPollingInterval)
EndEvent

Event OnUpdate()
    ; Check to see if player is bleeding out
    If (PlayerRef.IsBleedingOut())
        BrokenVessel()
    EndIf

    ; Check for shutdown state
    If (Common.GetDIHOState() > 2)
        ; Stop/start controller quest to trigger shutdown
        If (!pMacDIHOController.IsStopped())
            pMacDIHOController.Stop()
        EndIf

        ; Wait until quest has stopped before restarting - Stop() followed by an immediate Start() might fail
        int j = 0
        While (!pMacDIHOController.IsStopped() && j < 50)
            Utility.Wait(0.1)
            j += 1
        EndWhile

        pMacDIHOController.Start()
    Else
        ; Ensure polling interval is valid
        float fPollingInterval = Common.ValidatePollingInterval()

        ; Stop/start soul binding quest to populate bind activator references
        If (!pMacDIHOSoulBinding.IsStopped())
            pMacDIHOSoulBinding.Stop()
        EndIf

        ; Wait until quest has stopped before restarting - Stop() followed by an immediate Start() might fail
        int i = 0
        While (!pMacDIHOSoulBinding.IsStopped() && i < (fPollingInterval * 10))
            Utility.Wait(0.1)
            i += 1
        EndWhile

        pMacDIHOSoulBinding.Start()

        ; Continue "death watch" heartbeat
        RegisterForSingleUpdate(fPollingInterval)
    EndIf
EndEvent

Function BrokenVessel()
    ; If the player has an Amulet of Stendarr in their possession, they get a free pass (no loss of gold or items)
    If (PlayerRef.GetItemCount(pAmuletOfStendarr) > 0)
        ; Amulet is destroyed in the process
        PlayerRef.RemoveItem(pAmuletOfStendarr, 1, true)

        ; Death sequence without moving corpse marker
        PlayDeathFX(false)

        ; Player is returned to bind point at full health
        ReturnToBindPoint(false)

        ; Let them know their amulet was consumed in protecting them
        Debug.Notification("My Amulet of Stendarr has crumbled to dust.")
    Else
        ; Death sequence, move corpse marker
        PlayDeathFX(true)

        ; If DIHOBehavior is set to 1 or 2, all prior unclaimed items get dumped into panic storage (lost forever without cheating)
        int nDIHOBehavior = Common.GetDIHOBehavior()

        ;If (nDIHOBehavior == 1 || nDIHOBehavior == 2)
        ;    pDeathStorage.RemoveAllItems(pPanicStorage, true)
        ;EndIf
		
		If (nDIHOBehavior == 1 || nDIHOBehavior == 2)
			pDeathStorage.RemoveItem(pGold, 999999999, true, pPanicStorage) ; on second death, remove all gold to panic storage (lost forever without cheating)
			StorageUtil.SetIntValue(none, "volatileSkillXPdarkSouls", 0) ; on second death, permanently remove all lost XP
			StorageUtil.SetIntValue(none, "volatileUsableXPdarkSouls", 0)
		EndIf
		
		If (nDIHOBehavior == 1 || nDIHOBehavior == 2)
			PlayerRef.RemoveItem(pBeggarClothes, 999999999, true)
			PlayerRef.RemoveAllItems(pDeathStorage, true, false) ; items moved to temporary container
			PlayerRef.AddItem(pBeggarClothes, 1, true)
			int removedXP = StorageUtil.GetIntValue(none, "volatileSkillXP", 0)
			If removedXP > 0
				StorageUtil.SetIntValue(none, "volatileSkillXPdarkSouls", removedXP)
				StorageUtil.SetIntValue(none, "volatileSkillXP", 0) ; remove all non-permanent XP until shard is reclaimed
			EndIf
			StorageUtil.SetIntValue(none, "volatileUsableXPdarkSouls", Common.RemoveXPPoints())
		EndIf

        ; If DIHOBehavior set to 2 (hardcore), the player loses everything except quest items (and receives a set of beggar's robes for modesty's sake)
        ;If (nDIHOBehavior == 2)
        ;    PlayerRef.RemoveItem(pBeggarClothes, 999999999, true)
        ;    PlayerRef.RemoveAllItems(pDeathStorage, true, false)
        ;    PlayerRef.AddItem(pBeggarClothes, 1, true)
        ; If DIHOBehavior set to 1 (default), the player loses all of their gold
        ;ElseIf (nDIHOBehavior == 1)
        ;    PlayerRef.RemoveItem(pGold, 999999999, true, pDeathStorage)
        ;EndIf

        ; Player is returned to bind point at partial health
        ReturnToBindPoint(true)
		
		;Let them know where to find their power
		Debug.Notification("A shard of my power lingers at the site of my defeat...")
    EndIf
EndFunction

Function PlayDeathFX(bool bSetNewDeathMark)
    If (!bSetNewDeathMark)
        Debug.Notification("I am defeated, but my soul remains intact.")
    Else
        Debug.Notification("I...cannot go on...")
		
        ; For an unprotected death, apply permanent attribute damage if a prior soul shard went unclaimed (if the player has activated this feature)
        int nAttributeDamage = Common.ValidateAttributeDamage()

        If (nAttributeDamage > 0 && pSoulShard.GetParentCell() != pDeathStorage.GetParentCell())     ; if the soul shard is already outside of Oblivion, it went unclaimed
            PlayerRef.ModActorValue("Health", 0 - nAttributeDamage)
            PlayerRef.ModActorValue("Magicka", 0 - nAttributeDamage)
            PlayerRef.ModActorValue("Stamina", 0 - nAttributeDamage)

            Common.IncrementDIHOAttributeDamageTotal(nAttributeDamage)
        EndIf
    EndIf

    Game.DisablePlayerControls()
    PlayerRef.SetNoBleedoutRecovery(true)
    Utility.Wait(3.5)

    PlayerRef.SetAlpha(0, true)
    decayEffect.Play(PlayerRef)
    Utility.Wait(2.0)

    decayEffect2.Play(PlayerRef, 5.0)
    pDeathFXSound.Play(PlayerRef)
    Game.FadeOutGame(true, true, 5.0, 5.0)

    If (bSetNewDeathMark)
        ; Put the player's soul shard at the spot they "died"
        pSoulShard.MoveTo(PlayerRef, abMatchRotation = false)
        pDeathMarkFX.MoveTo(pSoulShard, abMatchRotation = false)
    EndIf

    Utility.Wait(5)
EndFunction

Function ReturnToBindPoint(bool bPartialHealth)
    ; Player recovers
    PlayerRef.SetNoBleedoutRecovery(false)
    Game.EnablePlayerControls()

    ; Dispel all effects from player
    PlayerRef.DispelAllSpells()

    ; Heal player, then damage to 50% if this was an unprotected death
    PlayerRef.RestoreActorValue("Health", 100000.0)

    If (bPartialHealth)
        float fPlayerBaseHealth = PlayerRef.GetBaseActorValue("Health")
        PlayerRef.DamageActorValue("Health", (fPlayerBaseHealth - Common.GetDIHOAttributeDamageTotal()) / 2.0)
    EndIf

    ; Force clear the player's hands (to eliminate unusable hand glitch when death occurs while a spell is equipped)
    PlayerRef.AddItem(pBattleAxe, 1, true)
    PlayerRef.EquipItem(pBattleAxe, false, true)

	; Equip modesty item
	PlayerRef.GetActorBase().SetInvulnerable(true)
	Utility.Wait(0.1)
	If (bPartialHealth)
		PlayerRef.EquipItem(pBeggarClothes, false, true) ; player has no items unless free pass
	EndIf
	PlayerRef.UnequipItem(pBattleAxe, false, true)
    PlayerRef.RemoveItem(pBattleAxe, 1, true)
	
    ; Return player to bind point
    If (pMQSovngarde.IsRunning() || pMQSovngarde2.IsRunning())        ; Sovngarde safety code
        PlayerRef.MoveTo(fallBackSovn)
    Else
        PlayerRef.MoveTo(pSpawnPoint)
    EndIf
	
    decayEffect.Stop(PlayerRef)
    decayEffect2.Stop(PlayerRef)
	
    ; Player is invulnerable briefly on respawn
    PlayerRef.GetActorBase().SetInvulnerable(true)

    ; Invulnerability lasts for ten seconds
    Utility.Wait(10)

    PlayerRef.SetAlpha(1, true)
    PlayerRef.GetActorBase().SetInvulnerable(false)
    PlayerRef.SetNoBleedoutRecovery(true)
EndFunction
