From 31a0ccbf16ba146ac06cd1abb77e2075cf4c20b8 Mon Sep 17 00:00:00 2001
From: Jaysyn904 <68194417+Jaysyn904@users.noreply.github.com>
Date: Sat, 10 Aug 2024 08:57:45 -0400
Subject: [PATCH] Added missing Force Missiles spell script

Added missing Force Missiles spell script.  Fixed a few minor typos.
---
 nwn/nwnprc/trunk/2das/iprp_spellcost.2DA      |   7 +-
 nwn/nwnprc/trunk/include/prc_inc_descrptr.nss |   4 +-
 nwn/nwnprc/trunk/spells/sp_forcemissiles.nss  | 194 ++++++++++++++++++
 nwn/nwnprc/trunk/tlk/prc8_consortium.tlk.xml  |   2 +-
 4 files changed, 203 insertions(+), 4 deletions(-)
 create mode 100644 nwn/nwnprc/trunk/spells/sp_forcemissiles.nss

diff --git a/nwn/nwnprc/trunk/2das/iprp_spellcost.2DA b/nwn/nwnprc/trunk/2das/iprp_spellcost.2DA
index 0321900a..f708ab49 100644
--- a/nwn/nwnprc/trunk/2das/iprp_spellcost.2DA
+++ b/nwn/nwnprc/trunk/2das/iprp_spellcost.2DA
@@ -249,4 +249,9 @@
 245        Crystallize                    16823673 14202      6    
 246        BasiliskMask                   16836737 18708      8    
 247        GorgonMask                     16836803 18729      8    
