Updated AMS marker feats. Removed arcane & divine marker feats. Updated Dread Necromancer for epic progression. Updated weapon baseitem models. Updated new weapons for crafting & npc equip. Updated prefix. Updated release archive.
		
			
				
	
	
		
			293 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
/*:://////////////////////////////////////////////
 | 
						|
//:: Spell Name Force Missiles
 | 
						|
//:: Spell FileName XXX_S_ForceMissi
 | 
						|
//:://////////////////////////////////////////////
 | 
						|
//:: In Game Spell desctiption
 | 
						|
//:://////////////////////////////////////////////
 | 
						|
    Evocation [Force]
 | 
						|
    Also known as: Mordenkainen's Force Missiles
 | 
						|
    Level: Sor/Wiz 4
 | 
						|
    Components: V, S
 | 
						|
    Casting Time: 1 standard action
 | 
						|
    Range: Medium (20M)
 | 
						|
    Targets: Up to four enemy creatures in a 1.67M-radius sphere (5ft)
 | 
						|
    Duration: Instantaneous
 | 
						|
    Saving Throw: None or Reflex half (see text)
 | 
						|
    Spell Resistance: Yes
 | 
						|
    Source: Various (WotC Spellbook)
 | 
						|
 | 
						|
    You create a powerful missile of magical force, which darts from your
 | 
						|
    fingertips and unerringly strikes its target, dealing 2d6 points of damage.
 | 
						|
    The missile then bursts in a 1.67-meter blast of force that inflicts half
 | 
						|
    this amount of damage to any creatures in the area (other than the primary
 | 
						|
    target). The primary target is not entitled to a saving throw against the
 | 
						|
    burst, but creatures affected by the burst may attempt a Reflex save for
 | 
						|
    half damage.
 | 
						|
 | 
						|
    If the missiles' burst areas overlap, secondary targets make only one saving
 | 
						|
    throw attempt (and only one SR check, if applicable). A character can be
 | 
						|
    struck by one missile (or more) and also be caught in the burst of another
 | 
						|
    missile. In such a case, the character may attempt a Reflex save to halve
 | 
						|
    the burst damage, and SR might apply.
 | 
						|
 | 
						|
    The missile strikes unerringly, even if the target is in melee or has
 | 
						|
    anything less than total cover or concealment.
 | 
						|
 | 
						|
    For every five caster levels, the caster gains one missile. A caster has two
 | 
						|
    missiles at 9th level or lower, three missiles from 10th to 14th level, and
 | 
						|
    four missiles at 15th level or higher. If you can fire more then one
 | 
						|
    missile, and target one person, all missiles fly towards that target. If you
 | 
						|
    target the ground, the nearest 2 or more enemies will get hit in a 3M
 | 
						|
    radius sphere.
 | 
						|
//:://////////////////////////////////////////////
 | 
						|
//:: Spell Effects Applied / Notes
 | 
						|
//:://////////////////////////////////////////////
 | 
						|
    Using the magic missile effect, it'll do burst damage against those in
 | 
						|
    the area *at the time of casting*, thus it might look a little odd, oh well.
 | 
						|
 | 
						|
    The way the "Only one save and SR check" is done is that a local is set
 | 
						|
    and checked for each target each blast.
 | 
						|
//:://////////////////////////////////////////////
 | 
						|
//:: Created By: Jasperre
 | 
						|
//::////////////////////////////////////////////*/
 | 
						|
 | 
						|
#include "SMP_INC_SPELLS"
 | 
						|
 | 
						|
// Do the AOE blast using oOriginalTarget's location
 | 
						|
// * Damages oOriginalTarget fully.
 | 
						|
// * Uses the visuals eMissile and eVis.
 | 
						|
void DoMissileAndBlast(object oOriginalTarget, object oCaster, int nSpellSaveDC, string sSRLocal, string sSaveLocal, int nMissiles, int nMetaMagic, effect eMissile, effect eVis);
 | 
						|
 | 
						|
// Reflex adjust nDamage, using stored stuff.
 | 
						|
int ReturnReflexDamage(int nDamage, string sSaveLocal, object oTarget, int nDC, object oSaveVersus, float fDelay);
 | 
						|
