// HCR v3.2.0 - Added Death System code and cleaned up. Thanks Sunjammer.
//::////////////////////////////////////////////////////////////////////////////
//:: FileName:  NW_S0_RaisDead
//::////////////////////////////////////////////////////////////////////////////
/*
    Spell script to bring a PC or an NPC back to life with one health.

    In HCR, death has many variants:
    ----------------------------------

    - Death, Limbo: PC dies and is in due course transferred to limbo. In their
      place a static Corpse NPC, an "invisible" DeathCorpse placeable and an PCT
      (Player Corpse Token) item are created. A caster can use the DeathCorpse
      to revive the PC.  Currently if the caster tries to revive a PC before
      they are transferred to limbo they will be informed that this is not
      possible. The DeathCorpse can be used to revive a PC who is off-line.

    - Death, No Limbo: PC dies and remains where they fell.  At their location
      the same 3 components are created (Corpse, DeathCorpse and PCT). A caster
      can use the DeathCorpse to revive the PC or cast directly onto the PC. The
      DeathCorpse can be used to revive a PC who is off-line.

    - No Death, No Limbo: PC dies and remains where they fell.  They have the
      option to respawn however if they choose to wait for help a caster can
      cast directly onto the PC to revive them.

    - NPC: NPC dies and remains where they fell (depending on other options). A
      Caster can cast directly onto the NPC to revive them.


    Issues Unresolved @ 3.2.0
    -------------------------

    - NOTFUGUE is used to prevent a PC from being transfered to fugue while in
      the process of being revived. The flag is checked by a delayed function
      in hc_on_play_death which otherwise transfers the PC to fugue. However as
      noted above MOVING pre-empts all attempts to revive a PC who has not yet
      been transfered to Fugue.  NOTFUGUE appears to have been rendered
      redundant.


    Original BW Notes
    -----------------

    When cast on placeables, you get a default error message.
    * You can specify a different message in X2_L_RESURRECT_SPELL_MSG_RESREF
    * You can turn off the message by setting the variable to -1

*/
//::////////////////////////////////////////////////////////////////////////////
#include "hc_inc"
#include "hc_inc_dcorpse"
#include "hc_inc_remeff"
#include "hc_inc_rezpen"
#include "hc_text_activate"
#include "x2_inc_spellhook"
//::////////////////////////////////////////////////////////////////////////////
void TakeHPs(object oTarget)
{
    int nDam = (GetCurrentHitPoints(oTarget) - 1);
    effect eDam = EffectDamage(nDam);
    ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget);
}
//::////////////////////////////////////////////////////////////////////////////
void main()
{
    // If code within the PreSpellCastHook reports FALSE, do not run this spell.
    if (X2PreSpellCastCode() == FALSE)
        return;

    object oCaster = OBJECT_SELF;
    object oTarget = GetSpellTargetObject();
    object oCreature;

    // Get system being used, oMod courtesy of hc_inc.
    int nUsingLimbo      = GetLocalInt(oMod, "LIMBO");
    int nUsingPersist    = GetLocalInt(oMod, "PERSIST");
    //int nUsingCorpses  = GetLocalInt(oMod, "NPCCORPSE");
    int nUsingRezPenalty = GetLocalInt(oMod, "REZPENALTY");
    int nUsingDeath      = GetLocalInt(oMod, "DEATHSYSTEM");

    // Cast at Death Corpse flag.
    int nCastAtDC = FALSE;


    // -------------------------------------------------------------------------
    // Part 1: Find the creature to revive.
    // -------------------------------------------------------------------------


    if (GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
    {
        // Target is a PC or an NPC and hence the creature to revive.
        oCreature = oTarget;
    }
    else if (GetObjectType(oTarget) == OBJECT_TYPE_PLACEABLE)
    {
        // Target is a DeathCorpse (requires Death System) or other placeable.
        if (nUsingDeath && GetTag(oTarget) == "DeathCorpse")
        {
            // Valid: get creature to revive and raise cast at DC flag.
            oCreature = GetLocalObject(oTarget, "Owner");
            nCastAtDC = TRUE;
        }
        else
        {
            // Invalid: send "can't revive that" message to caster and abort.
            int nStrRef = GetLocalInt(oTarget, "X2_L_RESURRECT_SPELL_MSG_RESREF");
            if (nStrRef == 0)
            {
                nStrRef = 83861;
            }
            if (nStrRef != -1)
            {
                // Inform caster that they cannot revive this object.
                FloatingTextStrRefOnCreature(nStrRef, oCaster);
            }
            return;
        }
    }
    else
    {
        // Target is invalid or an invalid type: abort.
        return;
    }

    // Pre-emptive abort if MOVING to LIMBO (see above).
    if (nUsingLimbo && GetLocalInt(oCreature, "MOVING"))
    {
        SendMessageToPC(oCaster, NOTRAISE);
        return;
    }


    // -------------------------------------------------------------------------
    // Part 2: Revive the creature.
    // -------------------------------------------------------------------------


    int nIsPC = GetIsPC(oCreature);

    // Raise/lower NOTFUGUE flag according to location (see above).
    if (nUsingLimbo && GetTag(GetArea(oCreature)) == "FuguePlane")
        DeleteLocalInt(oCreature, "NOTFUGUE");
    else
        SetLocalInt(oCreature, "NOTFUGUE", TRUE);

    // Visual revival.
    effect eVFX = EffectVisualEffect(VFX_IMP_RAISE_DEAD);
    ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eVFX, GetLocation(oTarget));

    // Actual revival.
    if (nCastAtDC && GetIsObjectValid(oCreature) == FALSE)
    {
        // ---------------------------------------------------------------------
        // Off-line revival can only occur when casting at a DeathCorpse.
        // ---------------------------------------------------------------------
        string sID = GetLocalString(oTarget, "Pkey");

        // Inform caster, store revival location.
        SendMessageToPC(oCaster, NOTONLINE);
        SetPersistentLocation(oMod, "RESLOC" + sID, GetLocation(oCaster));

        // Revive the off-line PC.
        if (GetIsDM(oCaster))
        {
            // DM provides a True Resserection.
            if (nUsingPersist)
                SetCampaignInt("HCRPC" + sID, "PlayerState", PWS_PLAYER_STATE_RESTRUE);
            else
                SetPersistentInt(oMod, "PlayerState" + sID, PWS_PLAYER_STATE_RESTRUE);
        }
        else
        {
            // PC/NPC provides a Resserection.
            if (nUsingPersist)
                SetCampaignInt("HCRPC" + sID, "PlayerState", PWS_PLAYER_STATE_RESURRECTED);
            else
                SetPersistentInt(oMod, "PlayerState" + sID, PWS_PLAYER_STATE_RESURRECTED);
        }

        return;
    }
    else
    {
        // ---------------------------------------------------------------------
        // On-line revival can occur when casting at a DeathCorpse or at a dead
        // PC or NPC with various combinations of systems and targets.
        // ---------------------------------------------------------------------
        // 1: PC, nUsingDeath, nUsingLimbo, nCastAtDC
        //      - PC in fugue (MOVING)
        //      - manually jump to DC
        //      - existing Heal, RemEff & Ress (hc_on_play_death)
        //      - existing kill body, DC & PCT (hc_fugue_exit)
        //      - existing player state change (hc_fugue_exit)
        // 2: PC, nUsingDeath, nCastAtDC
        //      - PC is anywhere, DC is target
        //      - manually jump to DC
        //      - manually kill body, DC & PCT
        //      - manually player state change
        //      - manually Heal, RemEff & Ress
        // 3: PC, nUsingDeath
        //      - PC (wait for help) is target
        //      - manually kill body, DC & PCT
        //      - manually player state change
        //      - manually Heal, RemEff & Ress
        // 4: PC
        //      - PC (wait for help) is target
        //      - manually Heal, RemEff & Ress
        // 5: NPC
        //      - NPC is target
        //      - manually Heal, RemEff & Ress
        // ---------------------------------------------------------------------
        string sID = GetPlayerID(oCreature);

        // Fire cast spell at event for the specified creature.
        SignalEvent(oCreature, EventSpellCastAt(oCaster, SPELL_RESURRECTION, FALSE));

        // Start reviving.
        if (nIsPC && nUsingDeath && nUsingLimbo && nCastAtDC)
        {
            // 1: Requires a jump to the DeathCorpse, everything else is done.
            AssignCommand(oCreature, JumpToObject(oTarget));
        }
        else
        {
            // 2-3: Require the body, DeathCorpse and PCT to be destoyed
            //      and for the PC's player state to be updated.
            if (nIsPC && nUsingDeath)
            {
                // 2: Requires a jump to the DeathCorpse.
                if (nCastAtDC)
                    AssignCommand(oCreature, JumpToObject(oTarget));

                // Destroy the cloned NPC corpse.
                DelayCommand(0.5, DestroyCorpse(oCreature));

                // Destroy DeathCorpse.
                DelayCommand(0.6, DestroyObject(oTarget));

                // Destoy the PlayerCorpseToken.
                object oPCT = GetLocalObject(oMod, "PlayerCorpse" + sID);
                DestroyObject(oPCT);

                // Set PC's player state as alive.
                SPS(oCreature, PWS_PLAYER_STATE_ALIVE);
            }

            effect eRess = EffectResurrection();
            int    nHeal = (abs(GetCurrentHitPoints(oCreature)) + GetMaxHitPoints(oCreature));
            effect eHeal = EffectHeal(nHeal);

            // 2-5: Require revival, healing and removal of effects.
            ApplyEffectToObject(DURATION_TYPE_INSTANT, eRess, oCreature);
            ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oCreature);
            RemoveEffects(oCreature);
        }
    }


    // -------------------------------------------------------------------------
    // Part 3: Post-revival common stuff and clean up.
    // -------------------------------------------------------------------------


    // Apply RezPenalty if appropriate.
    if (nIsPC && nUsingRezPenalty && GetIsDM(oCaster) == FALSE)
        DelayCommand(3.0, hcRezPenalty(oCreature));

    // Reset hitpoints to 1.
    DelayCommand(3.5, TakeHPs(oCreature));

    // Force any nearby hostiles to attack.
    DelayCommand(5.0, ExecuteScript("hc_attackpc" , oCreature));

    // Lower NOTFUGUE flag.
    DeleteLocalInt(oCreature, "NOTFUGUE");
}
//::////////////////////////////////////////////////////////////////////////////
