/*
   ----------------
   Energy Retort

   psi_pow_enrtrt
   ----------------

   11/12/04 by Stratovarius
*/ /** @file

    Energy Retort

    Psychokinesis [see text]
    Level: Psion/wilder 3
    Manifesting Time: 1 standard action
    Range: Personal and close (25 ft. + 5 ft./2 levels); see text
    Targets: You and creature or object attacking you; see text
    Duration: 1 min./level
    Saving Throw: Reflex half or Fortitude half; see text
    Power Resistance: Yes
    Power Points: 5
    Metapsionics: Chain, Empower, Extend, Maximize

    Upon manifesting this power, you choose cold, electricity, fire, or sonic.
    You weave a field of potential energy of the chosen type around your body.
    The first successful attack made against you in each round during the
    power’s duration prompts a response from the field without any effort on
    your part. The attack may be physical, the effect of a power, or the effect
    of a spell (including spell-like, supernatural, and extraordinary
    abilities). An “ectoburst” discharges from the field, targeting the source
    of the attack and dealing 4d6 points of damage of the chosen energy type.
    To be affected, a target must be within close range. The ectoburst is a
    ranged touch attack.

    Cold: A field of this energy type deals +1 point of damage per die. The
          saving throw to reduce damage from a cold retort is a Fortitude save
          instead of a Reflex save.
    Electricity: Manifesting a field of this energy type provides a +2 bonus to
                 the save DC and a +2 bonus on manifester level checks for the
                 purpose of overcoming power resistance.
    Fire: A field of this energy type deals +1 point of damage per die.
    Sonic: A field of this energy type deals -1 point of damage per die and
           ignores an object’s hardness.

    This power’s subtype is the same as the type of energy you manifest.

    Augment: For every additional power point you spend, this power’s duration
             increases by 1 minute.
*/

#include "psi_inc_psifunc"
#include "psi_inc_pwresist"
#include "psi_spellhook"
#include "prc_inc_sp_tch"
#include "psi_inc_enrgypow"

const string ENERGY_RETORT_VARNAME_BASE = "PRC_Power_EnergyRetort_";

void DispelMonitor(object oManifester, object oTarget, int nSpellID, int nBeatsRemaining);