// Check SR, using stored stuff.
 | 
						|
// * TRUE is resisted, FALSE isn't.
 | 
						|
int ReturnSRCheck(object oTarget, object oCaster, string sSRLocal, float fDelay = 0.0);
 | 
						|
 | 
						|
void main()
 | 
						|
{
 | 
						|
    // Spell Hook Check.
 | 
						|
    if(!SMP_SpellHookCheck(SMP_SPELL_FORCE_MISSILES)) return;
 | 
						|
 | 
						|
    //Declare major variables
 | 
						|
    object oCaster = OBJECT_SELF;
 | 
						|
    object oTarget = GetSpellTargetObject();
 | 
						|
    location lTarget = GetSpellTargetLocation();
 | 
						|
    int nMetaMagic = SMP_GetMetaMagicFeat();
 | 
						|
    int nCasterLevel = SMP_GetCasterLevel();
 | 
						|
    int nSpellSaveDC = SMP_GetSpellSaveDC();
 | 
						|
    int nPeopleDone;
 | 
						|
    string sSRLocal = IntToString(SMP_SPELL_FORCE_MISSILES) + ObjectToString(oCaster) + "SR";
 | 
						|
    string sSaveLocal = IntToString(SMP_SPELL_FORCE_MISSILES) + ObjectToString(oCaster) + "SAVE";
 | 
						|
 | 
						|
    // For every five caster levels, the caster gains one missile.
 | 
						|
    int nMissiles = SMP_LimitInteger(nCasterLevel/5, 4, 1);
 | 
						|
 | 
						|
    // Declare Effects
 | 
						|
    effect eMissile = EffectVisualEffect(VFX_IMP_MIRV);
 | 
						|
    effect eVis = EffectVisualEffect(VFX_IMP_MAGBLUE);
 | 
						|
 | 
						|
    // Is it one target?
 | 
						|
    if(GetIsObjectValid(oTarget) && GetObjectType(oTarget) == OBJECT_TYPE_CREATURE)
 | 
						|
    {
 | 
						|
        // Check PvP
 | 
						|
        if(!GetIsReactionTypeFriendly(oTarget))
 | 
						|
        {
 | 
						|
            // Use function
 | 
						|
            DoMissileAndBlast(oTarget, oCaster, nSpellSaveDC, sSRLocal, sSaveLocal, nMissiles, nMetaMagic, eMissile, eVis);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // We loop targets in the LOS of 2M sphere
 | 
						|
        oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 2.0, lTarget, TRUE, OBJECT_TYPE_CREATURE);
 | 
						|
        while(GetIsObjectValid(oTarget) && nPeopleDone < nMissiles)
 | 
						|
        {
 | 
						|
            // Check if an enemy and is seen
 | 
						|
            if(GetIsReactionTypeHostile(oTarget) && GetObjectSeen(oTarget))
 | 
						|
            {
 | 
						|
                // Use function - 1 missile blast
 | 
						|
                DoMissileAndBlast(oTarget, oCaster, nSpellSaveDC, sSRLocal, sSaveLocal, 1, nMetaMagic, eMissile, eVis);
 | 
						|
                // Add one to done list
 | 
						|
                nPeopleDone++;
 | 
						|
            }
 | 
						|
            oTarget = GetNextObjectInShape(SHAPE_SPHERE, 2.0, lTarget, TRUE, OBJECT_TYPE_CREATURE);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Do the AOE blast using oOriginalTarget's location
 | 
						|
// * Damages oOriginalTarget fully.
 | 
						|
// * Uses the visuals eMissile and eVis.
 | 
						|
void DoMissileAndBlast(object oOriginalTarget, object oCaster, int nSpellSaveDC, string sSRLocal, string sSaveLocal, int nMissiles, int nMetaMagic, effect eMissile, effect eVis)
 | 
						|
{
 | 
						|
    // Get delay for hitting.
 | 
						|
    float fDist = GetDistanceBetween(oCaster, oOriginalTarget);
 | 
						|
    float fDelay = fDist/(3.0 * log(fDist) + 2.0);
 | 
						|
    float fDelay2;
 | 
						|
    float fAOEDelay;
 | 
						|
    object oAOETarget;
 | 
						|
    int nCnt, nDam, nSR, nSave;
 | 
						|
    location lTarget;
 | 
						|
 | 
						|
    // Apply visual effect (Magic Missiles)
 | 
						|
    for(nCnt = 1; nCnt <= nMissiles; nCnt++)
 | 
						|
    {
 | 
						|
        fDelay2 += 0.1;
 | 
						|
        DelayCommand(fDelay2, SMP_ApplyVFX(oOriginalTarget, eMissile));
 | 
						|
 | 
						|
        // Signal Spell cast at, once
 | 
						|
        if(GetLocalInt(oOriginalTarget, sSRLocal) != FALSE)
 | 
						|
        {
 | 
						|
            SMP_SignalSpellCastAt(oOriginalTarget, SMP_SPELL_FORCE_MISSILES);
 | 
						|
        }
 | 
						|
 | 
						|
        // Do damage to oOriginalTarget, if they fail thier SR. No save!
 | 
						|
        // Check SR
 | 
						|
        if(!ReturnSRCheck(oOriginalTarget, oCaster, sSRLocal, fDelay))
 | 
						|
        {
 | 
						|
            // Do damage
 | 
						|
            nDam = SMP_MaximizeOrEmpower(6, 2, nMetaMagic);
 | 
						|
            DelayCommand(fDelay, SMP_ApplyDamageVFXToObject(oOriginalTarget, eVis, nDam));
 | 
						|
        }
 | 
						|
 | 
						|
        // For each blast, we loop targets in the AOE
 | 
						|
        // We loop targets in the LOS of 1.67M sphere
 | 
						|
        lTarget = GetLocation(oOriginalTarget);
 | 
						|
        oAOETarget = GetFirstObjectInShape(SHAPE_SPHERE, 1.67, lTarget, TRUE, OBJECT_TYPE_CREATURE);
 | 
						|
        while(GetIsObjectValid(oAOETarget))
 | 
						|
        {
 | 
						|
            // PvP Check and is NOT original target
 | 
						|
            if(!GetIsReactionTypeFriendly(oAOETarget) &&
 | 
						|
                oAOETarget != oOriginalTarget)
 | 
						|
            {
 | 
						|
                // Signal Spell cast at, once
 | 
						|
                if(GetLocalInt(oAOETarget, sSRLocal) != FALSE)
 | 
						|
                {
 | 
						|
                    // Signal Spell cast at
 | 
						|
                    SMP_SignalSpellCastAt(oAOETarget, SMP_SPELL_FORCE_MISSILES);
 | 
						|
                }
 | 
						|
                // Make sure we check SR as the original target
 | 
						|
                fAOEDelay = fDelay + GetDistanceBetweenLocations(lTarget, GetLocation(oAOETarget))/20;
 | 
						|
 | 
						|
                // Do damage to oAOETarget, if they fail thier SR. No save!
 | 
						|
                if(!ReturnSRCheck(oAOETarget, oCaster, sSRLocal, fAOEDelay))
 | 
						|
                {
 | 
						|
                    // Get damage to do
 | 
						|
                    nDam = SMP_MaximizeOrEmpower(6, 1, nMetaMagic);
 | 
						|
 | 
						|
                    // Adjust it due to saves, done already or not.
 | 
						|
                    nDam = ReturnReflexDamage(nDam, sSaveLocal, oAOETarget, nSpellSaveDC, oCaster, fAOEDelay);
 | 
						|
 | 
						|
                    // Do the damage, if any
 | 
						|
                    if(nDam > 0)
 | 
						|
                    {
 | 
						|
                        DelayCommand(fDelay, SMP_ApplyDamageVFXToObject(oOriginalTarget, eVis, nDam));
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            oAOETarget = GetNextObjectInShape(SHAPE_SPHERE, 1.67, lTarget, TRUE, OBJECT_TYPE_CREATURE);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
// Reflex adjust nDamage, using stored stuff.
 | 
						|
int ReturnReflexDamage(int nDamage, string sSaveLocal, object oTarget, int nDC, object oSaveVersus, float fDelay)
 | 
						|
{
 | 
						|
    // Check if saved already
 | 
						|
    int nSave = GetLocalInt(oTarget, sSaveLocal);
 | 
						|
 | 
						|
    // 0 = not attempted
 | 
						|
    // 1 = saved
 | 
						|
    // 2 = Not saved, failed in the attempt.
 | 
						|
 | 
						|
    if(nSave > 0)
 | 
						|
    {
 | 
						|
        if(nSave == 1)
 | 
						|
        {
 | 
						|
            // Saved
 | 
						|
            return SMP_ReflexAdjustDamage(TRUE, nDamage, oTarget);
 | 
						|
        }
 | 
						|
        else//if(nSave == 2)
 | 
						|
        {
 | 
						|
            // Failed
 | 
						|
            return SMP_ReflexAdjustDamage(FALSE, nDamage, oTarget);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // New save
 | 
						|
        nSave = SMP_SavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, SAVING_THROW_TYPE_NONE, oSaveVersus, fDelay);
 | 
						|
 | 
						|
        // Saved?
 | 
						|
        if(nSave == FALSE)
 | 
						|
        {
 | 
						|
            // Didn't save. Set and return.
 | 
						|
            SetLocalInt(oTarget, sSaveLocal, 2);
 | 
						|
            DelayCommand(0.1, DeleteLocalInt(oTarget, sSaveLocal));
 | 
						|
 | 
						|
            // Failed
 | 
						|
            return SMP_ReflexAdjustDamage(FALSE, nDamage, oTarget);
 | 
						|
        }
 | 
						|
        else// is over 1
 | 
						|
        {
 | 
						|
            // Passed
 | 
						|
            SetLocalInt(oTarget, sSaveLocal, 1);
 | 
						|
            DelayCommand(0.1, DeleteLocalInt(oTarget, sSaveLocal));
 | 
						|
 | 
						|
            // Passed
 | 
						|
            return SMP_ReflexAdjustDamage(TRUE, nDamage, oTarget);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return nDamage;
 | 
						|
}
 | 
						|
 | 
						|
// Check SR, using stored stuff.
 | 
						|
// * TRUE is resisted, FALSE isn't.
 | 
						|
int ReturnSRCheck(object oTarget, object oCaster, string sSRLocal, float fDelay = 0.0)
 | 
						|
{
 | 
						|
    // Get old result
 | 
						|
    int nResult = GetLocalInt(oTarget, sSRLocal);
 | 
						|
    if(nResult != TRUE)// Resisted already
 | 
						|
    {
 | 
						|
        // TRUE means we pass, and no damage, else we check if we need to do it.
 | 
						|
        // 1 Means we have passed, and do not need to do one.
 | 
						|
        // 2 means we failed the check, no more checks.
 | 
						|
        // 0 Means an absense of a check
 | 
						|
        if(nResult == 2)
 | 
						|
        {
 | 
						|
            // Failed already.
 | 
						|
            return FALSE;
 | 
						|
        }
 | 
						|
        else //if(nSR == 0)
 | 
						|
        {
 | 
						|
            // Make a new SR check - our first one!
 | 
						|
            nResult = SMP_SpellResistanceCheck(oCaster, oTarget, fDelay);
 | 
						|
 | 
						|
            if(nResult == FALSE)
 | 
						|
            {
 | 
						|
                // Failed
 | 
						|
                // Set we always failed - 2
 | 
						|
                SetLocalInt(oTarget, sSRLocal, 2);
 | 
						|
                DelayCommand(0.1, DeleteLocalInt(oTarget, sSRLocal));
 | 
						|
                return FALSE;
 | 
						|
            }
 | 
						|
            else //must have passed somehow
 | 
						|
            {
 | 
						|
                // Passed/resisted always. Set always pass - 1
 | 
						|
                SetLocalInt(oTarget, sSRLocal, 1);
 | 
						|
                DelayCommand(0.1, DeleteLocalInt(oTarget, sSRLocal));
 | 
						|
                // Return TRUE, passed
 | 
						|
                return TRUE;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        // Resisted, it was a 1
 | 
						|
        return TRUE;
 | 
						|
    }
 | 
						|
    // Return DIDN'T resist, as a defualt.
 | 
						|
    return FALSE;
 | 
						|
}
 |