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;
 | |
| }
 |