void main()
{
    // Are we running the manifestation part or the onhit part?
    if(GetRunningEvent() != EVENT_ONHIT)
    {
        // Power use hook
        if (!PsiPrePowerCastCode())  return;

        object oManifester = OBJECT_SELF;
        object oTarget = PRCGetSpellTargetObject();
        struct manifestation manif =
                EvaluateManifestation(oManifester, oTarget,
                                      PowerAugmentationProfile(PRC_NO_GENERIC_AUGMENTS,
                                                               1, PRC_UNLIMITED_AUGMENTATION
                                                               ),
                                      METAPSIONIC_CHAIN | METAPSIONIC_EMPOWER | METAPSIONIC_EXTEND | METAPSIONIC_MAXIMIZE
                                      );

        if(manif.bCanManifest)
        {
            int nDC           = GetManifesterDC(oManifester);
            int nPen          = GetPsiPenetration(oManifester);
            effect eDur       = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD);
            float fDuration   = 60.0f * (manif.nManifesterLevel + manif.nTimesAugOptUsed_1);
            if(manif.bExtend) fDuration *= 2;

            // Get the OnHitCast: Unique on the target's armor / hide
            ExecuteScript("prc_keep_onhit_a", oTarget);

            // Hook eventscript
            AddEventScript(oTarget, EVENT_ONHIT, "psi_pow_enrtrt", TRUE, FALSE);

            // Store data for use when hit
            SetLocalManifestation(oTarget, ENERGY_RETORT_VARNAME_BASE + "Manifestation", manif);
            SetLocalInt(oTarget, ENERGY_RETORT_VARNAME_BASE + "DC", nDC);
            SetLocalInt(oTarget, ENERGY_RETORT_VARNAME_BASE + "SRPenetration", nPen);

            // Do VFX for the monitor to look for
            SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDur, oTarget, fDuration, TRUE, manif.nSpellID, manif.nManifesterLevel);

            // Start effect end monitor
            DelayCommand(6.0f, DispelMonitor(oManifester, oTarget, manif.nSpellID, FloatToInt(fDuration) / 6));
        }// end if - Successfull manifestation
    }// end if - Manifesting a power
    else
    {
        object oHit  = OBJECT_SELF;
        object oItem = GetSpellCastItem();

        // Make sure the one doing the triggering hit was someone else
        if(GetBaseItemType(oItem) == BASE_ITEM_ARMOR ||
           GetBaseItemType(oItem) == BASE_ITEM_CREATUREITEM
           )
        {
            // Once per round lock
            if(GetLocalInt(oHit, ENERGY_RETORT_VARNAME_BASE + "UsedForRound"))
                return;
            else
            {
                SetLocalInt(oHit, ENERGY_RETORT_VARNAME_BASE + "UsedForRound", TRUE);
                DelayCommand(6.0f, DeleteLocalInt(oHit, ENERGY_RETORT_VARNAME_BASE + "UsedForRound"));
            }

            // Get data from the original manifestation
            struct manifestation manif      = GetLocalManifestation(oHit, ENERGY_RETORT_VARNAME_BASE + "Manifestation");
            object oMainTarget              = PRCGetSpellTargetObject();
            struct energy_adjustments enAdj =
                EvaluateEnergy(manif.nSpellID, POWER_ENERGYRETORT_COLD, POWER_ENERGYRETORT_ELEC, POWER_ENERGYRETORT_FIRE, POWER_ENERGYRETORT_SONIC,
                               VFX_BEAM_COLD, VFX_BEAM_LIGHTNING, VFX_BEAM_FIRE, VFX_BEAM_MIND);
            int nDC                         = GetLocalInt(oHit, ENERGY_RETORT_VARNAME_BASE + "DC") + enAdj.nDCMod;
            int nPen                        = GetLocalInt(oHit, ENERGY_RETORT_VARNAME_BASE + "SRPenetration") + enAdj.nPenMod;
            int nNumberOfDice               = 4;
            int nDieSize                    = 6;
            int nTouchAttack,
                nOriginalDamage,
                nDamage,
                i;
            effect eVis                     = EffectVisualEffect(enAdj.nVFX1);
            effect eRay,
                   eDamage;
            float fRange                    = FeetToMeters(25.0f + (5.0f * (manif.nManifesterLevel / 2)));
            object oChainTarget;

            // Test range
            if(GetDistanceBetween(oHit, oMainTarget) <= fRange)
            {
                // Determine Chain Power targets
                if(manif.bChain)
                    EvaluateChainPower(manif, oMainTarget, TRUE);

                // Let the AI know
                PRCSignalSpellEvent(oMainTarget, TRUE, manif.nSpellID, manif.oManifester);
                if(manif.bChain)
                    for(i = 0; i < array_get_size(manif.oManifester, PRC_CHAIN_POWER_ARRAY); i++)
                        PRCSignalSpellEvent(array_get_object(manif.oManifester, PRC_CHAIN_POWER_ARRAY, i), TRUE, manif.nSpellID, manif.oManifester);

                // Touch attack the main target
                nTouchAttack = PRCDoRangedTouchAttack(oMainTarget);

                // Shoot the ray
                eRay = EffectBeam(enAdj.nVFX2, manif.oManifester, BODY_NODE_HAND, !(nTouchAttack > 0));
                SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eRay, oMainTarget, 1.7, FALSE);

                if(nTouchAttack > 0)
                {
                    //Check for Power Resistance
                    if(PRCMyResistPower(manif.oManifester, oMainTarget, nPen))
                    {
                        // Roll damage
                        nDamage = MetaPsionicsDamage(manif, nDieSize, nNumberOfDice, 0, enAdj.nBonusPerDie, TRUE, TRUE);
                        // Target-specific stuff
                        nDamage = GetTargetSpecificChangesToDamage(oMainTarget, manif.oManifester, nDamage, TRUE, TRUE);

                        // Do save
                        if(enAdj.nSaveType == SAVING_THROW_TYPE_COLD)
                        {
                            // Cold has a fort save for half
                            if(PRCMySavingThrow(SAVING_THROW_FORT, oMainTarget, nDC, enAdj.nSaveType))
                            {
				if (GetHasMettle(oMainTarget, SAVING_THROW_FORT))
				// This script does nothing if it has Mettle, bail
					nDamage = 0;                              
                                nDamage /= 2;
                            }
                        }
                        else
                            // Adjust damage according to Reflex Save, Evasion or Improved Evasion
                            nDamage = PRCGetReflexAdjustedDamage(nDamage, oMainTarget, nDC, enAdj.nSaveType);

                        // Apply VFX and damage the main target, assuming there is still damage left to deal after modification
                        if(nDamage > 0)
                        {
                            // Apply the damage. Critical hits & precision damage apply
                            ApplyTouchAttackDamage(manif.oManifester, oMainTarget, nTouchAttack, nDamage, enAdj.nDamageType);

                            // Apply damage to Chain targets
                            if(manif.bChain)
                            {
                                // Halve the damage
                                nOriginalDamage = nDamage / 2;

                                for(i = 0; i < array_get_size(manif.oManifester, PRC_CHAIN_POWER_ARRAY); i++)
                                {
                                    // Get target to affect
                                    oChainTarget = array_get_object(manif.oManifester, PRC_CHAIN_POWER_ARRAY, i);
                                    // Determine damage
                                    nDamage = nOriginalDamage;
                                    // Target-specific stuff
                                    nDamage = GetTargetSpecificChangesToDamage(oChainTarget, manif.oManifester, nDamage, TRUE, TRUE);

                                    // Do save
                                    if(enAdj.nSaveType == SAVING_THROW_TYPE_COLD)
                                    {
                                        // Cold has a fort save for half
                                        if(PRCMySavingThrow(SAVING_THROW_FORT, oChainTarget, nDC, enAdj.nSaveType))
                                            nDamage /= 2;
                                    }
                                    else
                                        // Adjust damage according to Reflex Save, Evasion or Improved Evasion
                                        nDamage = PRCGetReflexAdjustedDamage(nDamage, oChainTarget, nDC, enAdj.nSaveType);

                                    // Apply VFX and damage to chained target, assuming there is still damage left to deal after modification
                                    if(nDamage > 0)
                                    {
                                        // Apply VFX and damage to chained target
                                        eDamage = EffectDamage(nDamage, enAdj.nDamageType);
                                        SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oChainTarget);
                                        SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oChainTarget);
                                        SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(enAdj.nVFX2, oMainTarget, BODY_NODE_CHEST), oChainTarget, 1.7, FALSE);
                                    }// end if - There was still damage remaining to be dealt after adjustments
                                }// end for - Chain targets
                            }// end if - Chain Power
                        }// end if - There is damage left to be dealt
                    }// end if - SR check
                }// end if - Touch attack hit
            }// end if - Range check
        }// end if - Manifester was the one hit in the triggering attack
    }// end else - Running OnHit event
}

void DispelMonitor(object oManifester, object oTarget, int nSpellID, int nBeatsRemaining)
{
    // Has the power ended since the last beat, or does the duration run out now
    if((--nBeatsRemaining == 0) ||
       PRCGetDelayedSpellEffectsExpired(nSpellID, oTarget, oManifester)
       )
    {
        if(DEBUG) DoDebug("psi_pow_enrtrt: Expired, cleaning up");
        // Clear the effect presence marker
        DeleteLocalManifestation(oTarget, ENERGY_RETORT_VARNAME_BASE + "Manifestation");
        DeleteLocalInt(oTarget, ENERGY_RETORT_VARNAME_BASE + "DC");
        DeleteLocalInt(oTarget, ENERGY_RETORT_VARNAME_BASE + "SRPenetration");
        // Remove the eventscript
        RemoveEventScript(oTarget, EVENT_ONHIT, "psi_pow_enrtrt", TRUE, FALSE);
    }
    else
       DelayCommand(6.0f, DispelMonitor(oManifester, oTarget, nSpellID, nBeatsRemaining));
}