//::///////////////////////////////////////////////
//:: Shadowcasting Mystery Hook File.
//:: shd_mysthook.nss
//:://////////////////////////////////////////////
/*

    This file acts as a hub for all code that
    is hooked into the mystery scripts for Shadowcasting

*/
//:://////////////////////////////////////////////
//:: Created By: Stratovarius
//:: Created On: 7-2-2019
//:://////////////////////////////////////////////

#include "x2_inc_spellhook"
#include "prc_inc_spells"
#include "inc_utility"
#include "prc_inc_itmrstr"

// This function holds all functions that are supposed to run before the actual
// spellscript gets run. If this functions returns FALSE, the spell is aborted
// and the spellscript will not run
int ShadPreMystCastCode();

// Does the counterspelling for Warp Spell,
// and the storing of the spell for later use
int WarpMyst(object oCaster, int nMyst)
{
    int nWarp = GetLocalInt(oCaster, "WarpSpell");
    // If Warp Spell isn't set, just keep going
    if (!nWarp) return TRUE;
    
    object oShadow = GetLocalObject(oCaster, "WarpSpell"); // The one who cast Warp Spell
    
    DeleteLocalInt(oCaster, "WarpSpell");
    DeleteLocalObject(oCaster, "WarpSpell");
    
    int nLevel = GetShadowcasterLevel(oCaster);
    if (d20() + nWarp > d20() + nLevel) // Won the caster level check
    {
        // Set a marker on the Shadowcaster
        SetLocalInt(oShadow, "WarpSpellSuccess", TRUE);  
        FloatingTextStringOnCreature("You have successfully warped your opponent's "+GetMysteryName(nMyst), oShadow, FALSE);
        ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SOUL_TRAP), oCaster);
        return FALSE;
    }
    
    FloatingTextStringOnCreature("Your warp spell has failed", oShadow, FALSE);
    return TRUE;
}

// Stores the spell for use with Echo Spell
void EchoMyst(object oCaster, int nMyst)
{
    int nEcho = GetLocalInt(oCaster, "EchoSpell");
    // If Echo Spell isn't set, just skip
    if (nEcho)
    {   
        object oShadow = GetLocalObject(oCaster, "EchoSpell"); // The one who cast Echo Spell
        SetLocalInt(oShadow, "EchoedSpell", nMyst);
        FloatingTextStringOnCreature("You have echoed " + GetMysteryName(nMyst) + " and have one round to cast it", oShadow, FALSE);
        DelayCommand(9.0, DeleteLocalInt(oShadow, "EchoedSpell"));
        DeleteLocalInt(oCaster, "EchoSpell");
        DeleteLocalObject(oCaster, "EchoSpell"); 
    }
}

int MysterySpellFailure(object oShadow, int nMystId, int nMystLevel, int nClass)
{
    // Mysteries of both types ignore arcane spell failure
    if (GetIsMysterySupernatural(oShadow, nMystId, nClass) || GetIsMysterySLA(oShadow, nMystId, nClass))
        return TRUE;

    int nASF = GetArcaneSpellFailure(oShadow);

    if(Random(100) < nASF)
    {
        int nFail = TRUE;
        // Still spell helps
        if((GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_1, oShadow) && nMystLevel <= 3)
        || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_2, oShadow) && nMystLevel <= 6)
        || (GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_3, oShadow) && nMystLevel <= 9))
        {
            nFail = FALSE;
        }
        if(nFail)
        {
            //52946 = Spell failed due to arcane spell failure!
            FloatingTextStrRefOnCreature(52946, oShadow, FALSE);
            return FALSE;
        }
    }
    return TRUE;
}

int ShadowTime(object oCaster, object oTarget, int bSpellIsHostile)
{
    // PnP Timestop
    if(GetPRCSwitch(PRC_TIMESTOP_NO_HOSTILE))
    {
        if(GetHasSpellEffect(SPELL_TIME_STOP, oCaster)
        || GetHasSpellEffect(4032, oCaster)          //epic spell: Greater Timestop
        || GetHasSpellEffect(14236, oCaster)         //psionic power: Temporal Acceleration
        || GetHasSpellEffect(18428, oCaster))        //Mystery MYST_SHADOW_TIME
        {
            if(!GetIsObjectValid(oTarget)
            || oTarget != oCaster
            || bSpellIsHostile)
            {
                return FALSE;
            }
        }
    }
    
    return TRUE;
}    

