//::///////////////////////////////////////////////
//:: Name      Eldritch Blast - Normal
//:: FileName  inv_eldtch_blast.nss
//:://////////////////////////////////////////////

#include "prc_inc_sp_tch"
#include "inv_inc_invfunc"
#include "inv_invokehook"
#include "inv_inc_blast"

//internal function for delayed damage
void DoDelayedBlast(object oTarget, int nDamageType = DAMAGE_TYPE_FIRE, int nVFX = VFX_IMP_FLAME_M)
{
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(2), nDamageType), oTarget);
    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nVFX), oTarget);
}

void main()
{
    // Ugly hack to tell PreInvocationCastCode() that we are casting as warlock
    object oPC = OBJECT_SELF;
    SetLocalInt(oPC, PRC_INVOKING_CLASS, CLASS_TYPE_WARLOCK + 1);
    DelayCommand(0.0f, DeleteLocalInt(oPC, PRC_INVOKING_CLASS));

    if(!PreInvocationCastCode()) return;
    
    object oBeamTarget = PRCGetSpellTargetObject();
    location lTargetArea = PRCGetSpellTargetLocation();
    int nEssence = GetLocalInt(oPC, "BlastEssence");
    int nEssence2 = GetLocalInt(oPC, "BlastEssence2");
    int nEssenceData = GetLocalInt(oPC, "EssenceData");
    int nEssenceData2 = GetLocalInt(oPC, "EssenceData2");
    effect eEssence;

    //If no beam target object, make one
    if (!GetIsObjectValid(oBeamTarget))
    {
        oBeamTarget = CreateObject(OBJECT_TYPE_PLACEABLE, "prc_invisobj", lTargetArea);
    }

    //remove Corrupting Blast essence - it's a one-shot power
    if(nEssence == INVOKE_CORRUPTING_BLAST)
        DeleteLocalInt(oPC, "BlastEssence");
    else if(nEssence2 == INVOKE_CORRUPTING_BLAST)
        DeleteLocalInt(oPC, "BlastEssence2");

    int nBlast = GetSpellId();
    int bSculptor = GetHasFeat(FEAT_ELDRITCH_SCULPTOR, oPC);
    int nShape, nShapeLevel, bDoom;
    float fRange;

    if(nBlast == INVOKE_ELDRITCH_CONE)
    {
        nShape = SHAPE_SPELLCONE;
        fRange = bSculptor ? FeetToMeters(60.0) : FeetToMeters(30.0);
        nShapeLevel = 5;
    }
    else if(nBlast == INVOKE_ELDRITCH_LINE)
    {
        nShape = SHAPE_SPELLCYLINDER;
        fRange = bSculptor ? FeetToMeters(120.0) : FeetToMeters(60.0);
        nShapeLevel = 5;
    }
    else if(nBlast == INVOKE_ELDRITCH_DOOM)
    {
        bDoom = TRUE;
        nShape = SHAPE_SPHERE;
        fRange = bSculptor ? FeetToMeters(40.0) : FeetToMeters(20.0);
        nShapeLevel = 8;
    }

    //calculate DC for essence effects
    int nInvLevel = GetInvokerLevel(oPC, CLASS_TYPE_WARLOCK);
    int nBlastLvl = min((nInvLevel + 1) / 2, 9);
    nBlastLvl = max(nShapeLevel, max(max(nEssenceData & 0xF, nEssenceData2 & 0xF), nBlastLvl));
    int nDC = 10 + nBlastLvl + GetAbilityModifier(ABILITY_CHARISMA);
    if(GetHasFeat(FEAT_LORD_OF_ALL_ESSENCES)) nDC += 2;
    int nDmgDice = GetBlastDamageDices(oPC, nInvLevel);

    int nDamageType = nEssence ? (nEssenceData >>> 4) & 0xFFF : DAMAGE_TYPE_MAGICAL;
    int nDamageType2 = nEssence2 ? (nEssenceData2 >>> 4) & 0xFFF : DAMAGE_TYPE_MAGICAL;

    //Set correct blast damage type
    if(nDamageType != nDamageType2)
    {
        if(nDamageType != DAMAGE_TYPE_MAGICAL)
        {
            if(nDamageType2 == DAMAGE_TYPE_MAGICAL)
                nDamageType2 = nDamageType;
        }
        else if(nDamageType2 != DAMAGE_TYPE_MAGICAL)
        {
            nDamageType = nDamageType2;
        }
    }

    //get beam and impact vfx
    int nBeamVFX, nVis, nReflexSaveType;
    switch(nDamageType)
    {
        case DAMAGE_TYPE_FIRE:
              nBeamVFX        = bDoom ? VFX_IMP_PULSE_FIRE : VFX_BEAM_FIRE;
              nVis            = VFX_IMP_FLAME_M;
              nReflexSaveType = SAVING_THROW_TYPE_FIRE;
              break;
        case DAMAGE_TYPE_COLD:
              nBeamVFX        = bDoom ? VFX_IMP_PULSE_COLD : VFX_BEAM_COLD;
              nVis            = VFX_IMP_FROST_S;
              nReflexSaveType = SAVING_THROW_TYPE_COLD;
              break;
        case DAMAGE_TYPE_ACID:
              nBeamVFX        = bDoom ? VFX_IMP_PULSE_WATER : VFX_BEAM_DISINTEGRATE;
              nVis            = VFX_IMP_ACID_S;
              nReflexSaveType = SAVING_THROW_TYPE_ACID;
              break;
        case DAMAGE_TYPE_NEGATIVE:
              nBeamVFX        = bDoom ? VFX_IMP_PULSE_NEGATIVE : VFX_BEAM_BLACK;
              nVis            = VFX_IMP_NEGATIVE_ENERGY;
              nReflexSaveType = SAVING_THROW_TYPE_NEGATIVE;
              break;
        default:
              nBeamVFX        = bDoom ? VFX_IMP_PULSE_NEGATIVE : VFX_BEAM_DISINTEGRATE;
              nVis            = VFX_IMP_LIGHTNING_S;
              nReflexSaveType = SAVING_THROW_TYPE_SPELL;
              break;
    }

    effect eVis = EffectVisualEffect(nVis);

    //add second vfx if needed
    if(nDamageType != nDamageType2)
    {
        switch(nDamageType2)
        {
            case DAMAGE_TYPE_FIRE:     nVis = VFX_IMP_FLAME_M;         break;
            case DAMAGE_TYPE_COLD:     nVis = VFX_IMP_FROST_S;         break;
            case DAMAGE_TYPE_ACID:     nVis = VFX_IMP_ACID_S;          break;
            case DAMAGE_TYPE_NEGATIVE: nVis = VFX_IMP_NEGATIVE_ENERGY; break;
        }
        eVis = EffectLinkEffects(eVis, EffectVisualEffect(nVis));
    }

    int nHellFire = 0;
    if(GetIsHellFireBlast(oPC))
    {
        if(HellFireConDamage(oPC))
        {
            nHellFire = GetLevelByClass(CLASS_TYPE_HELLFIRE_WARLOCK, oPC) * 2;
            nBeamVFX = bDoom ? VFX_IMP_PULSE_FIRE : VFX_BEAM_FIRE;
        }
    }

    //Penetrating Blast
    int nPenetr = nInvLevel + SPGetPenetr();
    if(nEssence == INVOKE_PENETRATING_BLAST || nEssence2 == INVOKE_PENETRATING_BLAST)
        nPenetr += 4;

    //Get first target in spell area
    object oTarget = MyFirstObjectInShape(nShape, fRange, lTargetArea, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, GetPosition(oPC));
    while(GetIsObjectValid(oTarget))
    {
        int nDamage = d6(nDmgDice);
        if(GetHasSpellEffect(INVOKE_WILD_FRENZY, oPC))
            nDamage += 2;

        //Bane Blast
        int nRace = MyPRCGetRacialType(oTarget);
        if(nRace == ((nEssenceData >>> 16) & 0xFF) - 1
        || nRace == ((nEssenceData2 >>> 16) & 0xFF) - 1)
            nDamage += d6(2);

        //Hammer Blast
        if(GetObjectType(oTarget) != OBJECT_TYPE_CREATURE && nEssence != INVOKE_HAMMER_BLAST && nEssence2 != INVOKE_HAMMER_BLAST)
        {
            nDamage /= 2;
            if(nDamage < 1) nDamage = 1;
            nHellFire /= 2;
        }

        int nRep = bDoom ? SPELL_TARGET_SELECTIVEHOSTILE : SPELL_TARGET_STANDARDHOSTILE;
        // This heals friendly undead targets with negative energy damage
        if (nDamageType == DAMAGE_TYPE_NEGATIVE && bDoom && GetIsFriend(oTarget, oPC) && MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD)
			ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nDamage), oTarget);
        else if(spellsIsTarget(oTarget, nRep, oPC) && oTarget != oPC)
        {
            //Fire cast spell at event for the specified target
            SignalEvent(oTarget, EventSpellCastAt(oPC, INVOKE_ELDRITCH_BLAST));
            float fDelay = GetDistanceBetween(oPC, oTarget)/20;

            nDamage = PRCGetReflexAdjustedDamage(nDamage, oTarget, nDC, nReflexSaveType);
            if(nDamage > 0)
            {
                //Make SR Check
                int iSR = PRCDoResistSpell(oPC, oTarget, nPenetr);
                if(!iSR)
                {
                     //Apply secondary effects from essence invocations
                     if(nEssence == INVOKE_PENETRATING_BLAST || nEssence2 == INVOKE_PENETRATING_BLAST)
                     {
                         eEssence = EffectSpellResistanceDecrease(5);
                         if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_SPELL))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, TurnsToSeconds(1));
                     }
                     if((nEssence == INVOKE_HINDERING_BLAST || nEssence2 == INVOKE_HINDERING_BLAST) && PRCGetIsAliveCreature(oTarget))
                     {
                         eEssence = EffectSlow();
                         if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_MIND_SPELLS))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, RoundsToSeconds(1));
                     }
                     if(nEssence == INVOKE_BINDING_BLAST || nEssence2 == INVOKE_BINDING_BLAST)
                     {
                         eEssence = EffectStunned();
                         if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_MIND_SPELLS))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, RoundsToSeconds(1));
                     }
                     if(nEssence == INVOKE_BEWITCHING_BLAST || nEssence2 == INVOKE_BEWITCHING_BLAST)
                     {
                         eEssence = PRCEffectConfused();
                         if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_MIND_SPELLS))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, RoundsToSeconds(1));
                     }
                     if((nEssence == INVOKE_BESHADOWED_BLAST || nEssence2 == INVOKE_BESHADOWED_BLAST) && PRCGetIsAliveCreature(oTarget))
                     {
                         eEssence = EffectBlindness();
                         if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, RoundsToSeconds(1));
                     }
                     if((nEssence == INVOKE_HELLRIME_BLAST || nEssence2 == INVOKE_HELLRIME_BLAST))
                     {
                         eEssence = EffectAbilityDecrease(ABILITY_DEXTERITY, 4);
                         if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, TurnsToSeconds(10));
                     }
                     if(nEssence == INVOKE_UTTERDARK_BLAST || nEssence2 == INVOKE_UTTERDARK_BLAST)
                     {
                         eEssence = EffectNegativeLevel(2);
                         if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, HoursToSeconds(1));
                     }
                     if(nEssence == INVOKE_FRIGHTFUL_BLAST || nEssence2 == INVOKE_FRIGHTFUL_BLAST)
                     {
                         effect eDur2 = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
                         effect eFear = EffectFrightened();
                         effect eAttackD = EffectAttackDecrease(2);
                         effect eDmgD = EffectDamageDecrease(2,DAMAGE_TYPE_BLUDGEONING|DAMAGE_TYPE_PIERCING|DAMAGE_TYPE_SLASHING);
                         effect SaveD = EffectSavingThrowDecrease(SAVING_THROW_ALL,2);
                         effect Skill = EffectSkillDecrease(SKILL_ALL_SKILLS,2);

                         eEssence = EffectLinkEffects(eDmgD, eDur2);
                         eEssence = EffectLinkEffects(eEssence, eAttackD);
                         eEssence = EffectLinkEffects(eEssence, SaveD);
                         eEssence = EffectLinkEffects(eEssence, eFear);
                         eEssence = EffectLinkEffects(eEssence, Skill);

                         if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget, nDC, SAVING_THROW_TYPE_FEAR))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, TurnsToSeconds(1));
                     }
                     if(nEssence == INVOKE_NOXIOUS_BLAST || nEssence2 == INVOKE_NOXIOUS_BLAST)
                     {
                         eEssence = EffectDazed();
                         if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL))
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, TurnsToSeconds(1));
                     }
                     if(nEssence == INVOKE_CORRUPTING_BLAST || nEssence2 == INVOKE_CORRUPTING_BLAST)
                     {
                         if(CheckTurnUndeadUses(oPC, 1))
                         {
                             int nRed = GetLevelByClass(CLASS_TYPE_ELDRITCH_DISCIPLE, oPC) / 2;
                             if(nRed < 1) nRed = 1;
                             eEssence = EffectSavingThrowDecrease(SAVING_THROW_WILL, nRed);
                             eEssence = EffectLinkEffects(eEssence, EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE));
                             ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, RoundsToSeconds(1));
                         }
                         else
                             SpeakStringByStrRef(40550);//"This ability is tied to your turn undead ability, which has no more uses for today."
                     }
                     if((nEssence == INVOKE_SICKENING_BLAST || nEssence2 == INVOKE_SICKENING_BLAST) && PRCGetIsAliveCreature(oTarget))
                     {
                        eEssence = EffectSickened();
                        if(!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nDC, SAVING_THROW_TYPE_SPELL))
                            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eEssence, oTarget, 60.0);
                     }
                     if((nEssence == INVOKE_BRIMSTONE_BLAST || nEssence2 == INVOKE_BRIMSTONE_BLAST) && !GetLocalInt(oTarget, "BrimstoneFire"))
                     {
                         if(!PRCMySavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, SAVING_THROW_TYPE_FIRE))
                         {
                             SetLocalInt(oTarget, "BrimstoneFire", TRUE);
                             int nDuration = nInvLevel / 5;
                             DelayCommand(RoundsToSeconds(nDuration), DeleteLocalInt(oTarget, "BrimstoneFire"));

                             int i;
                             float fRound = RoundsToSeconds(1);
                             for(i = 1; i <= nDuration; i++)
                             {
                                 DelayCommand(fRound * i, DoDelayedBlast(oTarget));
                             }
                         }
                     }
                     //Apply the VFX impact
                     ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
                 }
                 //Vitriolic ignores SR
                 if(nEssence == INVOKE_VITRIOLIC_BLAST || nEssence2 == INVOKE_VITRIOLIC_BLAST)
                 {
                     //Apply the VFX impact
                     if(iSR) ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);

                     //Apply secondary effect from essence invocations
                     int nDuration = nInvLevel / 5;

                     int i;
                     float fRound = RoundsToSeconds(1);
                     for(i = 1; i <= nDuration; i++)
                     {
                         DelayCommand(fRound * i, DoDelayedBlast(oTarget, DAMAGE_TYPE_ACID, VFX_IMP_ACID_S));
                     }
                 }
                 //Apply damage effect
                 ApplyBlastDamage(oPC, oTarget, 1, iSR, nDamage, nDamageType, nDamageType2, nHellFire, FALSE);
            }
        }

        //Get next target in spell area
        oTarget = MyNextObjectInShape(nShape, fRange, lTargetArea, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE, GetPosition(oPC));
        if(DEBUG) DoDebug("inv_eldtch_shape: Next target is: " + DebugObject2Str(oTarget));
    }

    if(nBlast == INVOKE_ELDRITCH_LINE)
        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(nBeamVFX, oPC, BODY_NODE_HAND, FALSE), oBeamTarget, 1.0f);
    else if(nBlast == INVOKE_ELDRITCH_DOOM)
        ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, EffectVisualEffect(nBeamVFX), lTargetArea, 1.0f);

    //Remove the created beam target object if it exists
    if (GetResRef(oBeamTarget) == "prc_invisobj") DestroyObject(oBeamTarget, 3.0);

    if(bSculptor)
    {
        if(!GetLocalInt(oPC, "UsingSecondBlast"))
        {
            SetLocalInt(oPC, "UsingSecondBlast", TRUE);
            UseInvocation(nBlast, CLASS_TYPE_WARLOCK, 0, TRUE);
        }
        else
            DeleteLocalInt(oPC, "UsingSecondBlast");
    }
}
