Added Sanctified damage type. Added Holy damage type. Added Unholy damage type. Added Falling damage type. Added Ballistic damage type. Added Desiccation damage type. Fixed Major Missile's LABEL in spells.2da. Fixed Major Missile's LABEL in des_cft_spells.2da. Fixed Major Missile's LABEL in des_cft_scrolls.2da. Disabled incomplete PnP version of Shades spell. Astaroth's free crafting feats should drop off onRest, when expelling a vestige & when rebinding a vestige. Re-enabled caster level override variable clearing in GetInvokerLevel(). Change Piercing Cold's damagetype to Untyped. Constanted new damagetypes correctly for scripting (@lightbeard) Changed all force spells damagetype to force. Fixed issue with Initiators using Shadows Blade, Ironheart Aura and Shadow Trickster with dual-stances. Changed Eldritch Blast to Untyped damage. Fixed Path of Shadow screwing up caster levels. Changed Sanctify Martial strike to be Holy damage. Changed Vile Martial strike to be Vile damage. Updated Forsaker to work with Warforged "Armor". Added a SignalEvent() to Invisible Needle so it would break Invisibility when it should. Fixed equip exploit with Shou Disciple and monk weapons. Changed Saint template's Holy Touch to do Holy Damage. Changed Horrid Wilting to do Desiccation damage. Fixed Break Enchantment's targeting. Fixed the Command spell to obey mind immunity. Changed Damning Darkness to do Unholy damage. Change the Necrotic spells to do Vile damage. Made Persistent Blade more like PnP and made it dispellable.
105 lines
4.0 KiB
Plaintext
105 lines
4.0 KiB
Plaintext
#include "x2_inc_spellhook"
|
|
#include "prc_inc_sp_tch"
|
|
|
|
// Function to calculate the ranged attack bonus manually
|
|
int CalculateRangedAB(object oCaster)
|
|
{
|
|
int nBAB = GetBaseAttackBonus(oCaster); // Get the base attack bonus of the caster
|
|
int nDexMod = GetAbilityModifier(ABILITY_DEXTERITY, oCaster); // Get Dexterity modifier
|
|
|
|
int nRangedAttackBonus = nBAB + nDexMod; // Ranged attack bonus is BAB + Dexterity modifier
|
|
|
|
return nRangedAttackBonus;
|
|
}
|
|
|
|
// Function to handle the ranged attack
|
|
void ForceNeedleAttack(object oCaster, object oTarget, int nBonus)
|
|
{
|
|
// Constants
|
|
float fRange = FeetToMeters(5.0 * IntToFloat(nBonus)); // Range is 5 feet per spell level
|
|
|
|
effect eMissile = EffectVisualEffect(VFX_IMP_MIRV);
|
|
effect eVis = EffectVisualEffect(VFX_IMP_MAGBLUE);
|
|
|
|
// Get the distance to the target and check if it's within range
|
|
float fDistance = GetDistanceBetween(oCaster, oTarget);
|
|
if (fDistance > fRange)
|
|
{
|
|
SendMessageToPC(oCaster, "Target is out of range.");
|
|
return;
|
|
}
|
|
|
|
// Calculate ranged attack bonus
|
|
int nRangedAttackBonus = CalculateRangedAB(oCaster);
|
|
|
|
// Check if the caster has Point Blank Shot feat and is within 30 feet of the target
|
|
if (GetHasFeat(FEAT_POINT_BLANK_SHOT, oCaster) && fDistance <= FeetToMeters(30.0)) // Within 30 feet for PBS bonus
|
|
{
|
|
nRangedAttackBonus += 1;
|
|
}
|
|
|
|
// Perform a standard ranged attack roll (not a ranged touch attack)
|
|
int nDieRoll = d20(1);
|
|
int nAttackRoll = nDieRoll + nRangedAttackBonus;
|
|
int nAC = GetAC(oTarget); // Get the target's AC for a normal ranged attack
|
|
|
|
// Construct the attack roll message in the format of NWN combat log
|
|
string sMessage;
|
|
string sCasterName = GetName(oCaster);
|
|
string sTargetName = GetName(oTarget);
|
|
|
|
if (nAttackRoll >= nAC)
|
|
{
|
|
// If the attack hits, deal damage based on the highest force spell level
|
|
int nDamage = d4(nBonus); // Deal 1d4 damage per force spell level
|
|
nDamage += SpellSneakAttackDamage(oCaster, oTarget);
|
|
|
|
// Handle critical hit on a natural 20
|
|
if (nDieRoll == 20)
|
|
{
|
|
nDamage *= 2;
|
|
sMessage = PRC_TEXT_LIGHT_BLUE + sCasterName + PRC_TEXT_ORANGE +" attacks " + sTargetName + ": *critical hit* " + "(" + IntToString(nDieRoll) + " + " + IntToString(nRangedAttackBonus) + " = " + IntToString(nAttackRoll) + ")";
|
|
}
|
|
else
|
|
{
|
|
sMessage = PRC_TEXT_LIGHT_BLUE + sCasterName + PRC_TEXT_ORANGE +" attacks " + sTargetName + ": *hit* " + "(" + IntToString(nDieRoll) + " + " + IntToString(nRangedAttackBonus) + " = " + IntToString(nAttackRoll) + ")";
|
|
}
|
|
|
|
// Apply the MIRV and damage effect
|
|
effect eDam = PRCEffectDamage(oTarget, nDamage, DAMAGE_TYPE_MAGICAL);
|
|
|
|
// Delay visual effects to simulate missile travel
|
|
float fDelay = fDistance / (3.0 * log(fDistance) + 2.0);
|
|
DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget));
|
|
DelayCommand(fDelay, SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget, 0.0f, FALSE));
|
|
DelayCommand(fDelay + 0.1, SPApplyEffectToObject(DURATION_TYPE_INSTANT, eMissile, oTarget));
|
|
|
|
DelayCommand(fDelay + 0.2, SendMessageToPC(oCaster, sMessage));
|
|
}
|
|
else
|
|
{
|
|
// If the attack misses
|
|
sMessage = PRC_TEXT_LIGHT_BLUE + sCasterName + PRC_TEXT_ORANGE + " attacks " + sTargetName + ": *miss* " + "(" + IntToString(nDieRoll) + " + " + IntToString(nRangedAttackBonus) + " = " + IntToString(nAttackRoll) + ")";
|
|
SendMessageToPC(oCaster, sMessage);
|
|
}
|
|
}
|
|
|
|
void main()
|
|
{
|
|
// Declare major variables
|
|
object oPC = OBJECT_SELF;
|
|
object oTarget = PRCGetSpellTargetObject();
|
|
|
|
int nBonus = GetLocalInt(oPC, "InvisibleNeedleBonus");
|
|
|
|
if (!GetLocalInt(oPC, "InvisibleNeedleBonus"))
|
|
{
|
|
FloatingTextStringOnCreature("You do not have a spell available of adequate level or type", oPC, FALSE);
|
|
return;
|
|
}
|
|
|
|
SignalEvent(oPC, EventSpellCastAt(oTarget, PRCGetSpellId(), FALSE));
|
|
|
|
ForceNeedleAttack(oPC, oTarget, nBonus);
|
|
}
|