//------------------------------------------------------------------------------
// if FALSE is returned by this function, the spell will not be cast
// the order in which the functions are called here DOES MATTER, changing it
// WILL break the crafting subsystems
//------------------------------------------------------------------------------
int ShadPreMystCastCode()
{
    object oShadow = OBJECT_SELF;
    object oTarget = PRCGetSpellTargetObject();
    int nMystID = PRCGetSpellId();
    int nMystLevel = GetMysteryLevel(oShadow);
    int nShadowcastingClass = GetShadowcastingClass(oShadow);
    int bMystIsHostile = Get2DACache("spells", "HostileSetting", nMystID) == "1";

    int nContinue = !ExecuteScriptAndReturnInt("prespellcode",oShadow);

    //---------------------------------------------------------------------------
    // Break any spell require maintaining concentration
    //---------------------------------------------------------------------------
    X2BreakConcentrationSpells();

    //---------------------------------------------------------------------------
    // Check for PRC spell effects
    //---------------------------------------------------------------------------
    if(nContinue)
        nContinue = PRCSpellEffects(oShadow, oTarget, nMystID, nMystLevel, nShadowcastingClass, bMystIsHostile, -1);
        
    if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #1: " + IntToString(nContinue));
        
    //---------------------------------------------------------------------------
    // Run Arcane Spell Failure
    //---------------------------------------------------------------------------
    if (nContinue)
        nContinue = MysterySpellFailure(oShadow, nMystID, nMystLevel, nShadowcastingClass);  
        
    if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #2: " + IntToString(nContinue));    
        
    //---------------------------------------------------------------------------
    // Run Shadow Time
    //---------------------------------------------------------------------------
    if (nContinue)
        nContinue = ShadowTime(oShadow, oTarget, bMystIsHostile);  
        
    if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #3: " + IntToString(nContinue));            

    //---------------------------------------------------------------------------
    // Run Grappling Concentration Check
    //---------------------------------------------------------------------------
    if (nContinue)
        nContinue = GrappleConc(oShadow, nMystLevel);
        
    if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #4: " + IntToString(nContinue));            

    //---------------------------------------------------------------------------
    // This stuff is only interesting for player characters we assume that use
    // magic device always works and NPCs don't use the crafting feats or
    // sequencers anyway. Thus, any NON PC spellcaster always exits this script
    // with TRUE (unless they are DM possessed or in the Wild Magic Area in
    // Chapter 2 of Hordes of the Underdark.
    //---------------------------------------------------------------------------
    if(!GetIsPC(oShadow)
    && !GetPRCSwitch(PRC_NPC_HAS_PC_SPELLCASTING))
    {
        if(!GetIsDMPossessed(oShadow) && !GetLocalInt(GetArea(oShadow), "X2_L_WILD_MAGIC"))
        {
            return TRUE;
        }
    }

    //---------------------------------------------------------------------------
    // Run use magic device skill check
    //---------------------------------------------------------------------------
    if (nContinue)
    {
        nContinue = X2UseMagicDeviceCheck(oShadow);
    }

    //-----------------------------------------------------------------------
    // run any user defined spellscript here
    //-----------------------------------------------------------------------
    if (nContinue)
    {
        nContinue = X2RunUserDefinedSpellScript();
    }

    //---------------------------------------------------------------------------
    // Check for the new restricted itemproperties
    //---------------------------------------------------------------------------
    if(nContinue
    && GetIsObjectValid(GetSpellCastItem())
    && !CheckPRCLimitations(GetSpellCastItem(), oShadow))
    {
        SendMessageToPC(oShadow, "You cannot use "+GetName(GetSpellCastItem()));
        nContinue = FALSE;
    }
    
    //---------------------------------------------------------------------------
    // Warp Spell
    //---------------------------------------------------------------------------    
    if (nContinue)
    {
        nContinue = WarpMyst(oShadow, nMystID);
    } 
    
    if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #5: " + IntToString(nContinue));    
    
    EchoMyst(oShadow, nMystID);

    //---------------------------------------------------------------------------
    // The following code is only of interest if an item was targeted
    //---------------------------------------------------------------------------
    if (GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_ITEM)
    {

        //-----------------------------------------------------------------------
        // Check if spell was used to trigger item creation feat
        //-----------------------------------------------------------------------
        if (nContinue) {
            nContinue = !ExecuteScriptAndReturnInt("x2_pc_craft", oShadow);
        }

        //-----------------------------------------------------------------------
        // * Execute item OnSpellCast At routing script if activated
        //-----------------------------------------------------------------------
        if (GetModuleSwitchValue(MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS) == TRUE)
        {
            SetUserDefinedItemEventNumber(X2_ITEM_EVENT_SPELLCAST_AT);
            int nRet = ExecuteScriptAndReturnInt(GetUserDefinedItemEventScriptName(oTarget), oShadow);
            if (nRet == X2_EXECUTE_SCRIPT_END)
            {
                return FALSE;
            }
        }

        //-----------------------------------------------------------------------
        // Prevent any spell that has no special coding to handle targetting of items
        // from being cast on items. We do this because we can not predict how
        // all the hundreds spells in NWN will react when cast on items
        //-----------------------------------------------------------------------
        if (nContinue) {
            nContinue = X2CastOnItemWasAllowed(oTarget);
        }
    }

    //Cleaning spell variables used for holding the charge
    if(!GetLocalInt(oShadow, "PRC_SPELL_EVENT"))
    {
        DeleteLocalInt(oShadow, "PRC_SPELL_CHARGE_COUNT");
        DeleteLocalInt(oShadow, "PRC_SPELL_CHARGE_SPELLID");
        DeleteLocalObject(oShadow, "PRC_SPELL_CONC_TARGET");
        DeleteLocalInt(oShadow, "PRC_SPELL_METAMAGIC");
        DeleteLocalManifestation(oShadow, "PRC_POWER_HOLD_MANIFESTATION");
        DeleteLocalMystery(oShadow, "MYST_HOLD_MYST");
    }
    else if(GetLocalInt(oShadow, "PRC_SPELL_CHARGE_SPELLID") != PRCGetSpellId())
    {   //Sanity check, in case something goes wrong with the action queue
        DeleteLocalInt(oShadow, "PRC_SPELL_EVENT");
    }

    if(DEBUG) DoDebug("ShadPreMystCastCode nContinue #6: " + IntToString(nContinue));    
    return nContinue;
}