-248        SPELL_FORCE_MISSILES           16790488 2480       4    
+248        Bigbys_Interposing_Hand        2683     459        5    
+249        Illithid_Mindblast             83865    789        3    
+250        Psionic_Mass_Concussion        84464    763        5    
+251        MONSTER_MindBlast              3891     551        3    
+252        MONSTER_CharmMonster           3893     552        3    
+253        SPELL_FORCE_MISSILES           16790488 2480       4    
diff --git a/nwn/nwnprc/trunk/include/prc_inc_descrptr.nss b/nwn/nwnprc/trunk/include/prc_inc_descrptr.nss
index c37dbe1b..bbee255f 100644
--- a/nwn/nwnprc/trunk/include/prc_inc_descrptr.nss
+++ b/nwn/nwnprc/trunk/include/prc_inc_descrptr.nss
@@ -148,7 +148,7 @@ int GetIsElementalSpell(int nSpellID, int nDescriptor = -1);
 int GetIsOfSubschool(int nSpellID, int nSubschoolFlag);
 
 /**
- * Returns an integer value containing the bitflags of descriptors set in prc_splls.2da
+ * Returns an integer value containing the bitflags of descriptors set in prc_spells.2da
  * for a given SpellID
  *
  * @param nSpellID  row number of tested spell in spells.2da
@@ -157,7 +157,7 @@ int GetIsOfSubschool(int nSpellID, int nSubschoolFlag);
 int GetDescriptorFlags(int nSpellID);
 
 /**
- * Returns an integer value containing the bitflags of subschools set in prc_splls.2da
+ * Returns an integer value containing the bitflags of subschools set in prc_spells.2da
  * for a given SpellID
  *
  * @param nSpellID  row number of tested spell in spells.2da
diff --git a/nwn/nwnprc/trunk/spells/sp_forcemissiles.nss b/nwn/nwnprc/trunk/spells/sp_forcemissiles.nss
new file mode 100644
index 00000000..ad4c0274
--- /dev/null
+++ b/nwn/nwnprc/trunk/spells/sp_forcemissiles.nss
@@ -0,0 +1,194 @@
+//::///////////////////////////////////////////////
+//:: Force Missiles
+//:: sp_forcemissiles
+//:: Copyright (c) 2022 PRC
+//:://////////////////////////////////////////////
+/*/
+Force Missiles
+(Spell Compendium, p. 98)
+
+Evocation [Force]
+Level: Sorcerer 4, Wizard 4,
+Components: V, S,
+Casting Time: 1 Standard Action
+Range: Medium (100 ft. + 10 ft./level)
+Target: Up to four creatures, no two of which are more than 30 ft. apart
+Duration: Instantaneous
+Saving Throw: None
+Spell Resistance: Yes
+
+Sparking bolts of blue magic, like giant magic missiles, streak from your
+outstretched hand to strike your foes and explode in sparkling bursts.
+
+You create powerful missiles of magical force, each of which darts from your
+fingertips and unerringly strikes its target, dealing 2d6 points of damage.
+The missile then explodes in a burst of force that deals half this amount of
+damage to any creatures adjacent to the primary target.
+
+The missile strikes unerringly, even if the target is in melee or has anything
+less than total cover or concealment. A caster cannot single out specific parts
+of a creature.
+
+You gain one missile for every four caster levels. You can make more than one
+missile strike a single target, if desired. However,you must designate targets
+before rolling for spell resistance or damage.
+
+/*/
+//:://////////////////////////////////////////////
+//:: Created By: Tsurani Nevericy
+//:: Created On: 05/15/2024
+//:://////////////////////////////////////////////
+//:: Last Updated By: Tsurani Nevericy
+//:: Last Updated On: 05/15/2024
+//:://////////////////////////////////////////////
+
+#include "prc_sp_func"
+#include "prc_inc_spells"
+#include "x2_inc_spellhook"
+
+void SendMissileBomb(object oCaster, object oTarget, float fDelay=0.0, float fTime=0.0)
+{
+    int nMetaMagic = PRCGetMetaMagicFeat();
+    ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_MIRV), oTarget);
+    location lLoc = GetLocation(oTarget);
+    object oLoop = GetFirstObjectInShape(SHAPE_SPHERE, 5.0, lLoc, TRUE);
+    while (GetIsObjectValid(oLoop))
+    {
+        SignalEvent(oLoop, EventSpellCastAt(oCaster, PRCGetSpellId()));
+        if (oLoop == oTarget)
+        {
+            int nDam = d6(2);
+            if (nMetaMagic == METAMAGIC_MAXIMIZE)
+                  nDam = 12;
+            if (nMetaMagic == METAMAGIC_EMPOWER)
+                  nDam += nDam/2;
+            DelayCommand(fTime, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, DAMAGE_TYPE_MAGICAL), oLoop));
+            DelayCommand(fTime, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_IMP_MAGBLUE, FALSE, 4.0f), oLoop));
+        }
+        else if (!PRCDoResistSpell(oCaster, oLoop, FloatToInt(fDelay)))
+        {
+            int nDam = d6(1);
+            if (nMetaMagic == METAMAGIC_MAXIMIZE)
+                  nDam = 6;
+            if (nMetaMagic == METAMAGIC_EMPOWER)
+                  nDam += nDam/2;
+            DelayCommand(fTime, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDam, DAMAGE_TYPE_MAGICAL), oLoop));
+            DelayCommand(fTime, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(VFX_IMP_MAGBLUE), oLoop));
+        }
+        oLoop = GetNextObjectInShape(SHAPE_SPHERE, 5.0, lLoc, TRUE);
+    }
+}
+
+//Implements the spell impact, put code here
+//  if called in many places, return TRUE if
+//  stored charges should be decreased
+//  eg. touch attack hits
+//
+//  Variables passed may be changed if necessary
+int DoSpell(object oCaster, object oTarget, int nCasterLevel, int nEvent)
+{
+    int nMetaMagic = PRCGetMetaMagicFeat();
+    int nSaveDC = PRCGetSaveDC(oTarget, oCaster);
+    int nPenetr = nCasterLevel + SPGetPenetr();
+	int i;
+    int nTargets;
+    int nCnt = 1;
+	float fDist, fDelay, fDelay2, fTime;
+
+	if (nCasterLevel > 40) nCasterLevel = 40;
+    int nMissiles = nCasterLevel/4;
+    if (nMissiles < 1) nMissiles = 1;	
+	
+	location lTarget = PRCGetSpellTargetLocation();
+
+    oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 9.144, lTarget, TRUE, OBJECT_TYPE_CREATURE);
+    while (GetIsObjectValid(oTarget))
+    {
+        if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oCaster) && oTarget != oCaster)
+        {
+            nTargets++;
+        }
+        oTarget = GetNextObjectInShape(SHAPE_SPHERE, 9.144, lTarget, TRUE, OBJECT_TYPE_CREATURE);
+    }
+	if (!nTargets)
+		return FALSE;
+
+    int nExtraMissiles = nMissiles / nTargets;
+
+    if (nExtraMissiles <= 0)
+        nExtraMissiles = 1;
+
+    int nRemainder = 0;
+
+    if (nTargets > nMissiles) nTargets = nMissiles;
+
+    nRemainder = nMissiles % nTargets;
+
+    oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 9.144, lTarget, TRUE, OBJECT_TYPE_CREATURE);
+    while (GetIsObjectValid(oTarget) && nCnt <= nTargets)
+    {
+        if (spellsIsTarget(oTarget, SPELL_TARGET_SELECTIVEHOSTILE, oCaster) && oTarget != oCaster)
+        {
+            if (!PRCDoResistSpell(oCaster, oTarget, FloatToInt(fDelay)))
+            {
+                int i;
+                for (i=1; i <= nExtraMissiles + nRemainder; i++)
+                {
+                    fDist = GetDistanceBetween(oCaster, oTarget);
+                    fDelay = fDist/(3.0 * log(fDist) + 2.0);
+                    fTime = fDelay;
+                    fDelay2 += 0.1;
+                    fTime += fDelay2;
+                    DelayCommand(fDelay2, SendMissileBomb(oCaster, oTarget, fDelay, fTime));
+                }
+            }
+            else
+            {
+                ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_MIRV), oTarget);
+            }
+            nCnt++;
+            nRemainder = 0;
+        }
+        oTarget = GetNextObjectInShape(SHAPE_SPHERE, 9.144, lTarget, TRUE, OBJECT_TYPE_CREATURE);
+    }
+	
+	return TRUE;
+}
+
+void main()
+{
+    object oCaster = OBJECT_SELF;
+	object oTarget = PRCGetSpellTargetObject();
+	
+    int nCasterLevel = PRCGetCasterLevel(oCaster);
+	int i;
+    int nTargets;
+    int nCnt = 1;
+	float fDist, fDelay, fDelay2, fTime;
+	
+    PRCSetSchool(GetSpellSchool(PRCGetSpellId()));
+	
+    if (!X2PreSpellCastCode()) return;
+	
+    int nEvent = GetLocalInt(oCaster, PRC_SPELL_EVENT); //use bitwise & to extract flags
+    if(!nEvent) //normal cast
+    {
+        if(GetLocalInt(oCaster, PRC_SPELL_HOLD) && oCaster == oTarget)
+        {   //holding the charge, casting spell on self
+            SetLocalSpellVariables(oCaster, 1);   //change 1 to number of charges
+            return;
+        }
+        DoSpell(oCaster, oTarget, nCasterLevel, nEvent);
+    }
+    else
+    {
+        if(nEvent & PRC_SPELL_EVENT_ATTACK)
+        {
+            if(DoSpell(oCaster, oTarget, nCasterLevel, nEvent))
+                DecrementSpellCharges(oCaster);
+        }
+    }
+    PRCSetSchool();
+}
+
+//::///////////////////////////////////////////////////////////////////
\ No newline at end of file
diff --git a/nwn/nwnprc/trunk/tlk/prc8_consortium.tlk.xml b/nwn/nwnprc/trunk/tlk/prc8_consortium.tlk.xml
index 22591d90..c62ca84c 100644
--- a/nwn/nwnprc/trunk/tlk/prc8_consortium.tlk.xml
+++ b/nwn/nwnprc/trunk/tlk/prc8_consortium.tlk.xml
@@ -72975,4 +72975,4 @@ Use: Activated</entry>
   <entry id="77190" lang="en" sex="m">Spellcasting isn't required to take the Talon of Tiamat prestige class.  If you currently have an arcane spellcasting, shadowcasting or invoking class, do not pick this marker feat.</entry>
   <entry id="77191" lang="en" sex="m">Spellcasting isn't required to take the Dragonsong Lyrist prestige class.  If you currently have an arcane spellcasting or shadowcasting class, do not pick this marker feat.</entry>
   <entry id="77192" lang="en" sex="m">Spellcasting isn't required to take the Ollam prestige class.  If you currently have an arcane spellcasting or shadowcasting class, do not pick this marker feat.</entry>
-</tlk>
+</tlk>
\ No newline at end of file