Forsaker / Barbarian Rage tweak. Removed duplicate class feats from Abjurant Champion, Eldritch Knight & Rage Mage. Fixed Maul icon typo in feat.2da. Fixed extra space typo in prc_material.2da. Fixed typos in TLK. Updated release archive.
3072 lines
122 KiB
Plaintext
3072 lines
122 KiB
Plaintext
/*
|
|
----------------
|
|
prc_inc_spells
|
|
----------------
|
|
|
|
7/25/04 by WodahsEht
|
|
|
|
Contains many useful functions for determining caster level, mostly. The goal
|
|
is to consolidate all caster level functions to this -- existing caster level
|
|
functions will be wrapped around the main function.
|
|
|
|
In the future, all new PrC's that add to caster levels should be added to
|
|
the GetArcanePRCLevels and GetDivinePRCLevels functions. Very little else should
|
|
be necessary, except when new casting feats are created.
|
|
*/
|
|
|
|
//:: Updated for .35 by Jaysyn 2023/03/10
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function prototypes */
|
|
//////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Adjusts the base class level (NOT caster level) of the class by any spellcasting PrCs
|
|
* @param nClass a base casting class (divine or arcane)
|
|
* @return The level of the class, adjusted for any appropriate PrC levels
|
|
*/
|
|
int GetPrCAdjustedClassLevel(int nClass, object oCaster = OBJECT_SELF);
|
|
|
|
/**
|
|
* Adjusts the base caster level of the class by any spellcasting PrCs plus Practised Spellcasting feats if appropriate
|
|
* @param nClass a base casting class
|
|
* @param bAdjustForPractisedSpellcaster add practiced spellcaster feat to caster level. TRUE by default
|
|
* @return the caster level in the class, adjusted by any PrC levels and practised spellcaster feats as appropriate
|
|
*/
|
|
int GetPrCAdjustedCasterLevel(int nClassType, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE);
|
|
|
|
/**
|
|
* finds the highest arcane or divine caster level, adjusting the base caster level of the class by any
|
|
* spellcasting PrCs plus Practised Spellcasting feats if appropriate
|
|
* @param nClassType TYPE_DIVINE or TYPE_ARCANE
|
|
* @param bAdjustForPractisedSpellcaster add practiced spellcaster feat to caster level. TRUE by default
|
|
* @return the highest arcane/divine caster level adjusted by any PrC levels and practised spellcaster feats as appropriate
|
|
*/
|
|
int GetPrCAdjustedCasterLevelByType(int nClassType, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE);
|
|
|
|
// Returns the best "feat-adjusted" arcane levels of the PC in question.
|
|
// Considers feats that situationally adjust caster level.
|
|
int GetLevelByTypeArcaneFeats(object oCaster = OBJECT_SELF, int iSpellID = -1);
|
|
|
|
// Returns the best "feat-adjusted" divine levels of the PC in question.
|
|
// Considers feats that situationally adjust caster level.
|
|
int GetLevelByTypeDivineFeats(object oCaster = OBJECT_SELF, int iSpellID = -1);
|
|
|
|
//Returns Reflex Adjusted Damage. Is a wrapper function that allows the
|
|
//DC to be adjusted based on conditions that cannot be done using iprops
|
|
//such as saves vs spellschools, or other adjustments
|
|
int PRCGetReflexAdjustedDamage(int nDamage, object oTarget, int nDC, int nSaveType=SAVING_THROW_TYPE_NONE, object oSaveVersus=OBJECT_SELF);
|
|
|
|
//Is a wrapper function that allows the DC to be adjusted based on conditions
|
|
//that cannot be done using iprops, such as saves vs spellschool.
|
|
//If bImmunityCheck == FALSE: returns 0 if failure or immune, 1 if success - same as MySavingThrow
|
|
//If bImmunityCheck == TRUE: returns 0 if failure, 1 if success, 2 if immune
|
|
int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType=SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0, int bImmunityCheck = FALSE);
|
|
|
|
// Finds caster levels by specific types (see the constants below).
|
|
int GetCasterLvl(int iTypeSpell, object oCaster = OBJECT_SELF);
|
|
|
|
// Calculates bonus damage to a spell for Spell Betrayal Ability
|
|
int SpellBetrayalDamage(object oTarget, object oCaster);
|
|
|
|
// Calculates damage to a spell for Spellstrike Ability
|
|
int SpellStrikeDamage(object oTarget, object oCaster);
|
|
|
|
// Create a Damage effect
|
|
// - oTarget: spell target
|
|
// - nDamageAmount: amount of damage to be dealt. This should be applied as an
|
|
// instantaneous effect.
|
|
// - nDamageType: DAMAGE_TYPE_*
|
|
// - nDamagePower: DAMAGE_POWER_*
|
|
// Used to add Warmage's Edge to spells.
|
|
effect PRCEffectDamage(object oTarget, int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL, int nMetaMagic=METAMAGIC_NONE);
|
|
|
|
/**
|
|
* Adds damage to all arcane spells based on the number of dice
|
|
*/
|
|
int SpellDamagePerDice(object oCaster, int nDice);
|
|
|
|
int IsSpellDamageElemental(int nDamageType);
|
|
|
|
// Get altered damage type for energy sub feats.
|
|
// nDamageType - The DAMAGE_TYPE_xxx constant of the damage. All types other
|
|
// than elemental damage types are ignored.
|
|
// oCaster - caster object.
|
|
// moved from spinc_common, possibly somewhat redundant
|
|
int PRCGetElementalDamageType(int nDamageType, object oCaster = OBJECT_SELF);
|
|
|
|
// Adds the bonus damage from both Spell Betrayal and Spellstrike together
|
|
int ApplySpellBetrayalStrikeDamage(object oTarget, object oCaster, int bShowTextString = TRUE);
|
|
|
|
// wrapper for DecrementRemainingSpellUses, works for newspellbook 'fake' spells too
|
|
void PRCDecrementRemainingSpellUses(object oCreature, int nSpell);
|
|
|
|
/**
|
|
* Determines and applies the alignment shift for using spells/powers with the
|
|
* [Evil] descriptor. The amount of adjustment is equal to the square root of
|
|
* the caster's distance from pure evil.
|
|
* In other words, the amount of shift is higher the farther the caster is from
|
|
* pure evil, with the extremes being 10 points of shift at pure good and 0
|
|
* points of shift at pure evil.
|
|
*
|
|
* Does nothing if the PRC_SPELL_ALIGNMENT_SHIFT switch is not set.
|
|
*
|
|
* @param oPC The caster whose alignment to adjust
|
|
*/
|
|
//void SPEvilShift(object oPC);
|
|
|
|
/**
|
|
* Determines and applies the alignment shift for using spells/powers with the
|
|
* [Good] descriptor. The amount of adjustment is equal to the square root of
|
|
* the caster's distance from pure good.
|
|
* In other words, the amount of shift is higher the farther the caster is from
|
|
* pure good, with the extremes being 10 points of shift at pure evil and 0
|
|
* points of shift at pure good.
|
|
*
|
|
* Does nothing if the PRC_SPELL_ALIGNMENT_SHIFT switch is not set.
|
|
*
|
|
* @param oPC The caster whose alignment to adjust
|
|
*/
|
|
//void SPGoodShift(object oPC);
|
|
|
|
/**
|
|
* Applies the corruption cost for Corrupt spells.
|
|
*
|
|
* @param oPC The caster of the Corrupt spell
|
|
* @param oTarget The target of the spell.
|
|
* Not used for anything, should probably remove - Ornedan
|
|
* @param nAbility ABILITY_* of the ability to apply the cost to
|
|
* @param nCost The amount of stat damage or drain to apply
|
|
* @param bDrain If this is TRUE, the cost is applied as ability drain.
|
|
* If FALSE, as ability damage.
|
|
*/
|
|
void DoCorruptionCost(object oPC, int nAbility, int nCost, int bDrain);
|
|
|
|
// This function is used in the spellscripts
|
|
// It functions as Evasion for Fortitude and Will partial saves
|
|
// This means the "partial" section is ignored
|
|
// nSavingThrow takes either SAVING_THROW_WILL or SAVING_THROW_FORT
|
|
int GetHasMettle(object oTarget, int nSavingThrow = SAVING_THROW_WILL);
|
|
|
|
// Applies all of the effects needed to set a creature incorporeal
|
|
// You need to set the creature incorporeal in the feat/spell/effect itself if using nPermanent = TRUE
|
|
// nSuperOrEx: 0 is normal, 1 is Supernatural, 2 is Extraordinary
|
|
void SetIncorporeal(object oTarget, float fDuration, int nSuperOrEx, int nPermanent = FALSE);
|
|
|
|
// Test for incorporeallity of the target
|
|
// useful for targetting loops when incorporeal creatures
|
|
// wouldnt be affected
|
|
int GetIsIncorporeal(object oTarget);
|
|
|
|
/** Tests if a creature is living. Should be called on creatures.
|
|
* Dead and not-alive creatures return FALSE
|
|
* Returns FALSE for non-creature objects.
|
|
*/
|
|
int PRCGetIsAliveCreature(object oTarget);
|
|
|
|
// Gets the total number of HD of controlled undead
|
|
// i.e from Animate Dead, Ghoul Gauntlet or similar
|
|
// Dominated undead from Turn Undead do not count
|
|
int GetControlledUndeadTotalHD(object oPC = OBJECT_SELF);
|
|
|
|
// Gets the total number of HD of controlled evil outsiders
|
|
// i.e from call dretch, call lemure, or similar
|
|
// Dominated outsiders from Turn Undead etc do not count
|
|
int GetControlledFiendTotalHD(object oPC = OBJECT_SELF);
|
|
|
|
// Gets the total number of HD of controlled good outsiders
|
|
// i.e from call favoured servants
|
|
// Dominated outsiders from Turn Undead etc do not count
|
|
int GetControlledCelestialTotalHD(object oPC = OBJECT_SELF);
|
|
|
|
/**
|
|
* Multisummon code, to be run before the summoning effect is applied.
|
|
* Normally, this will only perform the multisummon trick of setting
|
|
* pre-existing summons indestructable if PRC_MULTISUMMON is set.
|
|
*
|
|
* @param oPC The creature casting the summoning spell
|
|
* @param bOverride If this is set, ignores the value of PRC_MULTISUMMON switch
|
|
*/
|
|
void MultisummonPreSummon(object oPC = OBJECT_SELF, int bOverride = FALSE);
|
|
|
|
/**
|
|
* Sets up all of the AoE's variables, but only if they aren't already set.
|
|
*
|
|
* This sets many things that would have been checked against GetAreaOfEffectCreator()
|
|
* as local ints making it so the AoE can now function entirely independantly of its caster.
|
|
* - The only problem is that this will never be called until the AoE does a heartbeat or
|
|
* something.
|
|
*
|
|
* Since some functions (ie PRCGetLastSpellcastClass() can not work outside of the spell script
|
|
* sometimes SetAllAoEInts() was storing incorrect vaules for 'new spellbook' classes.
|
|
* I modified the function and moved it to main spell script, so it should work correctly now. - x
|
|
*
|
|
* @param SpellID Spell ID to store on the AoE.
|
|
* @param oAoE AoE object to store the variables on
|
|
* @param nBaseSaveDC save DC to store on the AoE
|
|
* @param SpecDispel Stored on the AoE (dunno what it's for)
|
|
* @param nCasterLevel Caster level to store on the AoE. If default used, gets
|
|
* caster level from the AoE creator.
|
|
*/
|
|
void SetAllAoEInts(int SpellID, object oAoE, int nBaseSaveDC, int SpecDispel = 0 , int nCasterLevel = 0);
|
|
|
|
// * Applies the effects of FEAT_AUGMENT_SUMMON to summoned creatures.
|
|
void AugmentSummonedCreature(string sResRef);
|
|
|
|
// -----------------
|
|
// BEGIN SPELLSWORD
|
|
// -----------------
|
|
|
|
//This function returns 1 only if the object oTarget is the object
|
|
//the weapon hit when it channeled the spell sSpell or if there is no
|
|
//channeling at all
|
|
int ChannelChecker(string sSpell, object oTarget);
|
|
|
|
//If a spell is being channeled, we store its target and its name
|
|
void StoreSpellVariables(string sString,int nDuration);
|
|
|
|
//Replacement for The MaximizeOrEmpower function
|
|
int PRCMaximizeOrEmpower(int nDice, int nNumberOfDice, int nMeta, int nBonus = 0);
|
|
|
|
//This checks if the spell is channeled and if there are multiple spells
|
|
//channeled, which one is it. Then it checks in either case if the spell
|
|
//has the metamagic feat the function gets and returns TRUE or FALSE accordingly
|
|
//int CheckMetaMagic(int nMeta,int nMMagic);
|
|
//not needed now there is PRCGetMetaMagicFeat()
|
|
|
|
//wrapper for biowares GetMetaMagicFeat()
|
|
//used for spellsword and items
|
|
// bClearFeatFlags - clear metamagic feat info (sudden meta, divine meta etc.)- set it to FALSE if you're
|
|
// going to use PRCGetMetaMagicFeat() more than once for a single spell
|
|
// (ie. first in spellhook code, next in spell script)
|
|
int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE);
|
|
|
|
// This function rolls damage and applies metamagic feats to the damage.
|
|
// nDamageType - The DAMAGE_TYPE_xxx constant for the damage, or -1 for no
|
|
// a non-damaging effect.
|
|
// nDice - number of dice to roll.
|
|
// nDieSize - size of dice, i.e. d4, d6, d8, etc.
|
|
// nBonusPerDie - Amount of bonus damage per die.
|
|
// nBonus - Amount of overall bonus damage.
|
|
// nMetaMagic - metamagic constant, if -1 GetMetaMagic() is called.
|
|
// returns - the damage rolled with metamagic applied.
|
|
int PRCGetMetaMagicDamage(int nDamageType, int nDice, int nDieSize,
|
|
int nBonusPerDie = 0, int nBonus = 0, int nMetaMagic = -1);
|
|
|
|
// Function to save the school of the currently cast spell in a variable. This should be
|
|
// called at the beginning of the script to set the spell school (passing the school) and
|
|
// once at the end of the script (with no arguments) to delete the variable.
|
|
// nSchool - SPELL_SCHOOL_xxx constant to set, if general then the variable is
|
|
// deleted.
|
|
// moved from spinc_common and renamed
|
|
void PRCSetSchool(int nSchool = SPELL_SCHOOL_GENERAL);
|
|
|
|
/**
|
|
* Signals a spell has been cast. Acts as a wrapper to fire EVENT_SPELL_CAST_AT
|
|
* via SignalEvent()
|
|
* @param oTarget Target of the spell.
|
|
* @param bHostile TRUE if the spell is a hostile act.
|
|
* @param nSpellID Spell ID or -1 if PRCGetSpellId() should be used.
|
|
* @param oCaster Object doing the casting.
|
|
*/
|
|
void PRCSignalSpellEvent(object oTarget, int bHostile = TRUE, int nSpellID = -1, object oCaster = OBJECT_SELF);
|
|
|
|
//GetFirstObjectInShape wrapper for changing the AOE of the channeled spells (Spellsword Channel Spell)
|
|
object MyFirstObjectInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0]);
|
|
|
|
//GetNextObjectInShape wrapper for changing the AOE of the channeled spells (Spellsword Channel Spell)
|
|
object MyNextObjectInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0]);
|
|
|
|
// * Kovi. removes any effects from this type of spell
|
|
// * i.e., used in Mage Armor to remove any previous
|
|
// * mage armors
|
|
void PRCRemoveEffectsFromSpell(object oTarget, int SpellID);
|
|
|
|
// * Get Scaled Effect
|
|
effect PRCGetScaledEffect(effect eStandard, object oTarget);
|
|
|
|
// * Searchs through a persons effects and removes all those of a specific type.
|
|
void PRCRemoveSpecificEffect(int nEffectTypeID, object oTarget);
|
|
|
|
// * Returns true if Target is a humanoid
|
|
int PRCAmIAHumanoid(object oTarget);
|
|
|
|
// * Get Difficulty Duration
|
|
int PRCGetScaledDuration(int nActualDuration, object oTarget);
|
|
|
|
// * Will pass back a linked effect for all the protection from alignment spells. The power represents the multiplier of strength.
|
|
// * That is instead of +3 AC and +2 Saves a power of 2 will yield +6 AC and +4 Saves.
|
|
effect PRCCreateProtectionFromAlignmentLink(int nAlignment, int nPower = 1);
|
|
|
|
// * Returns the time in seconds that the effect should be delayed before application.
|
|
float PRCGetSpellEffectDelay(location SpellTargetLocation, object oTarget);
|
|
|
|
// * This is a wrapper for how Petrify will work in Expansion Pack 1
|
|
// * Scripts affected: flesh to stone, breath petrification, gaze petrification, touch petrification
|
|
// * nPower : This is the Hit Dice of a Monster using Gaze, Breath or Touch OR it is the Caster Spell of
|
|
// * a spellcaster
|
|
// * nFortSaveDC: pass in this number from the spell script
|
|
void PRCDoPetrification(int nPower, object oSource, object oTarget, int nSpellID, int nFortSaveDC);
|
|
|
|
int PRCGetDelayedSpellEffectsExpired(int nSpell_ID, object oTarget, object oCaster);
|
|
|
|
int PRCGetSpellUsesLeft(int nRealSpellID, object oCreature = OBJECT_SELF);
|
|
// -----------------
|
|
// END SPELLSWORD
|
|
// -----------------
|
|
|
|
// Functions mostly only useful within the scope of this include
|
|
int BWSavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0);
|
|
|
|
// This function holds all functions that are supposed to run before the actual
|
|
// spellscript gets run. If this functions returns FALSE, the spell is aborted
|
|
// and the spellscript will not run
|
|
int X2PreSpellCastCode();
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Constants */
|
|
//////////////////////////////////////////////////
|
|
|
|
// this could also be put into in prc_inc_switches
|
|
const string PRC_SAVEDC_OVERRIDE = "PRC_SAVEDC_OVERRIDE";
|
|
|
|
const int TYPE_ARCANE = -1;
|
|
const int TYPE_DIVINE = -2;
|
|
|
|
|
|
//Changed to use CLASS_TYPE_* instead
|
|
//const int TYPE_SORCERER = 2;
|
|
//const int TYPE_WIZARD = 3;
|
|
//const int TYPE_BARD = 4;
|
|
//const int TYPE_CLERIC = 11;
|
|
//const int TYPE_DRUID = 12;
|
|
//const int TYPE_RANGER = 13;
|
|
//const int TYPE_PALADIN = 14;
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Includes */
|
|
//////////////////////////////////////////////////
|
|
|
|
//#include "inc_abil_damage"
|
|
//#include "prc_inc_castlvl"
|
|
//#include "lookup_2da_spell"
|
|
|
|
// ** THIS ORDER IS IMPORTANT **
|
|
|
|
//#include "inc_lookups" // access via prc_inc_core
|
|
#include "inc_newspellbook"
|
|
|
|
//#include "prc_inc_core" // Compiler won't allow access via inc_newspellbook
|
|
|
|
#include "inc_vfx_const"
|
|
#include "spinc_necro_cyst"
|
|
#include "true_utter_const"
|
|
//#include "shd_myst_const"
|
|
//#include "prc_spellhook"
|
|
#include "prc_inc_sneak"
|
|
#include "prcsp_engine"
|
|
//#include "prc_inc_descrptr"
|
|
#include "inc_item_props"
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function definitions */
|
|
//////////////////////////////////////////////////
|
|
|
|
int GetPrCAdjustedClassLevel(int nClass, object oCaster = OBJECT_SELF)
|
|
{
|
|
int iTemp;
|
|
// is it arcane, divine or neither?
|
|
if(GetIsArcaneClass(nClass, oCaster) && nClass != CLASS_TYPE_SUBLIME_CHORD)
|
|
{
|
|
if (GetPrimaryArcaneClass(oCaster) == nClass) // adjust for any PrCs
|
|
iTemp = GetArcanePRCLevels(oCaster, nClass);
|
|
}
|
|
else if(GetIsDivineClass(nClass, oCaster))
|
|
{
|
|
if (GetPrimaryDivineClass(oCaster) == nClass) // adjust for any PrCs
|
|
iTemp = GetDivinePRCLevels(oCaster, nClass);
|
|
}
|
|
else // a non-caster class or a PrC
|
|
{
|
|
return 0;
|
|
}
|
|
// add the caster class levels
|
|
return iTemp += GetLevelByClass(nClass, oCaster);
|
|
}
|
|
|
|
int GetPrCAdjustedCasterLevel(int nClass, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE)
|
|
{
|
|
int iTemp;
|
|
iTemp = GetPrCAdjustedClassLevel(nClass, oCaster);
|
|
iTemp = iTemp / GetCasterLevelModifier(nClass);
|
|
if (bAdjustForPractisedSpellcaster)
|
|
iTemp += PracticedSpellcasting(oCaster, nClass, iTemp);
|
|
return iTemp;
|
|
}
|
|
|
|
int GetPrCAdjustedCasterLevelByType(int nClassType, object oCaster = OBJECT_SELF, int bAdjustForPractisedSpellcaster = TRUE)
|
|
{
|
|
int nClassLvl;
|
|
int nClass1, nClass2, nClass3, nClass4, nClass5, nClass6, nClass7, nClass8;
|
|
int nClass1Lvl, nClass2Lvl, nClass3Lvl, nClass4Lvl, nClass5Lvl, nClass6Lvl, nClass7Lvl, nClass8Lvl;
|
|
|
|
nClass1 = GetClassByPosition(1, oCaster);
|
|
nClass2 = GetClassByPosition(2, oCaster);
|
|
nClass3 = GetClassByPosition(3, oCaster);
|
|
nClass4 = GetClassByPosition(4, oCaster);
|
|
nClass5 = GetClassByPosition(5, oCaster);
|
|
nClass6 = GetClassByPosition(6, oCaster);
|
|
nClass7 = GetClassByPosition(7, oCaster);
|
|
nClass8 = GetClassByPosition(8, oCaster);
|
|
|
|
if(nClassType == TYPE_ARCANE && (GetFirstArcaneClassPosition(oCaster) > 0))
|
|
{
|
|
if (GetIsArcaneClass(nClass1, oCaster)) nClass1Lvl = GetPrCAdjustedCasterLevel(nClass1, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass2, oCaster)) nClass2Lvl = GetPrCAdjustedCasterLevel(nClass2, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass3, oCaster)) nClass3Lvl = GetPrCAdjustedCasterLevel(nClass3, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass4, oCaster)) nClass4Lvl = GetPrCAdjustedCasterLevel(nClass4, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass5, oCaster)) nClass5Lvl = GetPrCAdjustedCasterLevel(nClass5, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass6, oCaster)) nClass6Lvl = GetPrCAdjustedCasterLevel(nClass6, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass7, oCaster)) nClass7Lvl = GetPrCAdjustedCasterLevel(nClass7, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsArcaneClass(nClass8, oCaster)) nClass8Lvl = GetPrCAdjustedCasterLevel(nClass8, oCaster, bAdjustForPractisedSpellcaster);
|
|
}
|
|
else if (nClassType == TYPE_DIVINE && (GetFirstDivineClassPosition(oCaster) > 0))
|
|
{
|
|
if (GetIsDivineClass(nClass1, oCaster)) nClass1Lvl = GetPrCAdjustedCasterLevel(nClass1, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass2, oCaster)) nClass2Lvl = GetPrCAdjustedCasterLevel(nClass2, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass3, oCaster)) nClass3Lvl = GetPrCAdjustedCasterLevel(nClass3, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass4, oCaster)) nClass4Lvl = GetPrCAdjustedCasterLevel(nClass4, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass5, oCaster)) nClass5Lvl = GetPrCAdjustedCasterLevel(nClass5, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass6, oCaster)) nClass6Lvl = GetPrCAdjustedCasterLevel(nClass6, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass7, oCaster)) nClass7Lvl = GetPrCAdjustedCasterLevel(nClass7, oCaster, bAdjustForPractisedSpellcaster);
|
|
if (GetIsDivineClass(nClass8, oCaster)) nClass8Lvl = GetPrCAdjustedCasterLevel(nClass8, oCaster, bAdjustForPractisedSpellcaster);
|
|
}
|
|
int nHighest = nClass1Lvl;
|
|
if (nClass2Lvl > nHighest) nHighest = nClass2Lvl;
|
|
if (nClass3Lvl > nHighest) nHighest = nClass3Lvl;
|
|
if (nClass4Lvl > nHighest) nHighest = nClass4Lvl;
|
|
if (nClass5Lvl > nHighest) nHighest = nClass5Lvl;
|
|
if (nClass6Lvl > nHighest) nHighest = nClass6Lvl;
|
|
if (nClass7Lvl > nHighest) nHighest = nClass7Lvl;
|
|
if (nClass8Lvl > nHighest) nHighest = nClass8Lvl;
|
|
return nHighest;
|
|
}
|
|
|
|
int GetLevelByTypeArcaneFeats(object oCaster = OBJECT_SELF, int iSpellID = -1)
|
|
{
|
|
int iFirstArcane = GetPrimaryArcaneClass(oCaster);
|
|
int iBest = 0;
|
|
int iClass1 = GetClassByPosition(1, oCaster);
|
|
int iClass2 = GetClassByPosition(2, oCaster);
|
|
int iClass3 = GetClassByPosition(3, oCaster);
|
|
int iClass4 = GetClassByPosition(4, oCaster);
|
|
int iClass5 = GetClassByPosition(5, oCaster);
|
|
int iClass6 = GetClassByPosition(6, oCaster);
|
|
int iClass7 = GetClassByPosition(7, oCaster);
|
|
int iClass8 = GetClassByPosition(8, oCaster);
|
|
|
|
int iClass1Lev = GetLevelByPosition(1, oCaster);
|
|
int iClass2Lev = GetLevelByPosition(2, oCaster);
|
|
int iClass3Lev = GetLevelByPosition(3, oCaster);
|
|
int iClass4Lev = GetLevelByPosition(4, oCaster);
|
|
int iClass5Lev = GetLevelByPosition(5, oCaster);
|
|
int iClass6Lev = GetLevelByPosition(6, oCaster);
|
|
int iClass7Lev = GetLevelByPosition(7, oCaster);
|
|
int iClass8Lev = GetLevelByPosition(8, oCaster);
|
|
|
|
if (iSpellID = -1) iSpellID = PRCGetSpellId(oCaster);
|
|
|
|
int iBoost = ShadowWeave(oCaster, iSpellID) +
|
|
FireAdept(oCaster, iSpellID) +
|
|
DomainPower(oCaster, iSpellID) +
|
|
StormMagic(oCaster) +
|
|
CormanthyranMoonMagic(oCaster) +
|
|
DraconicPower(oCaster);
|
|
|
|
if (iClass1 == CLASS_TYPE_HEXBLADE) iClass1Lev = (iClass1Lev >= 4) ? (iClass1Lev / 2) : 0;
|
|
if (iClass2 == CLASS_TYPE_HEXBLADE) iClass2Lev = (iClass2Lev >= 4) ? (iClass2Lev / 2) : 0;
|
|
if (iClass3 == CLASS_TYPE_HEXBLADE) iClass3Lev = (iClass3Lev >= 4) ? (iClass3Lev / 2) : 0;
|
|
if (iClass4 == CLASS_TYPE_HEXBLADE) iClass4Lev = (iClass4Lev >= 4) ? (iClass4Lev / 2) : 0;
|
|
if (iClass5 == CLASS_TYPE_HEXBLADE) iClass5Lev = (iClass5Lev >= 4) ? (iClass5Lev / 2) : 0;
|
|
if (iClass6 == CLASS_TYPE_HEXBLADE) iClass6Lev = (iClass6Lev >= 4) ? (iClass6Lev / 2) : 0;
|
|
if (iClass7 == CLASS_TYPE_HEXBLADE) iClass7Lev = (iClass7Lev >= 4) ? (iClass7Lev / 2) : 0;
|
|
if (iClass8 == CLASS_TYPE_HEXBLADE) iClass8Lev = (iClass8Lev >= 4) ? (iClass8Lev / 2) : 0;
|
|
|
|
if (iClass1 == iFirstArcane) iClass1Lev += GetArcanePRCLevels(oCaster, iClass1);
|
|
if (iClass2 == iFirstArcane) iClass2Lev += GetArcanePRCLevels(oCaster, iClass2);
|
|
if (iClass3 == iFirstArcane) iClass3Lev += GetArcanePRCLevels(oCaster, iClass3);
|
|
if (iClass4 == iFirstArcane) iClass4Lev += GetArcanePRCLevels(oCaster, iClass4);
|
|
if (iClass5 == iFirstArcane) iClass5Lev += GetArcanePRCLevels(oCaster, iClass5);
|
|
if (iClass6 == iFirstArcane) iClass6Lev += GetArcanePRCLevels(oCaster, iClass6);
|
|
if (iClass7 == iFirstArcane) iClass7Lev += GetArcanePRCLevels(oCaster, iClass7);
|
|
if (iClass8 == iFirstArcane) iClass8Lev += GetArcanePRCLevels(oCaster, iClass8);
|
|
|
|
iClass1Lev += iBoost;
|
|
iClass2Lev += iBoost;
|
|
iClass3Lev += iBoost;
|
|
iClass4Lev += iBoost;
|
|
iClass5Lev += iBoost;
|
|
iClass6Lev += iBoost;
|
|
iClass7Lev += iBoost;
|
|
iClass8Lev += iBoost;
|
|
|
|
iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev);
|
|
iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev);
|
|
iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev);
|
|
iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev);
|
|
iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev);
|
|
iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev);
|
|
iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass7Lev);
|
|
iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass8Lev);
|
|
|
|
if (!GetIsArcaneClass(iClass1, oCaster)) iClass1Lev = 0;
|
|
if (!GetIsArcaneClass(iClass2, oCaster)) iClass2Lev = 0;
|
|
if (!GetIsArcaneClass(iClass3, oCaster)) iClass3Lev = 0;
|
|
if (!GetIsArcaneClass(iClass4, oCaster)) iClass4Lev = 0;
|
|
if (!GetIsArcaneClass(iClass5, oCaster)) iClass5Lev = 0;
|
|
if (!GetIsArcaneClass(iClass6, oCaster)) iClass6Lev = 0;
|
|
if (!GetIsArcaneClass(iClass7, oCaster)) iClass7Lev = 0;
|
|
if (!GetIsArcaneClass(iClass8, oCaster)) iClass8Lev = 0;
|
|
|
|
if (iClass1Lev > iBest) iBest = iClass1Lev;
|
|
if (iClass2Lev > iBest) iBest = iClass2Lev;
|
|
if (iClass3Lev > iBest) iBest = iClass3Lev;
|
|
if (iClass4Lev > iBest) iBest = iClass4Lev;
|
|
if (iClass5Lev > iBest) iBest = iClass5Lev;
|
|
if (iClass6Lev > iBest) iBest = iClass6Lev;
|
|
if (iClass7Lev > iBest) iBest = iClass7Lev;
|
|
if (iClass8Lev > iBest) iBest = iClass8Lev;
|
|
|
|
return iBest;
|
|
}
|
|
|
|
int GetLevelByTypeDivineFeats(object oCaster = OBJECT_SELF, int iSpellID = -1)
|
|
{
|
|
int iFirstDivine = GetPrimaryDivineClass(oCaster);
|
|
int iBest = 0;
|
|
int iClass1 = GetClassByPosition(1, oCaster);
|
|
int iClass2 = GetClassByPosition(2, oCaster);
|
|
int iClass3 = GetClassByPosition(3, oCaster);
|
|
int iClass4 = GetClassByPosition(4, oCaster);
|
|
int iClass5 = GetClassByPosition(5, oCaster);
|
|
int iClass6 = GetClassByPosition(6, oCaster);
|
|
int iClass7 = GetClassByPosition(7, oCaster);
|
|
int iClass8 = GetClassByPosition(8, oCaster);
|
|
|
|
int iClass1Lev = GetLevelByPosition(1, oCaster);
|
|
int iClass2Lev = GetLevelByPosition(2, oCaster);
|
|
int iClass3Lev = GetLevelByPosition(3, oCaster);
|
|
int iClass4Lev = GetLevelByPosition(4, oCaster);
|
|
int iClass5Lev = GetLevelByPosition(5, oCaster);
|
|
int iClass6Lev = GetLevelByPosition(6, oCaster);
|
|
int iClass7Lev = GetLevelByPosition(7, oCaster);
|
|
int iClass8Lev = GetLevelByPosition(8, oCaster);
|
|
|
|
if (iSpellID = -1) iSpellID = PRCGetSpellId(oCaster);
|
|
|
|
int iBoost = ShadowWeave(oCaster, iSpellID) +
|
|
FireAdept(oCaster, iSpellID) +
|
|
DomainPower(oCaster, iSpellID) +
|
|
CormanthyranMoonMagic(oCaster) +
|
|
StormMagic(oCaster);
|
|
|
|
if (iClass1 == CLASS_TYPE_PALADIN
|
|
|| iClass1 == CLASS_TYPE_RANGER
|
|
|| iClass1 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass1Lev = iClass1Lev / 2;
|
|
if (iClass2 == CLASS_TYPE_PALADIN
|
|
|| iClass2 == CLASS_TYPE_RANGER
|
|
|| iClass2 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass2Lev = iClass2Lev / 2;
|
|
if (iClass3 == CLASS_TYPE_PALADIN
|
|
|| iClass3 == CLASS_TYPE_RANGER
|
|
|| iClass3 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass3Lev = iClass3Lev / 2;
|
|
if (iClass4 == CLASS_TYPE_PALADIN
|
|
|| iClass4 == CLASS_TYPE_RANGER
|
|
|| iClass4 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass4Lev = iClass4Lev / 2;
|
|
if (iClass5 == CLASS_TYPE_PALADIN
|
|
|| iClass5 == CLASS_TYPE_RANGER
|
|
|| iClass5 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass5Lev = iClass5Lev / 2;
|
|
if (iClass6 == CLASS_TYPE_PALADIN
|
|
|| iClass6 == CLASS_TYPE_RANGER
|
|
|| iClass6 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass6Lev = iClass6Lev / 2;
|
|
if (iClass7 == CLASS_TYPE_PALADIN
|
|
|| iClass7 == CLASS_TYPE_RANGER
|
|
|| iClass7 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass7Lev = iClass7Lev / 2;
|
|
if (iClass8 == CLASS_TYPE_PALADIN
|
|
|| iClass8 == CLASS_TYPE_RANGER
|
|
|| iClass8 == CLASS_TYPE_ANTI_PALADIN)
|
|
iClass8Lev = iClass7Lev / 2;
|
|
|
|
if (iClass1 == iFirstDivine) iClass1Lev += GetDivinePRCLevels(oCaster, iClass1);
|
|
if (iClass2 == iFirstDivine) iClass2Lev += GetDivinePRCLevels(oCaster, iClass2);
|
|
if (iClass3 == iFirstDivine) iClass3Lev += GetDivinePRCLevels(oCaster, iClass3);
|
|
if (iClass4 == iFirstDivine) iClass4Lev += GetDivinePRCLevels(oCaster, iClass4);
|
|
if (iClass5 == iFirstDivine) iClass5Lev += GetDivinePRCLevels(oCaster, iClass5);
|
|
if (iClass6 == iFirstDivine) iClass6Lev += GetDivinePRCLevels(oCaster, iClass6);
|
|
if (iClass7 == iFirstDivine) iClass7Lev += GetDivinePRCLevels(oCaster, iClass7);
|
|
if (iClass8 == iFirstDivine) iClass8Lev += GetDivinePRCLevels(oCaster, iClass8);
|
|
|
|
iClass1Lev += iBoost;
|
|
iClass2Lev += iBoost;
|
|
iClass3Lev += iBoost;
|
|
iClass4Lev += iBoost;
|
|
iClass5Lev += iBoost;
|
|
iClass6Lev += iBoost;
|
|
iClass7Lev += iBoost;
|
|
iClass8Lev += iBoost;
|
|
|
|
iClass1Lev += PracticedSpellcasting(oCaster, iClass1, iClass1Lev);
|
|
iClass2Lev += PracticedSpellcasting(oCaster, iClass2, iClass2Lev);
|
|
iClass3Lev += PracticedSpellcasting(oCaster, iClass3, iClass3Lev);
|
|
iClass4Lev += PracticedSpellcasting(oCaster, iClass4, iClass4Lev);
|
|
iClass5Lev += PracticedSpellcasting(oCaster, iClass5, iClass5Lev);
|
|
iClass6Lev += PracticedSpellcasting(oCaster, iClass6, iClass6Lev);
|
|
iClass7Lev += PracticedSpellcasting(oCaster, iClass7, iClass7Lev);
|
|
iClass8Lev += PracticedSpellcasting(oCaster, iClass8, iClass8Lev);
|
|
|
|
if (!GetIsDivineClass(iClass1, oCaster)) iClass1Lev = 0;
|
|
if (!GetIsDivineClass(iClass2, oCaster)) iClass2Lev = 0;
|
|
if (!GetIsDivineClass(iClass3, oCaster)) iClass3Lev = 0;
|
|
if (!GetIsDivineClass(iClass4, oCaster)) iClass4Lev = 0;
|
|
if (!GetIsDivineClass(iClass5, oCaster)) iClass5Lev = 0;
|
|
if (!GetIsDivineClass(iClass6, oCaster)) iClass6Lev = 0;
|
|
if (!GetIsDivineClass(iClass7, oCaster)) iClass7Lev = 0;
|
|
if (!GetIsDivineClass(iClass8, oCaster)) iClass8Lev = 0;
|
|
|
|
if (iClass1Lev > iBest) iBest = iClass1Lev;
|
|
if (iClass2Lev > iBest) iBest = iClass2Lev;
|
|
if (iClass3Lev > iBest) iBest = iClass3Lev;
|
|
if (iClass4Lev > iBest) iBest = iClass4Lev;
|
|
if (iClass5Lev > iBest) iBest = iClass5Lev;
|
|
if (iClass6Lev > iBest) iBest = iClass6Lev;
|
|
if (iClass7Lev > iBest) iBest = iClass7Lev;
|
|
if (iClass8Lev > iBest) iBest = iClass8Lev;
|
|
|
|
return iBest;
|
|
}
|
|
|
|
// looks up the spell level for the arcane caster classes (Wiz_Sorc, Bard) in spells.2da.
|
|
// Caveat: some onhitcast spells don't have any spell-levels listed for any class
|
|
int GetIsArcaneSpell (int iSpellId)
|
|
{
|
|
return Get2DACache("spells", "Wiz_Sorc", iSpellId) != ""
|
|
|| Get2DACache("spells", "Bard", iSpellId) != "";
|
|
}
|
|
|
|
// looks up the spell level for the divine caster classes (Cleric, Druid, Ranger, Paladin) in spells.2da.
|
|
// Caveat: some onhitcast spells don't have any spell-levels listed for any class
|
|
int GetIsDivineSpell (int iSpellId)
|
|
{
|
|
return Get2DACache("spells", "Cleric", iSpellId) != ""
|
|
|| Get2DACache("spells", "Druid", iSpellId) != ""
|
|
|| Get2DACache("spells", "Ranger", iSpellId) != ""
|
|
|| Get2DACache("spells", "Paladin", iSpellId) != "";
|
|
}
|
|
|
|
int BWSavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0)
|
|
{
|
|
// GZ: sanity checks to prevent wrapping around
|
|
if (nDC < 1) nDC = 1;
|
|
else if (nDC > 255) nDC = 255;
|
|
|
|
int bValid = 0;
|
|
int nEff;
|
|
|
|
//some maneuvers allow people to use skill check instead of a save
|
|
if(nSavingThrow == SAVING_THROW_FORT)
|
|
{
|
|
bValid = GetLocalInt(oTarget, "MindOverBody") ?
|
|
GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) :
|
|
FortitudeSave(oTarget, nDC, nSaveType, oSaveVersus);
|
|
if(bValid == 1) nEff = VFX_IMP_FORTITUDE_SAVING_THROW_USE;
|
|
}
|
|
else if(nSavingThrow == SAVING_THROW_REFLEX)
|
|
{
|
|
bValid = GetLocalInt(oTarget, "ActionBeforeThought") ?
|
|
GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) :
|
|
ReflexSave(oTarget, nDC, nSaveType, oSaveVersus);
|
|
if(bValid == 1) nEff = VFX_IMP_REFLEX_SAVE_THROW_USE;
|
|
}
|
|
else if(nSavingThrow == SAVING_THROW_WILL)
|
|
{
|
|
bValid = GetLocalInt(oTarget, "MomentOfPerfectMind") ?
|
|
GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC) :
|
|
WillSave(oTarget, nDC, nSaveType, oSaveVersus);
|
|
if(bValid == 1) nEff = VFX_IMP_WILL_SAVING_THROW_USE;
|
|
}
|
|
|
|
/*
|
|
return 0 = FAILED SAVE
|
|
return 1 = SAVE SUCCESSFUL
|
|
return 2 = IMMUNE TO WHAT WAS BEING SAVED AGAINST
|
|
*/
|
|
if(bValid == 0)
|
|
{
|
|
int nSpellID = PRCGetSpellId(oSaveVersus);
|
|
if(nSaveType == SAVING_THROW_TYPE_DEATH
|
|
|| nSpellID == SPELL_WEIRD
|
|
|| nSpellID == SPELL_FINGER_OF_DEATH)
|
|
//&& nSpellID != SPELL_HORRID_WILTING)
|
|
{
|
|
if(fDelay > 0.0f) DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oTarget));
|
|
else ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_DEATH), oTarget);
|
|
}
|
|
}
|
|
else //if(bValid == 1 || bValid == 2)
|
|
{
|
|
if(bValid == 2) nEff = VFX_IMP_MAGIC_RESISTANCE_USE;
|
|
|
|
if(fDelay > 0.0f) DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nEff), oTarget));
|
|
else ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(nEff), oTarget);
|
|
}
|
|
return bValid;
|
|
}
|
|
|
|
void PRCBonusDamage(object oTarget, object oCaster = OBJECT_SELF)
|
|
{
|
|
// Does nothing, left here so files don't need to be edited.
|
|
}
|
|
|
|
// Bonus damage to a spell for Spell Betrayal Ability
|
|
int SpellBetrayalDamage(object oTarget, object oCaster)
|
|
{
|
|
int iDam = 0;
|
|
int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster);
|
|
|
|
if(ThrallLevel >= 2)
|
|
{
|
|
if( GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE) )
|
|
{
|
|
ThrallLevel /= 2;
|
|
iDam = d6(ThrallLevel);
|
|
}
|
|
}
|
|
|
|
return iDam;
|
|
}
|
|
|
|
// Bonus damage to a spell for Spellstrike Ability
|
|
int SpellStrikeDamage(object oTarget, object oCaster)
|
|
{
|
|
int iDam = 0;
|
|
int ThrallLevel = GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_A, oCaster) + GetLevelByClass(CLASS_TYPE_THRALL_OF_GRAZZT_D, oCaster);
|
|
|
|
if(ThrallLevel >= 6)
|
|
{
|
|
if( GetIsAOEFlanked(oTarget, oCaster) )
|
|
{
|
|
ThrallLevel /= 4;
|
|
iDam = d6(ThrallLevel);
|
|
}
|
|
}
|
|
|
|
return iDam;
|
|
}
|
|
|
|
// Adds the bonus damage from both Spell Betrayal and Spellstrike together
|
|
int ApplySpellBetrayalStrikeDamage(object oTarget, object oCaster, int bShowTextString = TRUE)
|
|
{
|
|
int iDam = 0;
|
|
int iBetrayalDam = SpellBetrayalDamage(oTarget, oCaster);
|
|
int iStrikeDam = SpellStrikeDamage(oTarget, oCaster);
|
|
string sMes = "";
|
|
|
|
if(iStrikeDam > 0 && iBetrayalDam > 0) sMes ="*Spellstrike Betrayal Sucess*";
|
|
else if(iBetrayalDam > 0) sMes ="*Spell Betrayal Sucess*";
|
|
else if(iStrikeDam > 0) sMes ="*Spellstrike Sucess*";
|
|
|
|
if(bShowTextString) FloatingTextStringOnCreature(sMes, oCaster, TRUE);
|
|
|
|
iDam = iBetrayalDam + iStrikeDam;
|
|
|
|
// debug code
|
|
//sMes = "Spell Betrayal / Spellstrike Bonus Damage: " + IntToString(iBetrayalDam) + " + " + IntToString(iStrikeDam) + " = " + IntToString(iDam);
|
|
//DelayCommand( 1.0, FloatingTextStringOnCreature(sMes, oCaster, TRUE) );
|
|
|
|
return iDam;
|
|
}
|
|
|
|
int SpellDamagePerDice(object oCaster, int nDice)
|
|
{
|
|
// Arcane only
|
|
if (!GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster)))
|
|
return 0;
|
|
|
|
int nDam = 0;
|
|
nDam += GetLocalInt(oCaster, "StrengthFromMagic") * nDice * 2;
|
|
|
|
if (GetLocalInt(oCaster, "WarsoulTyrant"))
|
|
nDam += nDice * GetHitDice(oCaster);
|
|
|
|
if (DEBUG) DoDebug("SpellDamagePerDice returning "+IntToString(nDam)+" for "+GetName(oCaster));
|
|
|
|
return nDam;
|
|
}
|
|
|
|
int PRCMySavingThrow(int nSavingThrow, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF, float fDelay = 0.0, int bImmunityCheck = FALSE)
|
|
{
|
|
int nSpell = PRCGetSpellId(oSaveVersus);
|
|
int nSpellSchool = GetSpellSchool(nSpell);
|
|
|
|
//If bImmunityCheck == FALSE: returns 0 if failure or immune, 1 if success - same as MySavingThrow
|
|
//If bImmunityCheck == TRUE: returns 0 if failure, 1 if success, 2 if immune
|
|
|
|
// Enigma Helm Crown Bind
|
|
if (GetIsMeldBound(oTarget, MELD_ENIGMA_HELM) == CHAKRA_CROWN && GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM))
|
|
return 2;
|
|
|
|
// Planar Ward
|
|
if (GetHasSpellEffect(MELD_PLANAR_WARD, oTarget) && (GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM) || GetIsOfSubschool(nSpell, SUBSCHOOL_COMPULSION)))
|
|
return 2;
|
|
|
|
// Chucoclops influence makes you autofail fear saves
|
|
if (GetHasSpellEffect(VESTIGE_CHUPOCLOPS, oTarget) && !GetLocalInt(oTarget, "PactQuality"+IntToString(VESTIGE_CHUPOCLOPS)) && nSaveType == SAVING_THROW_TYPE_FEAR)
|
|
return 1;
|
|
|
|
// Iron Mind's Mind Over Body, allows them to treat other saves as will saves up to 3/day.
|
|
// No point in having it trigger when its a will save.
|
|
if(nSavingThrow != SAVING_THROW_WILL && GetLocalInt(oTarget, "IronMind_MindOverBody"))
|
|
{
|
|
nSavingThrow = SAVING_THROW_WILL;
|
|
DeleteLocalInt(oTarget, "IronMind_MindOverBody");
|
|
}
|
|
|
|
// Handle the target having Force of Will and being targeted by a psionic power
|
|
if(nSavingThrow != SAVING_THROW_WILL &&
|
|
((nSpell > 14000 && nSpell < 14360) ||
|
|
(nSpell > 15350 && nSpell < 15470)
|
|
) &&
|
|
GetHasFeat(FEAT_FORCE_OF_WILL, oTarget) &&
|
|
!GetLocalInt(oTarget, "ForceOfWillUsed") &&
|
|
// Only use will save if it's better
|
|
((nSavingThrow == SAVING_THROW_FORT ? GetFortitudeSavingThrow(oTarget) : GetReflexSavingThrow(oTarget)) > GetWillSavingThrow(oTarget))
|
|
)
|
|
{
|
|
nSavingThrow = SAVING_THROW_WILL;
|
|
SetLocalInt(oTarget, "ForceOfWillUsed", TRUE);
|
|
DelayCommand(6.0f, DeleteLocalInt(oTarget, "ForceOfWillUsed"));
|
|
// "Force of Will used for this round."
|
|
FloatingTextStrRefOnCreature(16826670, oTarget, FALSE);
|
|
}
|
|
|
|
//TouchOfDistraction
|
|
if (GetLocalInt(oTarget, "HasTouchOfDistraction") && (nSavingThrow = SAVING_THROW_REFLEX))
|
|
{
|
|
nDC += 2;
|
|
DeleteLocalInt(oTarget, "HasTouchOfDistraction");
|
|
}
|
|
|
|
//Diamond Defense
|
|
if(GetLocalInt(oTarget, "PRC_TOB_DIAMOND_DEFENSE"))
|
|
nDC -= GetLocalInt(oTarget, "PRC_TOB_DIAMOND_DEFENSE");
|
|
|
|
// Master of Nine
|
|
if(GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oSaveVersus) > 2)
|
|
{
|
|
int nLastClass = GetLocalInt(oSaveVersus, "PRC_CurrentManeuver_InitiatingClass") - 1;
|
|
if(nLastClass == CLASS_TYPE_WARBLADE
|
|
|| nLastClass == CLASS_TYPE_SWORDSAGE
|
|
|| nLastClass == CLASS_TYPE_CRUSADER)
|
|
{
|
|
// Increases maneuver DCs by 1
|
|
nDC += 1;
|
|
}
|
|
}
|
|
|
|
// Incarnum Resistance
|
|
if(GetHasFeat(FEAT_INCARNUM_RESISTANCE, oTarget) && nSpell > 18900 && nSpell < 18969)
|
|
nDC -= 2;
|
|
|
|
// This is done here because it is possible to tell the saving throw type here
|
|
// it works by lowering the DC rather than adding to the save
|
|
// same net effect but slightly different numbers
|
|
if(nSaveType == SAVING_THROW_TYPE_MIND_SPELLS)
|
|
{
|
|
//Thayan Knights auto-fail mind spells cast by red wizards
|
|
if(GetLevelByClass(CLASS_TYPE_RED_WIZARD, oSaveVersus)
|
|
&& GetLevelByClass(CLASS_TYPE_THAYAN_KNIGHT, oTarget))
|
|
return 0;
|
|
// Tyranny Domain increases the DC of mind spells by +2.
|
|
if(GetHasFeat(FEAT_DOMAIN_POWER_TYRANNY, oSaveVersus))
|
|
nDC += 2;
|
|
// +2 bonus on saves against mind affecting, done here
|
|
if(GetLevelByClass(CLASS_TYPE_FIST_DAL_QUOR, oTarget) > 1)
|
|
nDC -= 2;
|
|
// Scorpion's Resolve gives a +4 bonus on mind affecting saves
|
|
if(GetHasFeat(FEAT_SCORPIONS_RESOLVE, oTarget))
|
|
nDC -= 4;
|
|
// Warped Mind gives a bonus on mind affecting equal to half aberrant
|
|
if(GetHasFeat(FEAT_ABERRANT_WARPED_MIND, oTarget))
|
|
nDC -= GetAberrantFeatCount(oTarget)/2;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_FEAR)
|
|
{
|
|
// Unnatural Will adds Charisma to saves against fear
|
|
if(GetHasFeat(FEAT_UNNATURAL_WILL, oTarget))
|
|
nDC -= GetAbilityModifier(ABILITY_CHARISMA, oTarget);
|
|
// Krinth have +4 saves against Fear
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_KRINTH)
|
|
nDC -= 4;
|
|
// Turlemoi/Lashemoi have -4 saves against Fear
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_LASHEMOI || GetRacialType(oTarget) == RACIAL_TYPE_TURLEMOI || GetLocalInt(oTarget, "SpeedFromPain") >= 3)
|
|
nDC += 4;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_NEGATIVE)
|
|
{
|
|
// +4 bonus on saves against negative energy, done here
|
|
if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 8)
|
|
nDC -= 4;
|
|
// Jergal's Pact gives a +2 bonus on negative energy saves
|
|
if(GetHasFeat(FEAT_JERGALS_PACT, oTarget))
|
|
nDC -= 2;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_DISEASE)
|
|
{
|
|
// Plague Resistant gives a +4 bonus on disease saves
|
|
if(GetHasFeat(FEAT_PLAGUE_RESISTANT, oTarget))
|
|
nDC -= 4;
|
|
// +4/+2 bonus on saves against disease, done here
|
|
if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13)
|
|
nDC -= 4;
|
|
else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3)
|
|
nDC -= 2;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_POISON)
|
|
{
|
|
// +4/+2 bonus on saves against poison, done here
|
|
if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 13)
|
|
nDC -= 4;
|
|
else if(GetLevelByClass(CLASS_TYPE_DREAD_NECROMANCER, oTarget) > 3)
|
|
nDC -= 2;
|
|
if(GetHasFeat(FEAT_POISON_4, oTarget))
|
|
nDC -= 4;
|
|
if(GetHasFeat(FEAT_POISON_3, oTarget))
|
|
nDC -= 3;
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_EXTAMINAAR && nSavingThrow == SAVING_THROW_FORT)
|
|
nDC -= 2;
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK)
|
|
nDC -= 1;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_FIRE)
|
|
{
|
|
// Bloodline of Fire gives a +4 bonus on fire saves
|
|
if(GetHasFeat(FEAT_BLOODLINE_OF_FIRE, oTarget))
|
|
nDC -= 4;
|
|
if(GetHasFeat(FEAT_HARD_FIRE, oTarget))
|
|
nDC -= 1 + (GetHitDice(oTarget) / 5);
|
|
// +2 vs fire for Halfgiant
|
|
if(GetHasFeat(FEAT_ACCLIMATED_FIRE, oTarget))
|
|
nDC -= 2;
|
|
// +2 vs fire for Heat Endurance feat
|
|
if(GetHasFeat(FEAT_HEAT_ENDURANCE, oTarget))
|
|
nDC -= 2;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_COLD)
|
|
{
|
|
if(GetHasFeat(FEAT_HARD_WATER, oTarget))
|
|
nDC -= 1 + (GetHitDice(oTarget) / 5);
|
|
// +2 vs cold for Cold Endurance feat
|
|
if(GetHasFeat(FEAT_COLD_ENDURANCE, oTarget))
|
|
nDC -= 2;
|
|
// +2 vs cold for Winterhide Shifter trait
|
|
if(GetHasFeat(FEAT_SHIFTER_WINTERHIDE, oTarget))
|
|
nDC -= 2;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_ELECTRICITY)
|
|
{
|
|
if(GetHasFeat(FEAT_HARD_AIR, oTarget))
|
|
nDC -= 1 + (GetHitDice(oTarget) / 5);
|
|
else if(GetHasFeat(FEAT_HARD_ELEC, oTarget))
|
|
nDC -= 2;
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_SONIC)
|
|
{
|
|
if(GetHasFeat(FEAT_HARD_AIR, oTarget))
|
|
nDC -= 1 + (GetHitDice(oTarget) / 5);
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_ACID)
|
|
{
|
|
if(GetHasFeat(FEAT_HARD_EARTH, oTarget))
|
|
nDC -= 1 + (GetHitDice(oTarget) / 5);
|
|
}
|
|
|
|
// This is done here because it is possible to tell the spell school here
|
|
// it works by lowering the DC rather than adding to the save
|
|
// same net effect but slightly different numbers
|
|
if(nSpellSchool == SPELL_SCHOOL_TRANSMUTATION)
|
|
{
|
|
// Full Moon's Trick - +2 vs Transmutation spels
|
|
if(GetLocalInt(oTarget, "FullMoon_Trans"))
|
|
nDC -= 2;
|
|
if(GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oTarget) >= 2)
|
|
nDC -= 4;
|
|
}
|
|
|
|
// Sapphire Heirarch gets +4 against Chaos and Transmutation effects
|
|
if(GetLevelByClass(CLASS_TYPE_SAPPHIRE_HIERARCH, oTarget) >= 2 && GetHasDescriptor(nSpell, DESCRIPTOR_CHAOTIC))
|
|
nDC -= 4;
|
|
|
|
// Charming Veil Brow bind grants Essentia bonus on saves against charm and compulsion
|
|
if(GetIsOfSubschool(nSpell, SUBSCHOOL_CHARM) || GetIsOfSubschool(nSpell, SUBSCHOOL_COMPULSION))
|
|
{
|
|
if (GetIsMeldBound(oTarget, MELD_CHARMING_VEIL) == CHAKRA_BROW)
|
|
nDC -= GetEssentiaInvested(oTarget, MELD_CHARMING_VEIL);
|
|
}
|
|
|
|
// Krinth get +1 save against Shadow spells
|
|
if (GetIsOfSubschool(nSpell, SUBSCHOOL_SHADOW) && GetRacialType(oTarget) == RACIAL_TYPE_KRINTH)
|
|
nDC -= 1;
|
|
|
|
//Psionic race save boosts - +1 vs all spells for Xeph
|
|
if(GetHasFeat(FEAT_XEPH_SPELLHARD, oTarget))
|
|
nDC -= 1;
|
|
|
|
// Apostate - 1/2 HD bonus to resist divine spells
|
|
if(GetHasFeat(FEAT_APOSTATE, oTarget))
|
|
{
|
|
//if divine
|
|
if(GetIsDivineClass(PRCGetLastSpellCastClass(oSaveVersus)))
|
|
nDC -= GetHitDice(oTarget) / 2;
|
|
}
|
|
|
|
// Hammer of Witches - +1 bonus against Arcane spells
|
|
if(GetIsObjectValid(GetItemPossessedBy(oTarget, "WOL_HammerWitches")))
|
|
{
|
|
if(GetIsArcaneClass(PRCGetLastSpellCastClass(oSaveVersus)))
|
|
nDC -= 1;
|
|
}
|
|
|
|
// Cultist of the Shattered Peak - +1 bonus against Arcane spells
|
|
if(GetLevelByClass(CLASS_TYPE_CULTIST_SHATTERED_PEAK, oTarget))
|
|
{
|
|
if(GetIsArcaneClass(PRCGetLastSpellCastClass(oSaveVersus)))
|
|
nDC -= 1;
|
|
}
|
|
|
|
//Insightful Divination
|
|
if(GetLocalInt(oTarget, "Insightful Divination"))
|
|
{
|
|
nDC -= GetLocalInt(oTarget, "Insightful Divination");
|
|
DeleteLocalInt(oTarget, "Insightful Divination");
|
|
}
|
|
|
|
// Phierans Resolve - +4 bonus vs spells with evil descriptor
|
|
if(GetHasSpellEffect(SPELL_PHIERANS_RESOLVE, oTarget) && GetHasDescriptor(nSpell, DESCRIPTOR_EVIL))
|
|
nDC -= 4;
|
|
|
|
//spell school modificators
|
|
int nSchool = GetLocalInt(oSaveVersus, "X2_L_LAST_SPELLSCHOOL_VAR");
|
|
if(nSchool == SPELL_SCHOOL_NECROMANCY)
|
|
{
|
|
// Necrotic Cyst penalty on Necromancy spells
|
|
if(GetPersistantLocalInt(oTarget, NECROTIC_CYST_MARKER))
|
|
nDC += 2;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_ILLUSION)
|
|
{
|
|
// Piercing Sight gives a +4 bonus on illusion saves
|
|
if(GetHasFeat(FEAT_PIERCING_SIGHT, oTarget))
|
|
nDC -= 4;
|
|
// Adds +2 to Illusion DCs
|
|
if(GetLocalInt(oSaveVersus, "ShadowTrickster"))
|
|
nDC += 2;
|
|
// Illusion Veil Meld
|
|
if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oSaveVersus))
|
|
nDC += 1;
|
|
// Illusion Veil Meld
|
|
if(GetHasSpellEffect(MELD_ILLUSION_VEIL, oTarget))
|
|
nDC -= GetEssentiaInvested(oTarget, MELD_ILLUSION_VEIL);
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK)
|
|
nDC -= 1;
|
|
}
|
|
else if(nSchool == SPELL_SCHOOL_ENCHANTMENT)
|
|
{
|
|
//check if Unsettling Enchantment applies
|
|
if(GetHasFeat(FEAT_UNSETTLING_ENCHANTMENT, oSaveVersus) && !GetIsImmune(oTarget, IMMUNITY_TYPE_MIND_SPELLS))
|
|
{
|
|
effect eLink = EffectLinkEffects(EffectACDecrease(2), EffectAttackDecrease(2));
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, 6.0);
|
|
}
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_KILLOREN)
|
|
nDC -= 2;
|
|
if(GetLocalInt(oTarget, "KillorenAncient"))
|
|
nDC -= 2;
|
|
if(GetRacialType(oTarget) == RACIAL_TYPE_MONGRELFOLK)
|
|
nDC -= 1;
|
|
}
|
|
|
|
// Hexblade gets a bonus against spells equal to his Charisma (Min +1)
|
|
if(nSchool && GetLevelByClass(CLASS_TYPE_HEXBLADE, oTarget))
|
|
{
|
|
int nHexCha = GetAbilityModifier(ABILITY_CHARISMA, oTarget);
|
|
if (nHexCha < 1) nHexCha = 1;
|
|
nDC -= nHexCha;
|
|
}
|
|
|
|
// Totemist gets a save v magical beasts
|
|
if(MyPRCGetRacialType(oSaveVersus) == RACIAL_TYPE_MAGICAL_BEAST && GetLevelByClass(CLASS_TYPE_TOTEMIST, oTarget) >= 3)
|
|
nDC -= 3;
|
|
|
|
int nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
|
|
// If the spell is save immune then the link must be applied in order to get the true immunity
|
|
// to be resisted. That is the reason for returing false and not true. True blocks the
|
|
// application of effects.
|
|
if(nSaveRoll == 2 && !bImmunityCheck)
|
|
return 0;
|
|
|
|
// Failed the save - check if we can reroll
|
|
if(!nSaveRoll)
|
|
{
|
|
if(nSaveType == SAVING_THROW_TYPE_MIND_SPELLS)
|
|
{
|
|
// Bond Of Loyalty
|
|
if(GetLocalInt(oTarget, "BondOfLoyalty"))
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
DeleteLocalInt(oTarget, "BondOfLoyalty");
|
|
}
|
|
}
|
|
else if(nSaveType == SAVING_THROW_TYPE_FEAR)
|
|
{
|
|
// Call To Battle Reroll
|
|
if(GetLocalInt(oTarget, "CallToBattle"))
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
DeleteLocalInt(oTarget, "CallToBattle");
|
|
}
|
|
}
|
|
|
|
// Second Chance power reroll
|
|
if(!nSaveRoll && GetLocalInt(oTarget, "PRC_Power_SecondChance_Active") // Second chance is active
|
|
&& !GetLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound")) // And hasn't yet been used for this round
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
|
|
// Can't use this ability again for a round
|
|
SetLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound", TRUE);
|
|
DelayCommand(6.0f, DeleteLocalInt(oTarget, "PRC_Power_SecondChance_UserForRound"));
|
|
}
|
|
|
|
// Zealous Surge Reroll
|
|
if(!nSaveRoll && GetLocalInt(oTarget, "ZealousSurge"))
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
|
|
// Ability Used
|
|
DeleteLocalInt(oTarget, "ZealousSurge");
|
|
}
|
|
|
|
// Balam's Cunning Reroll
|
|
if(!nSaveRoll && GetLocalInt(oTarget, "BalamCunning"))
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
|
|
// Ability Used
|
|
DeleteLocalInt(oTarget, "BalamCunning");
|
|
}
|
|
|
|
if(!nSaveRoll)
|
|
{
|
|
if(nSavingThrow == SAVING_THROW_REFLEX)
|
|
{
|
|
// Dive for Cover reroll
|
|
if(GetHasFeat(FEAT_DIVE_FOR_COVER, oTarget))
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, 6.0);
|
|
}
|
|
}
|
|
// Impetuous Endurance - fortitude or will save
|
|
else if(GetLevelByClass(CLASS_TYPE_KNIGHT, oTarget) > 16)
|
|
{
|
|
// Reroll
|
|
nSaveRoll = BWSavingThrow(nSavingThrow, oTarget, nDC, nSaveType, oSaveVersus, fDelay);
|
|
}
|
|
}
|
|
}
|
|
//Serene Guardian Unclouded Mind
|
|
int nSerene = GetLevelByClass(CLASS_TYPE_SERENE_GUARDIAN, oTarget);
|
|
//Applies on failed will saves from 9th level on
|
|
if (nSaveRoll == 1 && nSavingThrow == SAVING_THROW_WILL && nSerene >= 9)
|
|
{
|
|
if (GetIsSkillSuccessful(oTarget, SKILL_CONCENTRATION, nDC)) // Concentration check to beat the DC
|
|
nSaveRoll = 0;
|
|
}
|
|
|
|
// Iron Mind Barbed Mind ability
|
|
if(nSaveRoll == 1 && nSaveType == SAVING_THROW_TYPE_MIND_SPELLS)
|
|
{
|
|
// Only works in Heavy Armour
|
|
if(GetLevelByClass(CLASS_TYPE_IRONMIND, oTarget) > 9
|
|
&& GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget)) > 5)
|
|
{
|
|
// Spell/Power caster takes 1d6 damage and 1 Wisdom damage
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(d6(), DAMAGE_TYPE_MAGICAL), oSaveVersus);
|
|
ApplyAbilityDamage(oSaveVersus, ABILITY_WISDOM, 1, DURATION_TYPE_TEMPORARY, TRUE, -1.0);
|
|
}
|
|
}
|
|
|
|
// Hobgoblin Warsoul spell eater
|
|
if(nSaveRoll == 1 && GetRacialType(oTarget) == RACIAL_TYPE_HOBGOBLIN_WARSOUL)
|
|
{
|
|
//Apply the Aid VFX impact and effects
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_HOLY_AID), oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectAttackIncrease(2), oTarget, 6.0);
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectTemporaryHitpoints(5), oTarget, 60.0);
|
|
}
|
|
|
|
return nSaveRoll;
|
|
}
|
|
|
|
|
|
int PRCGetReflexAdjustedDamage(int nDamage, object oTarget, int nDC, int nSaveType = SAVING_THROW_TYPE_NONE, object oSaveVersus = OBJECT_SELF)
|
|
{
|
|
int nEvasion;
|
|
if(GetHasFeat(FEAT_IMPROVED_EVASION, oTarget))
|
|
nEvasion = 2;
|
|
else if(GetHasFeat(FEAT_EVASION, oTarget))
|
|
nEvasion = 1;
|
|
|
|
// Grants evasion against dragons, don't override if they've already gotit
|
|
object oWOL = GetItemPossessedBy(oTarget, "WOL_CrimsonRuination");
|
|
if(oWOL != GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget) && MyPRCGetRacialType(oSaveVersus) == RACIAL_TYPE_DRAGON && 1 > nEvasion)
|
|
nEvasion = 1;
|
|
|
|
// This ability removes evasion from the target
|
|
if(GetLocalInt(oTarget, "TrueConfoundingResistance"))
|
|
{
|
|
if(nEvasion)
|
|
nEvasion -= 1;
|
|
else
|
|
nDC += 2;
|
|
}
|
|
|
|
//Get saving throw result
|
|
int nSaveRoll = PRCMySavingThrow(SAVING_THROW_REFLEX, oTarget, nDC, nSaveType, oSaveVersus);
|
|
|
|
//save success
|
|
if(nSaveRoll)
|
|
nDamage = nEvasion ? 0 : nDamage / 2;
|
|
//save failed - check improved evasion
|
|
else if(nEvasion == 2)
|
|
nDamage = nDamage / 2;
|
|
|
|
return nDamage;
|
|
}
|
|
|
|
// function for internal use in GetCasterLvl
|
|
|
|
// caster level for arcane base classes (with practiced spellcaster feats)
|
|
int GetCasterLvlArcane(int iClassType, object oCaster)
|
|
{
|
|
if (GetPrimaryArcaneClass(oCaster) == iClassType)
|
|
return GetLevelByTypeArcane(oCaster);
|
|
|
|
int iTemp = GetLevelByClass(iClassType, oCaster);
|
|
iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp);
|
|
return iTemp;
|
|
}
|
|
|
|
// caster level for classes with half progression
|
|
int GetCasterLvlArcaneSemi(int iClassType, object oCaster)
|
|
{
|
|
if (GetPrimaryArcaneClass(oCaster) == iClassType)
|
|
return GetLevelByTypeArcane(oCaster);
|
|
|
|
int iTemp = GetLevelByClass(iClassType, oCaster) / 2;
|
|
iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp);
|
|
return iTemp;
|
|
}
|
|
|
|
// caster level for divine base classes (with practiced spellcaster feats)
|
|
int GetCasterLvlDivine(int iClassType, object oCaster)
|
|
{
|
|
if (GetPrimaryDivineClass(oCaster) == iClassType)
|
|
return GetLevelByTypeDivine(oCaster);
|
|
|
|
int iTemp = GetLevelByClass(iClassType, oCaster);
|
|
iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp);
|
|
return iTemp;
|
|
}
|
|
|
|
// caster level for classes with half progression
|
|
int GetCasterLvlDivineSemi(int iClassType, object oCaster)
|
|
{
|
|
if (GetPrimaryDivineClass(oCaster) == iClassType)
|
|
return GetLevelByTypeDivine(oCaster);
|
|
|
|
int iTemp = GetLevelByClass(iClassType, oCaster) / 2;
|
|
iTemp += PracticedSpellcasting(oCaster, iClassType, iTemp);
|
|
return iTemp;
|
|
}
|
|
|
|
int GetCasterLvl(int iTypeSpell, object oCaster = OBJECT_SELF)
|
|
{
|
|
switch (iTypeSpell)
|
|
{
|
|
case TYPE_ARCANE:
|
|
return GetLevelByTypeArcane(oCaster);
|
|
|
|
case TYPE_DIVINE:
|
|
return GetLevelByTypeDivine(oCaster);
|
|
|
|
case CLASS_TYPE_SORCERER:
|
|
{
|
|
if (GetPrimaryArcaneClass(oCaster) == CLASS_TYPE_SORCERER)
|
|
return GetLevelByTypeArcane(oCaster);
|
|
|
|
if(!GetLevelByClass(CLASS_TYPE_SORCERER, oCaster))
|
|
{
|
|
int iTemp;
|
|
int nRace = GetRacialType(oCaster);
|
|
|
|
//Aranea include shapechanger HD as sorc
|
|
if(nRace == RACIAL_TYPE_ARANEA)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_SHAPECHANGER, oCaster);
|
|
|
|
//Rakshasa include outsider HD as sorc
|
|
if(nRace == RACIAL_TYPE_RAKSHASA)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_OUTSIDER, oCaster);
|
|
|
|
//Drider include aberration HD as sorc
|
|
if(nRace == RACIAL_TYPE_DRIDER)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_ABERRATION, oCaster);
|
|
|
|
//Arkamoi + Redspawn include MH HD as sorc
|
|
if(nRace == RACIAL_TYPE_ARKAMOI)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster);
|
|
if(nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster);
|
|
if(nRace == RACIAL_TYPE_REDSPAWN_ARCANISS)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster)*3/4;
|
|
if(nRace == RACIAL_TYPE_MARRUTACT)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_MONSTROUS, oCaster)*6/7;
|
|
|
|
iTemp += PracticedSpellcasting(oCaster, CLASS_TYPE_SORCERER, iTemp);
|
|
iTemp += DraconicPower(oCaster);
|
|
return iTemp;
|
|
}
|
|
}
|
|
|
|
case CLASS_TYPE_BARD:
|
|
{
|
|
if (GetPrimaryArcaneClass(oCaster) == CLASS_TYPE_BARD)
|
|
return GetLevelByTypeArcane(oCaster);
|
|
|
|
if(!GetLevelByClass(CLASS_TYPE_BARD, oCaster))
|
|
{
|
|
int iTemp;
|
|
int nRace = GetRacialType(oCaster);
|
|
|
|
//Gloura include fey HD as bard
|
|
if(nRace == RACIAL_TYPE_GLOURA)
|
|
iTemp = GetLevelByClass(CLASS_TYPE_FEY, oCaster);
|
|
|
|
iTemp += PracticedSpellcasting(oCaster, CLASS_TYPE_BARD, iTemp);
|
|
iTemp += DraconicPower(oCaster);
|
|
return iTemp;
|
|
}
|
|
}
|
|
|
|
case CLASS_TYPE_ASSASSIN:
|
|
case CLASS_TYPE_CELEBRANT_SHARESS:
|
|
case CLASS_TYPE_BEGUILER:
|
|
case CLASS_TYPE_CULTIST_SHATTERED_PEAK:
|
|
case CLASS_TYPE_DREAD_NECROMANCER:
|
|
case CLASS_TYPE_DUSKBLADE:
|
|
case CLASS_TYPE_SHADOWLORD:
|
|
case CLASS_TYPE_SUEL_ARCHANAMACH:
|
|
case CLASS_TYPE_WARMAGE:
|
|
case CLASS_TYPE_WIZARD:
|
|
return GetCasterLvlArcane(iTypeSpell, oCaster);
|
|
|
|
case CLASS_TYPE_HEXBLADE:
|
|
return GetCasterLvlArcaneSemi(iTypeSpell, oCaster);
|
|
|
|
case CLASS_TYPE_ARCHIVIST:
|
|
case CLASS_TYPE_BLACKGUARD:
|
|
case CLASS_TYPE_BLIGHTER:
|
|
case CLASS_TYPE_CLERIC:
|
|
case CLASS_TYPE_DRUID:
|
|
case CLASS_TYPE_FAVOURED_SOUL:
|
|
case CLASS_TYPE_HEALER:
|
|
case CLASS_TYPE_KNIGHT_CHALICE:
|
|
case CLASS_TYPE_KNIGHT_MIDDLECIRCLE:
|
|
case CLASS_TYPE_NENTYAR_HUNTER:
|
|
case CLASS_TYPE_OCULAR:
|
|
case CLASS_TYPE_SHAMAN:
|
|
case CLASS_TYPE_SLAYER_OF_DOMIEL:
|
|
case CLASS_TYPE_SOLDIER_OF_LIGHT:
|
|
case CLASS_TYPE_UR_PRIEST:
|
|
case CLASS_TYPE_VASSAL:
|
|
return GetCasterLvlDivine(iTypeSpell, oCaster);
|
|
|
|
case CLASS_TYPE_PALADIN:
|
|
case CLASS_TYPE_RANGER:
|
|
case CLASS_TYPE_SOHEI:
|
|
return GetCasterLvlDivineSemi(iTypeSpell, oCaster);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
////////////////Begin Spellsword//////////////////
|
|
|
|
void SetAllAoEInts(int SpellID, object oAoE, int nBaseSaveDC, int SpecDispel = 0, int nCasterLevel = 0)
|
|
{
|
|
if(!GetLocalInt(oAoE, "X2_AoE_BaseSaveDC"))//DC should always be > 0
|
|
{
|
|
SetLocalInt(oAoE, "X2_AoE_SpellID", SpellID);
|
|
SetLocalInt(oAoE, "X2_AoE_BaseSaveDC", nBaseSaveDC);
|
|
if(SpecDispel) SetLocalInt(oAoE, "X2_AoE_SpecDispel", SpecDispel);
|
|
if(!nCasterLevel) nCasterLevel = PRCGetCasterLevel();
|
|
SetLocalInt(oAoE, "X2_AoE_Caster_Level", nCasterLevel);
|
|
if(GetHasFeat(FEAT_SHADOWWEAVE)) SetLocalInt(oAoE, "X2_AoE_Weave", TRUE);
|
|
}
|
|
}
|
|
|
|
//GetNextObjectInShape wrapper for changing the AOE of the channeled spells
|
|
object MyNextObjectInShape(int nShape,
|
|
float fSize, location lTarget,
|
|
int bLineOfSight = FALSE,
|
|
int nObjectFilter = OBJECT_TYPE_CREATURE,
|
|
vector vOrigin=[0.0, 0.0, 0.0])
|
|
{
|
|
object oCaster = OBJECT_SELF;
|
|
|
|
// War Wizard of Cormyr's Widen Spell ability
|
|
float fMulti = GetLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER);
|
|
//if(DEBUG) DoDebug("Original Spell Size: " + FloatToString(fSize)+"\n" + "Widened Multiplier: " + FloatToString(fMulti));
|
|
|
|
float fHFInfusion = GetLocalFloat(oCaster, "PRC_HF_Infusion_Wid");
|
|
if(fHFInfusion > 0.0f)
|
|
{
|
|
object oItem = PRCGetSpellCastItem(oCaster);
|
|
if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster)
|
|
{
|
|
//Hellfire Infusion - doesn't work on scrolls and potions
|
|
int nType = GetBaseItemType(oItem);
|
|
if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION
|
|
&& nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL)
|
|
{
|
|
fMulti = fHFInfusion;
|
|
DelayCommand(1.0, DeleteLocalFloat(oCaster, "PRC_HF_Infusion_Wid"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(fMulti > 0.0)
|
|
{
|
|
fSize *= fMulti;
|
|
if(DEBUG) DoDebug("New Spell Size: " + FloatToString(fSize));
|
|
}
|
|
|
|
if(GetLocalInt(oCaster, "spellswd_aoe") == 1)
|
|
{
|
|
return OBJECT_INVALID;
|
|
}
|
|
|
|
return GetNextObjectInShape(nShape,fSize,lTarget,bLineOfSight,nObjectFilter,vOrigin);
|
|
}
|
|
|
|
//GetFirstObjectInShape wrapper for changing the AOE of the channeled spells
|
|
object MyFirstObjectInShape(int nShape,
|
|
float fSize, location lTarget,
|
|
int bLineOfSight = FALSE,
|
|
int nObjectFilter = OBJECT_TYPE_CREATURE,
|
|
vector vOrigin=[0.0, 0.0, 0.0])
|
|
{
|
|
object oCaster = OBJECT_SELF;
|
|
|
|
//int on caster for the benefit of spellfire wielder resistance
|
|
// string sName = "IsAOE_" + IntToString(GetSpellId());
|
|
string sName = "IsAOE_" + IntToString(PRCGetSpellId(oCaster));
|
|
|
|
SetLocalInt(oCaster, sName, 1);
|
|
DelayCommand(0.1, DeleteLocalInt(oCaster, sName));
|
|
|
|
// War Wizard of Cormyr's Widen Spell ability
|
|
float fMulti = GetLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER);
|
|
//if(DEBUG) DoDebug("Original Spell Size: " + FloatToString(fSize)+"\n" + "Widened Multiplier: " + FloatToString(fMulti));
|
|
|
|
float fHFInfusion = GetLocalFloat(oCaster, "PRC_HF_Infusion_Wid");
|
|
if(fHFInfusion > 0.0f)
|
|
{
|
|
object oItem = PRCGetSpellCastItem(oCaster);
|
|
if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster)
|
|
{
|
|
//Hellfire Infusion - doesn't work on scrolls and potions
|
|
int nType = GetBaseItemType(oItem);
|
|
if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION
|
|
&& nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL)
|
|
{
|
|
fMulti = fHFInfusion;
|
|
DelayCommand(1.0, DeleteLocalFloat(oCaster, "PRC_HF_Infusion_Wid"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if(fMulti > 0.0)
|
|
{
|
|
fSize *= fMulti;
|
|
if(DEBUG) DoDebug("New Spell Size: " + FloatToString(fSize));
|
|
|
|
// This allows it to affect the entire casting
|
|
DelayCommand(1.0, DeleteLocalFloat(oCaster, PRC_SPELL_AREA_SIZE_MULTIPLIER));
|
|
}
|
|
|
|
if(GetLocalInt(oCaster, "spellswd_aoe") == 1)
|
|
{
|
|
return PRCGetSpellTargetObject(oCaster);
|
|
}
|
|
|
|
return GetFirstObjectInShape(nShape,fSize,lTarget,bLineOfSight,nObjectFilter,vOrigin);
|
|
}
|
|
|
|
|
|
//This checks if the spell is channeled and if there are multiple spells
|
|
//channeled, which one is it. Then it checks in either case if the spell
|
|
//has the metamagic feat the function gets and returns TRUE or FALSE accordingly
|
|
//Also used by the new spellbooks for the same purpose
|
|
/* replaced by wrapper for GetMetaMagicFeat instead
|
|
Not necessarily. This may still be a usefule level of abstraction - Ornedan
|
|
*/
|
|
int CheckMetaMagic(int nMeta,int nMMagic)
|
|
{
|
|
return nMeta & nMMagic;
|
|
}
|
|
|
|
int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE)
|
|
{
|
|
int nOverride = GetLocalInt(oCaster, PRC_METAMAGIC_OVERRIDE);
|
|
if(nOverride)
|
|
{
|
|
if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found override metamagic = "+IntToString(nOverride)+", original = "+IntToString(GetMetaMagicFeat()));
|
|
return nOverride;
|
|
}
|
|
|
|
object oItem = PRCGetSpellCastItem(oCaster);
|
|
|
|
// we assume that we are casting from an item, if the item is valid and the item belongs to oCaster
|
|
// however, we cannot be sure because of Bioware's inadequate implementation of GetSpellCastItem
|
|
if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster)
|
|
{
|
|
int nFeat;
|
|
|
|
//check item for metamagic
|
|
itemproperty ipTest = GetFirstItemProperty(oItem);
|
|
while(GetIsItemPropertyValid(ipTest))
|
|
{
|
|
if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_METAMAGIC)
|
|
{
|
|
int nSubType = GetItemPropertySubType(ipTest);
|
|
nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType));
|
|
if(nSubType == PRCGetSpellId(oCaster))
|
|
{
|
|
int nCostValue = GetItemPropertyCostTableValue(ipTest);
|
|
if(nCostValue == -1 && DEBUG)
|
|
DoDebug("Problem examining itemproperty");
|
|
switch(nCostValue)
|
|
{
|
|
//bitwise "addition" equivalent to nFeat = (nFeat | nSSFeat)
|
|
case 1: nFeat |= METAMAGIC_QUICKEN; break;
|
|
case 2: nFeat |= METAMAGIC_EMPOWER; break;
|
|
case 3: nFeat |= METAMAGIC_EXTEND; break;
|
|
case 4: nFeat |= METAMAGIC_MAXIMIZE; break;
|
|
case 5: nFeat |= METAMAGIC_SILENT; break;
|
|
case 6: nFeat |= METAMAGIC_STILL; break;
|
|
}
|
|
}
|
|
}
|
|
ipTest = GetNextItemProperty(oItem);
|
|
}
|
|
if (DEBUG) DoDebug("PRCGetMetaMagicFeat: item casting with item = "+GetName(oItem)+", found metamagic = "+IntToString(nFeat));
|
|
|
|
//Hellfire Infusion - doesn't work on scrolls and potions
|
|
int nType = GetBaseItemType(oItem);
|
|
if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION
|
|
&& nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL)
|
|
{
|
|
nFeat |= GetLocalInt(oCaster, "PRC_HF_Infusion");
|
|
if(bClearFeatFlags) DeleteLocalInt(oCaster, "PRC_HF_Infusion");
|
|
}
|
|
|
|
//apply metamagic adjustment (chanell spell)
|
|
nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT);
|
|
return nFeat;
|
|
}
|
|
|
|
if(GetLocalInt(oCaster, "PRC_SPELL_EVENT"))
|
|
{
|
|
if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found spell event metamagic = "+IntToString(GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC"))+", original = "+IntToString(GetMetaMagicFeat()));
|
|
return GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC");
|
|
}
|
|
|
|
int nFeat = GetMetaMagicFeat();
|
|
if(nFeat == METAMAGIC_ANY)
|
|
// work around for spontaneous casters (bard or sorcerer) having all metamagic turned on when using ActionCastSpell*
|
|
nFeat = METAMAGIC_NONE;
|
|
|
|
nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT);
|
|
|
|
int nClass = PRCGetLastSpellCastClass(oCaster);
|
|
// Suel Archanamach's Extend spells they cast on themselves.
|
|
// Only works for Suel Spells, and not any other caster type they might have
|
|
// Since this is a spellscript, it assumes OBJECT_SELF is the caster
|
|
if(nClass == CLASS_TYPE_SUEL_ARCHANAMACH
|
|
&& GetLevelByClass(CLASS_TYPE_SUEL_ARCHANAMACH) >= 3)
|
|
{
|
|
// Check that they cast on themselves
|
|
// if (oCaster == PRCGetSpellTargetObject())
|
|
if(oCaster == PRCGetSpellTargetObject(oCaster))
|
|
{
|
|
// Add extend to the metamagic feat using bitwise math
|
|
nFeat |= METAMAGIC_EXTEND;
|
|
}
|
|
}
|
|
//Code for the Abjurant Champion. Works similar to the Suel Archamamach but
|
|
//their extend only works on abjuration spells they cast.
|
|
if(GetHasFeat(FEAT_EXTENDED_ABJURATION, oCaster)
|
|
&& GetLevelByClass(CLASS_TYPE_ABJURANT_CHAMPION) >= 1)
|
|
{
|
|
int nSpellSchool = GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR");
|
|
// Check that they cast an abjuration spell
|
|
if(nSpellSchool == SPELL_SCHOOL_ABJURATION)
|
|
{
|
|
// Add extend to the metamagic feat using bitwise math
|
|
nFeat |= METAMAGIC_EXTEND;
|
|
if (DEBUG) FloatingTextStringOnCreature("Extended Abjuration Applied", oCaster, FALSE);
|
|
}
|
|
}
|
|
// Magical Contraction, Truenaming Utterance
|
|
if(GetHasSpellEffect(UTTER_MAGICAL_CONTRACTION_R, oCaster))
|
|
//(GetLocalInt(oCaster, "TrueMagicalContraction"))
|
|
{
|
|
nFeat |= METAMAGIC_EMPOWER;
|
|
}
|
|
// Sudden Metamagic
|
|
int nSuddenMeta = GetLocalInt(oCaster, "SuddenMeta");
|
|
if(nSuddenMeta)
|
|
{
|
|
nFeat |= nSuddenMeta;
|
|
if(bClearFeatFlags)
|
|
DeleteLocalInt(oCaster, "SuddenMeta");
|
|
}
|
|
|
|
int nDivMeta = GetLocalInt(oCaster, "DivineMeta");
|
|
if(nDivMeta)
|
|
{
|
|
if(GetIsDivineClass(nClass, oCaster))
|
|
{
|
|
nFeat |= nDivMeta;
|
|
if(bClearFeatFlags)
|
|
DeleteLocalInt(oCaster, "DivineMeta");
|
|
}
|
|
}
|
|
|
|
int nSpelldance = GetLocalInt(oCaster, "Spelldance");
|
|
if(nSpelldance)
|
|
{
|
|
nFeat |= nSpelldance;
|
|
if (DEBUG) FloatingTextStringOnCreature("Metamagic Spelldances applied", oCaster, FALSE);
|
|
}
|
|
|
|
int nSpellLevel = PRCGetSpellLevel(oCaster, PRCGetSpellId());
|
|
if (GetLocalInt(oCaster, "Aradros_Extend")/* && GetIsArcaneClass(nClass, oCaster)*/)
|
|
{
|
|
if (DEBUG) DoDebug("PRCGetMetaMagicFeat: GetIsArcaneClass is TRUE");
|
|
int nHD = GetHitDice(oCaster);
|
|
if (nHD >= 12 && 6 >= nSpellLevel)
|
|
{
|
|
nFeat |= METAMAGIC_EXTEND;
|
|
DeleteLocalInt(oCaster, "Aradros_Extend");
|
|
if (DEBUG) FloatingTextStringOnCreature("Aradros Extend Applied", oCaster, FALSE);
|
|
}
|
|
else if (3 >= nSpellLevel)
|
|
{
|
|
nFeat |= METAMAGIC_EXTEND;
|
|
DeleteLocalInt(oCaster, "Aradros_Extend");
|
|
if (DEBUG) FloatingTextStringOnCreature("Aradros Extend Applied", oCaster, FALSE);
|
|
}
|
|
}
|
|
|
|
if (DEBUG) DoDebug("PRCGetMetaMagicFeat: nSpellLevel " +IntToString(nSpellLevel)+", PRCGetSpellId " +IntToString(PRCGetSpellId())+", nClass " +IntToString(nClass));
|
|
if (DEBUG) DoDebug("PRCGetMetaMagicFeat: returning " +IntToString(nFeat));
|
|
return nFeat;
|
|
}
|
|
|
|
//Wrapper for The MaximizeOrEmpower function
|
|
int PRCMaximizeOrEmpower(int nDice, int nNumberOfDice, int nMeta, int nBonus = 0)
|
|
{
|
|
int i, nDamage;
|
|
for (i = 1; i <= nNumberOfDice; i++)
|
|
{
|
|
nDamage = nDamage + Random(nDice) + 1;
|
|
}
|
|
|
|
//Resolve metamagic
|
|
if(nMeta & METAMAGIC_MAXIMIZE)
|
|
nDamage = nDice * nNumberOfDice;
|
|
if(nMeta & METAMAGIC_EMPOWER)
|
|
nDamage = nDamage + nDamage / 2;
|
|
|
|
return nDamage + nBonus;
|
|
}
|
|
|
|
float PRCGetMetaMagicDuration(float fDuration, int nMeta = -1)
|
|
{
|
|
if(nMeta == -1) // no metamagic value was passed, so get it here
|
|
nMeta = PRCGetMetaMagicFeat();
|
|
|
|
if(nMeta & METAMAGIC_EXTEND)
|
|
fDuration *= 2;
|
|
|
|
return fDuration;
|
|
}
|
|
|
|
int PRCGetMetaMagicDamage(int nDamageType, int nDice, int nDieSize,
|
|
int nBonusPerDie = 0, int nBonus = 0, int nMetaMagic = -1)
|
|
{
|
|
// If the metamagic argument wasn't given get it.
|
|
if (-1 == nMetaMagic) nMetaMagic = PRCGetMetaMagicFeat();
|
|
|
|
// Roll the damage, applying metamagic.
|
|
int nDamage = PRCMaximizeOrEmpower(nDieSize, nDice, nMetaMagic, (nBonusPerDie * nDice) + nBonus);
|
|
return nDamage;
|
|
}
|
|
|
|
void PRCSetSchool(int nSchool = SPELL_SCHOOL_GENERAL)
|
|
{
|
|
// Remove the last value in case it is there and set the new value if it is NOT general.
|
|
if(SPELL_SCHOOL_GENERAL != nSchool)
|
|
SetLocalInt(OBJECT_SELF, "X2_L_LAST_SPELLSCHOOL_VAR", nSchool);
|
|
else
|
|
DeleteLocalInt(OBJECT_SELF, "X2_L_LAST_SPELLSCHOOL_VAR");
|
|
}
|
|
|
|
void PRCSignalSpellEvent(object oTarget, int bHostile = TRUE, int nSpellID = -1, object oCaster = OBJECT_SELF)
|
|
{
|
|
if (-1 == nSpellID) nSpellID = PRCGetSpellId();
|
|
|
|
//Fire cast spell at event for the specified target
|
|
SignalEvent(oTarget, EventSpellCastAt(oCaster, nSpellID, bHostile));
|
|
}
|
|
/*
|
|
void SPEvilShift(object oPC)
|
|
{
|
|
// Check for alignment shift switch being active
|
|
if(GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT))
|
|
{
|
|
// Amount of adjustment is equal to the square root of your distance from pure evil.
|
|
// In other words, the amount of shift is higher the farther you are from pure evil, with the
|
|
// extremes being 10 points of shift at pure good and 0 points of shift at pure evil.
|
|
AdjustAlignment(oPC, ALIGNMENT_EVIL, FloatToInt(sqrt(IntToFloat(GetGoodEvilValue(oPC)))), FALSE);
|
|
}
|
|
}
|
|
|
|
void SPGoodShift(object oPC)
|
|
{
|
|
// Check for alignment shift switch being active
|
|
if(GetPRCSwitch(PRC_SPELL_ALIGNMENT_SHIFT))
|
|
{
|
|
// Amount of adjustment is equal to the square root of your distance from pure good.
|
|
// In other words, the amount of shift is higher the farther you are from pure good, with the
|
|
// extremes being 10 points of shift at pure evil and 0 points of shift at pure good.
|
|
AdjustAlignment(oPC, ALIGNMENT_GOOD, FloatToInt(sqrt(IntToFloat(100 - GetGoodEvilValue(oPC)))), FALSE);
|
|
}
|
|
}*/
|
|
|
|
void DoCorruptionCost(object oPC, int nAbility, int nCost, int bDrain)
|
|
{
|
|
// Undead redirect all damage & drain to Charisma, sez http://www.wizards.com/dnd/files/BookVileFAQ12102002.zip
|
|
if(MyPRCGetRacialType(oPC) == RACIAL_TYPE_UNDEAD)
|
|
nAbility = ABILITY_CHARISMA;
|
|
|
|
//Exalted Raiment
|
|
if(GetHasSpellEffect(SPELL_EXALTED_RAIMENT, GetItemInSlot(INVENTORY_SLOT_CHEST, oPC)))
|
|
{
|
|
nCost -= 1;
|
|
|
|
if(nCost < 1)
|
|
nCost = 1;
|
|
}
|
|
|
|
if (GetHasFeat(FEAT_FAVORED_ZULKIRS, oPC)) nCost -= 1;
|
|
|
|
// Is it ability drain?
|
|
if(bDrain)
|
|
ApplyAbilityDamage(oPC, nAbility, nCost, DURATION_TYPE_PERMANENT, TRUE);
|
|
// Or damage
|
|
else
|
|
ApplyAbilityDamage(oPC, nAbility, nCost, DURATION_TYPE_TEMPORARY, TRUE, -1.0f);
|
|
}
|
|
|
|
void MultisummonPreSummon(object oPC = OBJECT_SELF, int bOverride = FALSE)
|
|
{
|
|
if(!GetPRCSwitch(PRC_MULTISUMMON) && !bOverride)
|
|
return;
|
|
int i=1;
|
|
int nCount = GetPRCSwitch(PRC_MULTISUMMON);
|
|
if(bOverride)
|
|
nCount = bOverride;
|
|
if(nCount < 0
|
|
|| nCount == 1)
|
|
nCount = 99;
|
|
if(nCount > 99)
|
|
nCount = 99;
|
|
object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
while(GetIsObjectValid(oSummon) && !GetLocalInt(oSummon, "RFSummonedElemental") && i < nCount)
|
|
{
|
|
AssignCommand(oSummon, SetIsDestroyable(FALSE, FALSE, FALSE));
|
|
AssignCommand(oSummon, DelayCommand(0.3, SetIsDestroyable(TRUE, FALSE, FALSE)));
|
|
i++;
|
|
oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
}
|
|
}
|
|
|
|
|
|
//This function returns 1 only if the object oTarget is the object
|
|
//the weapon hit when it channeled the spell sSpell or if there is no
|
|
//channeling at all
|
|
int ChannelChecker(string sSpell, object oTarget)
|
|
{
|
|
int nSpell = GetLocalInt(GetAreaOfEffectCreator(), sSpell+"channeled");
|
|
int nTarget = GetLocalInt(oTarget, sSpell+"target");
|
|
if(nSpell == 1 && nTarget == 1)
|
|
{
|
|
return 1;
|
|
}
|
|
else if(nSpell != 1 && nTarget != 1)
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//If a spell is being channeled, we store its target and its name
|
|
void StoreSpellVariables(string sString,int nDuration)
|
|
{
|
|
object oCaster = OBJECT_SELF;
|
|
object oTarget = GetSpellTargetObject(); //using prc version could cause problems
|
|
|
|
if(GetLocalInt(oCaster,"spellswd_aoe") == 1)
|
|
{
|
|
SetLocalInt(oCaster, sString+"channeled",1);
|
|
SetLocalInt(oTarget, sString+"target",1);
|
|
}
|
|
DelayCommand(RoundsToSeconds(nDuration),DeleteLocalInt(oTarget, sString+"target"));
|
|
DelayCommand(RoundsToSeconds(nDuration),DeleteLocalInt(oCaster, sString+"channeled"));
|
|
}
|
|
|
|
effect ChannelingVisual()
|
|
{
|
|
return EffectVisualEffect(VFX_DUR_SPELLTURNING);
|
|
}
|
|
|
|
////////////////End Spellsword//////////////////
|
|
|
|
|
|
int GetHasMettle(object oTarget, int nSavingThrow = SAVING_THROW_WILL)
|
|
{
|
|
if(GetLevelByClass(CLASS_TYPE_HEXBLADE, oTarget) > 2) return TRUE;
|
|
if(GetLevelByClass(CLASS_TYPE_SOHEI, oTarget) > 8) return TRUE;
|
|
if(GetLevelByClass(CLASS_TYPE_CRUSADER, oTarget) > 12) return TRUE;
|
|
if(GetLocalInt(oTarget, "FactotumMettle")) return TRUE;
|
|
|
|
if(nSavingThrow = SAVING_THROW_WILL)
|
|
{
|
|
// Iron Mind's ability only functions in Heavy Armour
|
|
if(GetLevelByClass(CLASS_TYPE_IRONMIND, oTarget) > 4
|
|
&& GetBaseAC(GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget)) > 5)
|
|
return TRUE;
|
|
// Fill out the line below to add another class with Will mettle
|
|
// if (GetLevelByClass(CLASS_TYPE_X, oTarget) >= X) return TRUE;
|
|
}
|
|
/*else if(nSavingThrow = SAVING_THROW_FORT)
|
|
{
|
|
// Add Classes with Fort mettle here
|
|
// if (GetLevelByClass(CLASS_TYPE_X, oTarget) >= X) return TRUE;
|
|
}*/
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void DoCommandSpell(object oCaster, object oTarget, int nSpellId, int nDuration, int nCaster)
|
|
{
|
|
if(DEBUG) DoDebug("DoCommandSpell: SpellId: " + IntToString(nSpellId));
|
|
if(DEBUG) DoDebug("Command Spell: Begin");
|
|
if(nSpellId == SPELL_COMMAND_APPROACH
|
|
|| nSpellId == SPELL_GREATER_COMMAND_APPROACH
|
|
|| nSpellId == 18359 //MYST_VOICE_SHADOW_APPROACH
|
|
|| nSpellId == SPELL_DOA_COMMAND_APPROACH
|
|
|| nSpellId == SPELL_DOA_GREATER_COMMAND_APPROACH)
|
|
{
|
|
// Force the target to approach the caster
|
|
if(DEBUG) DoDebug("Command Spell: Approach");
|
|
AssignCommand(oTarget, ClearAllActions(TRUE));
|
|
AssignCommand(oTarget, ActionForceMoveToObject(oCaster, TRUE));
|
|
}
|
|
// Creatures that can't be disarmed ignore this
|
|
else if(nSpellId == SPELL_COMMAND_DROP
|
|
|| nSpellId == SPELL_GREATER_COMMAND_DROP
|
|
|| nSpellId == 18360 //MYST_VOICE_SHADOW_DROP
|
|
|| nSpellId == SPELL_DOA_COMMAND_DROP
|
|
|| nSpellId == SPELL_DOA_GREATER_COMMAND_DROP)
|
|
{
|
|
if(GetIsCreatureDisarmable(oTarget) && !GetPRCSwitch(PRC_PNP_DISARM))
|
|
{
|
|
// Force the target to drop what its holding
|
|
if(DEBUG) DoDebug("Command Spell: Drop");
|
|
AssignCommand(oTarget, ClearAllActions(TRUE));
|
|
object oTargetWep = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
|
|
SetDroppableFlag(oTargetWep, TRUE);
|
|
SetStolenFlag(oTargetWep, FALSE);
|
|
AssignCommand(oTarget, ActionPutDownItem(oTargetWep));
|
|
/*oTargetWep = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget);
|
|
SetDroppableFlag(oTargetWep, TRUE);
|
|
SetStolenFlag(oTargetWep, FALSE);
|
|
AssignCommand(oTarget, ActionPutDownItem(oTargetWep)); */
|
|
}
|
|
else
|
|
{
|
|
FloatingTextStringOnCreature(GetName(oTarget) + " is not disarmable.", oCaster, FALSE);
|
|
}
|
|
}
|
|
else if(nSpellId == SPELL_COMMAND_FALL
|
|
|| nSpellId == SPELL_GREATER_COMMAND_FALL
|
|
|| nSpellId == 18361 //MYST_VOICE_SHADOW_FALL
|
|
|| nSpellId == SPELL_DOA_COMMAND_FALL
|
|
|| nSpellId == SPELL_DOA_GREATER_COMMAND_FALL)
|
|
{
|
|
// Force the target to fall down
|
|
if(DEBUG) DoDebug("Command Spell: Fall");
|
|
AssignCommand(oTarget, ClearAllActions(TRUE));
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oTarget, RoundsToSeconds(nDuration),TRUE,-1,nCaster);
|
|
}
|
|
else if(nSpellId == SPELL_COMMAND_FLEE
|
|
|| nSpellId == SPELL_GREATER_COMMAND_FLEE
|
|
|| nSpellId == 18362 //MYST_VOICE_SHADOW_FLEE
|
|
|| nSpellId == SPELL_DOA_COMMAND_FLEE
|
|
|| nSpellId == SPELL_DOA_GREATER_COMMAND_FLEE)
|
|
{
|
|
// Force the target to flee the caster
|
|
if(DEBUG) DoDebug("Command Spell: Flee");
|
|
AssignCommand(oTarget, ClearAllActions(TRUE));
|
|
AssignCommand(oTarget, ActionMoveAwayFromObject(oCaster, TRUE));
|
|
}
|
|
else if(nSpellId == SPELL_COMMAND_HALT
|
|
|| nSpellId == SPELL_GREATER_COMMAND_HALT
|
|
|| nSpellId == 18363 //MYST_VOICE_SHADOW_HALT
|
|
|| nSpellId == SPELL_DOA_COMMAND_HALT
|
|
|| nSpellId == SPELL_DOA_GREATER_COMMAND_HALT)
|
|
{
|
|
// Force the target to stand still
|
|
if(DEBUG) DoDebug("Command Spell: Stand");
|
|
AssignCommand(oTarget, ClearAllActions(TRUE));
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectCutsceneParalyze(), oTarget, RoundsToSeconds(nDuration),TRUE,-1,nCaster);
|
|
}
|
|
else // Catch errors here
|
|
{
|
|
FloatingTextStringOnCreature("sp_command/sp_greatcommand: Error, Unknown SpellId", oCaster, FALSE);
|
|
}
|
|
if(DEBUG) DoDebug("Command Spell: End");
|
|
}
|
|
|
|
void SetIncorporeal(object oTarget, float fDuration, int nSuperOrEx, int nPermanent = FALSE)
|
|
{
|
|
if (!GetIsObjectValid(oTarget))
|
|
return; //No point working
|
|
|
|
// Immune to non-magical weapons, ignore physical objects
|
|
effect eIncorporeal = EffectLinkEffects(EffectDamageReduction(100, DAMAGE_POWER_PLUS_ONE, 0), EffectCutsceneGhost());
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_BLUDGEONING, 50)); // 50% chance of magical weapons not working. Done as 50% Damage Immunity
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_SLASHING, 50));
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectDamageImmunityIncrease(DAMAGE_TYPE_PIERCING, 50));
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectMissChance(50, MISS_CHANCE_TYPE_VS_MELEE)); // 50% melee miss chance to hit non-incorporeal targets. That's going to be everything
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectSkillIncrease(SKILL_MOVE_SILENTLY, 99)); // Cannot be heard
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectVisualEffect(VFX_DUR_ETHEREAL_VISAGE)); // Visual effect
|
|
|
|
// No Strength Score, use Dex for melee attacks too
|
|
int nStr = GetAbilityScore(oTarget, ABILITY_STRENGTH);
|
|
int nDex = GetAbilityModifier(ABILITY_DEXTERITY, oTarget);
|
|
int nPen;
|
|
|
|
if (nStr>10)
|
|
{
|
|
nPen = nStr - 10;
|
|
eIncorporeal = EffectLinkEffects(eIncorporeal, EffectAbilityDecrease(ABILITY_STRENGTH, nPen)); // Reduce Strength to 10
|
|
|
|
object oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
|
|
if (GetIsObjectValid(oWeapon) && IPGetIsMeleeWeapon(oWeapon))
|
|
{
|
|
IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus
|
|
}
|
|
oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget);
|
|
if (GetIsObjectValid(oWeapon) && IPGetIsMeleeWeapon(oWeapon))
|
|
{
|
|
IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus
|
|
}
|
|
oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_B, oTarget);
|
|
if (GetIsObjectValid(oWeapon)) // We know these are melee weapons
|
|
{
|
|
IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus
|
|
}
|
|
oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_L, oTarget);
|
|
if (GetIsObjectValid(oWeapon)) // We know these are melee weapons
|
|
{
|
|
IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus
|
|
}
|
|
oWeapon = GetItemInSlot(INVENTORY_SLOT_CWEAPON_R, oTarget);
|
|
if (GetIsObjectValid(oWeapon)) // We know these are melee weapons
|
|
{
|
|
IPSafeAddItemProperty(oWeapon, ItemPropertyAttackBonus(nDex), fDuration); //Dex has to be done as a melee weapon bonus
|
|
}
|
|
}
|
|
|
|
SetLocalInt(oTarget, "Incorporeal", TRUE);
|
|
if (!nPermanent) DelayCommand(fDuration, DeleteLocalInt(oTarget, "Incorporeal"));
|
|
|
|
if (nSuperOrEx == 1)
|
|
eIncorporeal = SupernaturalEffect(eIncorporeal);
|
|
else if (nSuperOrEx == 2)
|
|
eIncorporeal = ExtraordinaryEffect(eIncorporeal);
|
|
|
|
if (nPermanent)
|
|
SPApplyEffectToObject(DURATION_TYPE_PERMANENT, eIncorporeal, oTarget);
|
|
else
|
|
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eIncorporeal, oTarget, fDuration, FALSE, -1, -1);
|
|
}
|
|
|
|
int GetIsIncorporeal(object oTarget)
|
|
{
|
|
//Check for local int
|
|
if(GetPersistantLocalInt(oTarget, "Is_Incorporeal"))
|
|
return TRUE;
|
|
|
|
//Check for local int
|
|
if(GetLocalInt(oTarget, "Incorporeal"))
|
|
return TRUE;
|
|
|
|
//check for feat
|
|
if(GetHasFeat(FEAT_INCORPOREAL, oTarget))
|
|
return TRUE;
|
|
|
|
//base it on appearance
|
|
int nAppear = GetAppearanceType(oTarget);
|
|
if(nAppear == APPEARANCE_TYPE_ALLIP
|
|
|| nAppear == APPEARANCE_TYPE_SHADOW
|
|
|| nAppear == APPEARANCE_TYPE_SHADOW_FIEND
|
|
|| nAppear == APPEARANCE_TYPE_SPECTRE
|
|
|| nAppear == APPEARANCE_TYPE_WRAITH)
|
|
return TRUE;
|
|
|
|
//Return value
|
|
return FALSE;
|
|
}
|
|
|
|
int GetIsEthereal(object oTarget)
|
|
{
|
|
return GetPersistantLocalInt(oTarget, "Is_Ethereal")
|
|
|| GetHasFeat(FEAT_ETHEREAL, oTarget);
|
|
}
|
|
|
|
int PRCGetIsAliveCreature(object oTarget)
|
|
{
|
|
int bAlive = TRUE;
|
|
// non-creatures aren't alive
|
|
if (GetObjectType(oTarget) != OBJECT_TYPE_CREATURE)
|
|
return FALSE; // night of the living waypoints :p
|
|
|
|
int nType = MyPRCGetRacialType(oTarget);
|
|
|
|
//Non-living races, excluding warforged
|
|
if(nType == RACIAL_TYPE_UNDEAD ||
|
|
(nType == RACIAL_TYPE_CONSTRUCT && !GetIsWarforged(oTarget))) bAlive = FALSE;
|
|
|
|
//If they're dead :P
|
|
if(GetIsDead(oTarget)) bAlive = FALSE;
|
|
|
|
//return
|
|
return bAlive;
|
|
}
|
|
|
|
int GetControlledUndeadTotalHD(object oPC = OBJECT_SELF)
|
|
{
|
|
int nTotalHD;
|
|
int i = 1;
|
|
object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
while(GetIsObjectValid(oSummonTest))
|
|
{
|
|
if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_UNDEAD)
|
|
nTotalHD += GetHitDice(oSummonTest);
|
|
if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC);
|
|
i++;
|
|
oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
}
|
|
return nTotalHD;
|
|
}
|
|
|
|
|
|
int GetControlledFiendTotalHD(object oPC = OBJECT_SELF)
|
|
{
|
|
int nTotalHD;
|
|
int i = 1;
|
|
object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
while(GetIsObjectValid(oSummonTest))
|
|
{
|
|
if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_OUTSIDER
|
|
&& GetAlignmentGoodEvil(oSummonTest) == ALIGNMENT_EVIL)
|
|
nTotalHD += GetHitDice(oSummonTest);
|
|
if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC);
|
|
i++;
|
|
oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
}
|
|
return nTotalHD;
|
|
}
|
|
|
|
int GetControlledCelestialTotalHD(object oPC = OBJECT_SELF)
|
|
{
|
|
int nTotalHD;
|
|
int i = 1;
|
|
object oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
while(GetIsObjectValid(oSummonTest))
|
|
{
|
|
if(MyPRCGetRacialType(oSummonTest) == RACIAL_TYPE_OUTSIDER
|
|
&& GetAlignmentGoodEvil(oSummonTest) == ALIGNMENT_GOOD)
|
|
nTotalHD += GetHitDice(oSummonTest);
|
|
if(DEBUG)FloatingTextStringOnCreature(GetName(oSummonTest)+" is summon number "+IntToString(i), oPC);
|
|
i++;
|
|
oSummonTest = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC, i);
|
|
}
|
|
return nTotalHD;
|
|
}
|
|
|
|
|
|
|
|
// wrapper for DecrementRemainingSpellUses, works for newspellbook 'fake' spells too
|
|
// should also find and decrement metamagics for newspellbooks
|
|
void PRCDecrementRemainingSpellUses(object oCreature, int nSpell)
|
|
{
|
|
if (!UseNewSpellBook(oCreature) && GetHasSpell(nSpell, oCreature))
|
|
{
|
|
DecrementRemainingSpellUses(oCreature, nSpell);
|
|
return;
|
|
}
|
|
|
|
int nClass, nSpellbookID, nCount, nMeta, i, j;
|
|
int nSpellbookType, nSpellLevel;
|
|
string sFile, sFeat;
|
|
for(i = 1; i <= 8; i++)
|
|
{
|
|
nClass = GetClassByPosition(i, oCreature);
|
|
sFile = GetFileForClass(nClass);
|
|
nSpellbookType = GetSpellbookTypeForClass(nClass);
|
|
nSpellbookID = RealSpellToSpellbookID(nClass, nSpell);
|
|
nMeta = RealSpellToSpellbookIDCount(nClass, nSpell);
|
|
if (nSpellbookID != -1)
|
|
{ //non-spellbook classes should return -1
|
|
for(j = nSpellbookID; j <= nSpellbookID + nMeta; j++)
|
|
{
|
|
sFeat = Get2DACache(sFile, "ReqFeat", j);
|
|
if(sFeat != "")
|
|
{
|
|
if(!GetHasFeat(StringToInt(sFeat), oCreature))
|
|
continue;
|
|
}
|
|
nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j));
|
|
|
|
if(nSpellbookType == SPELLBOOK_TYPE_PREPARED)
|
|
{
|
|
nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j);
|
|
if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount));
|
|
if(nCount > 0)
|
|
{
|
|
persistant_array_set_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j, nCount - 1);
|
|
return;
|
|
}
|
|
}
|
|
else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
{
|
|
nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel);
|
|
if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount));
|
|
if(nCount > 0)
|
|
{
|
|
persistant_array_set_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel, nCount - 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(DEBUG) DoDebug("PRCDecrementRemainingSpellUses: Spell " + IntToString(nSpell) + " not found");
|
|
}
|
|
|
|
//
|
|
// This function determines if spell damage is elemental
|
|
//
|
|
int IsSpellDamageElemental(int nDamageType)
|
|
{
|
|
return nDamageType & 2480;// 2480 = (DAMAGE_TYPE_ACID | DAMAGE_TYPE_COLD | DAMAGE_TYPE_ELECTRICAL | DAMAGE_TYPE_FIRE | DAMAGE_TYPE_SONIC)
|
|
}
|
|
|
|
//
|
|
// This function converts spell damage into the correct type
|
|
// TODO: Change the name to consistent (large churn project).
|
|
//
|
|
int ChangedElementalDamage(object oCaster, int nDamageType)
|
|
{
|
|
|
|
// None of the stuff here works when items are involved
|
|
if (GetIsObjectValid(PRCGetSpellCastItem())) return nDamageType;
|
|
|
|
int nNewType;
|
|
|
|
//eldritch spellweave
|
|
if(GetIsObjectValid(GetLocalObject(oCaster, "SPELLWEAVE_TARGET")))
|
|
{
|
|
int nEssence = GetLocalInt(oCaster, "BlastEssence");
|
|
|
|
if(nEssence == INVOKE_BRIMSTONE_BLAST)
|
|
nNewType = DAMAGE_TYPE_FIRE;
|
|
|
|
else if(nEssence == INVOKE_HELLRIME_BLAST)
|
|
nNewType = DAMAGE_TYPE_COLD;
|
|
|
|
else if(nEssence == INVOKE_UTTERDARK_BLAST)
|
|
nNewType = DAMAGE_TYPE_NEGATIVE;
|
|
|
|
else if(nEssence == INVOKE_VITRIOLIC_BLAST)
|
|
nNewType = DAMAGE_TYPE_ACID;
|
|
|
|
//save new type for other functions
|
|
if(nNewType)
|
|
SetLocalInt(oCaster, "archmage_mastery_elements", nNewType);
|
|
}
|
|
else
|
|
// Check if an override is set
|
|
nNewType = GetLocalInt(oCaster, "archmage_mastery_elements");
|
|
|
|
// If so, check if the spell qualifies for a change
|
|
if (!nNewType || !IsSpellDamageElemental(nDamageType))
|
|
nNewType = nDamageType;
|
|
|
|
return nNewType;
|
|
}
|
|
|
|
//used in scripts after ChangedElementalDamage() to determine saving throw type
|
|
int ChangedSaveType(int nDamageType)
|
|
{
|
|
switch(nDamageType)
|
|
{
|
|
case DAMAGE_TYPE_ACID: return SAVING_THROW_TYPE_ACID;
|
|
case DAMAGE_TYPE_COLD: return SAVING_THROW_TYPE_COLD;
|
|
case DAMAGE_TYPE_ELECTRICAL: return SAVING_THROW_TYPE_ELECTRICITY;
|
|
case DAMAGE_TYPE_FIRE: return SAVING_THROW_TYPE_FIRE;
|
|
case DAMAGE_TYPE_SONIC: return SAVING_THROW_TYPE_SONIC;
|
|
case DAMAGE_TYPE_DIVINE: return SAVING_THROW_TYPE_DIVINE;
|
|
case DAMAGE_TYPE_NEGATIVE: return SAVING_THROW_TYPE_NEGATIVE;
|
|
case DAMAGE_TYPE_POSITIVE: return SAVING_THROW_TYPE_POSITIVE;
|
|
}
|
|
return SAVING_THROW_TYPE_NONE;//if it ever gets here, than the function was used incorrectly
|
|
}
|
|
|
|
// this is possibly used in variations elsewhere
|
|
int PRCGetElementalDamageType(int nDamageType, object oCaster = OBJECT_SELF)
|
|
{
|
|
switch(nDamageType)
|
|
{
|
|
case DAMAGE_TYPE_ACID:
|
|
case DAMAGE_TYPE_COLD:
|
|
case DAMAGE_TYPE_ELECTRICAL:
|
|
case DAMAGE_TYPE_FIRE:
|
|
case DAMAGE_TYPE_SONIC:
|
|
nDamageType = ChangedElementalDamage(oCaster, nDamageType);
|
|
}
|
|
return nDamageType;
|
|
}
|
|
|
|
int GetHasBaneMagic(int nRace)
|
|
{
|
|
switch(nRace)
|
|
{
|
|
case RACIAL_TYPE_ABERRATION: return GetHasFeat(FEAT_BANE_MAGIC_ABERRATION);
|
|
case RACIAL_TYPE_ANIMAL: return GetHasFeat(FEAT_BANE_MAGIC_ANIMAL);
|
|
case RACIAL_TYPE_BEAST: return GetHasFeat(FEAT_BANE_MAGIC_BEAST);
|
|
case RACIAL_TYPE_CONSTRUCT: return GetHasFeat(FEAT_BANE_MAGIC_CONSTRUCT);
|
|
case RACIAL_TYPE_DRAGON: return GetHasFeat(FEAT_BANE_MAGIC_DRAGON);
|
|
case RACIAL_TYPE_DWARF: return GetHasFeat(FEAT_BANE_MAGIC_DWARF);
|
|
case RACIAL_TYPE_ELEMENTAL: return GetHasFeat(FEAT_BANE_MAGIC_ELEMENTAL);
|
|
case RACIAL_TYPE_ELF: return GetHasFeat(FEAT_BANE_MAGIC_ELF);
|
|
case RACIAL_TYPE_FEY: return GetHasFeat(FEAT_BANE_MAGIC_FEY);
|
|
case RACIAL_TYPE_GIANT: return GetHasFeat(FEAT_BANE_MAGIC_GIANT);
|
|
case RACIAL_TYPE_GNOME: return GetHasFeat(FEAT_BANE_MAGIC_GNOME);
|
|
case RACIAL_TYPE_HALFELF: return GetHasFeat(FEAT_BANE_MAGIC_HALFELF);
|
|
case RACIAL_TYPE_HALFLING: return GetHasFeat(FEAT_BANE_MAGIC_HALFLING);
|
|
case RACIAL_TYPE_HALFORC: return GetHasFeat(FEAT_BANE_MAGIC_HALFORC);
|
|
case RACIAL_TYPE_HUMAN: return GetHasFeat(FEAT_BANE_MAGIC_HUMAN);
|
|
case RACIAL_TYPE_HUMANOID_GOBLINOID: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_GOBLINOID);
|
|
case RACIAL_TYPE_HUMANOID_MONSTROUS: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_MONSTROUS);
|
|
case RACIAL_TYPE_HUMANOID_ORC: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_ORC);
|
|
case RACIAL_TYPE_HUMANOID_REPTILIAN: return GetHasFeat(FEAT_BANE_MAGIC_HUMANOID_REPTILIAN);
|
|
case RACIAL_TYPE_MAGICAL_BEAST: return GetHasFeat(FEAT_BANE_MAGIC_MAGICAL_BEAST);
|
|
case RACIAL_TYPE_OUTSIDER: return GetHasFeat(FEAT_BANE_MAGIC_OUTSIDER);
|
|
case RACIAL_TYPE_SHAPECHANGER: return GetHasFeat(FEAT_BANE_MAGIC_SHAPECHANGER);
|
|
case RACIAL_TYPE_UNDEAD: return GetHasFeat(FEAT_BANE_MAGIC_UNDEAD);
|
|
case RACIAL_TYPE_VERMIN: return GetHasFeat(FEAT_BANE_MAGIC_VERMIN);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void DoPiercingCold(object oCaster, object oTarget, int nDamageAmount, int nCurrentHP)
|
|
{
|
|
// Get change in HP from spell damage being applied
|
|
int nTest = nCurrentHP - GetCurrentHitPoints(oTarget);
|
|
// If there's no damage resistance, nTest and nDamageAmount should be equal
|
|
if (nDamageAmount > nTest)
|
|
{
|
|
// Apply the difference to ignore resist
|
|
effect eDam = EffectDamage(nDamageAmount - nTest, DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget);
|
|
effect eVis = EffectVisualEffect(VFX_IMP_FROST_L);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oTarget);
|
|
//FloatingTextStringOnCreature("Piercing Cold Triggered", oCaster, FALSE);
|
|
}
|
|
}
|
|
|
|
void BreakDR(object oCaster, object oTarget, int nDamageAmount, int nCurrentHP)
|
|
{
|
|
// Get change in HP from spell damage being applied
|
|
int nTest = nCurrentHP - GetCurrentHitPoints(oTarget);
|
|
// If there's no damage resistance, nTest and nDamageAmount should be equal
|
|
if (nDamageAmount > nTest)
|
|
{
|
|
// Apply the difference to ignore resist
|
|
effect eDam = EffectDamage(nDamageAmount - nTest, DAMAGE_TYPE_POSITIVE, DAMAGE_POWER_ENERGY);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oTarget);
|
|
}
|
|
}
|
|
|
|
effect PRCEffectDamage(object oTarget, int nDamageAmount, int nDamageType=DAMAGE_TYPE_MAGICAL, int nDamagePower=DAMAGE_POWER_NORMAL, int nMetaMagic=METAMAGIC_NONE)
|
|
{
|
|
object oCaster = OBJECT_SELF;
|
|
|
|
// Incorporeal creatures have a 50% chance of not being hurt by damage other than Magical/Divine/Negative
|
|
if (GetLocalInt(oTarget, "Incorporeal") && nDamageType != DAMAGE_TYPE_MAGICAL && nDamageType != DAMAGE_TYPE_NEGATIVE && nDamageType != DAMAGE_TYPE_DIVINE)
|
|
{
|
|
if (d2() == 1) // 50% chance
|
|
{
|
|
if (GetIsPC(oTarget))
|
|
FloatingTextStringOnCreature("Spell missed due to Incorporeality", oTarget, FALSE);
|
|
effect eEffect;
|
|
return eEffect; // Null return
|
|
}
|
|
}
|
|
|
|
// None of the stuff here works when items are involved
|
|
if (!GetIsObjectValid(PRCGetSpellCastItem()))
|
|
{
|
|
if(PRCGetLastSpellCastClass(oCaster) == CLASS_TYPE_WARMAGE && !GetLocalInt(oTarget, "WarmageEdgeDelay"))
|
|
{
|
|
// Warmage Edge
|
|
nDamageAmount += GetAbilityModifier(ABILITY_INTELLIGENCE);
|
|
if(GetHasFeat(FEAT_TYPE_EXTRA_EDGE))
|
|
{
|
|
// Extra Edge feat.
|
|
nDamageAmount += (GetLevelByClass(CLASS_TYPE_WARMAGE) / 4) + 1;
|
|
}
|
|
SetLocalInt(oTarget, "WarmageEdgeDelay", TRUE);
|
|
DelayCommand(0.25, DeleteLocalInt(oTarget, "WarmageEdgeDelay"));
|
|
}
|
|
|
|
if(GetHasSpellEffect(MELD_ARCANE_FOCUS, oCaster) && !GetLocalInt(oTarget, "ArcaneFocusDelay"))
|
|
{
|
|
nDamageAmount += 1 + GetEssentiaInvested(oCaster, MELD_ARCANE_FOCUS);
|
|
SetLocalInt(oTarget, "ArcaneFocusDelay", TRUE);
|
|
DelayCommand(0.25, DeleteLocalInt(oTarget, "ArcaneFocusDelay"));
|
|
if (DEBUG) DoDebug("ArcaneFocus Damage Applied");
|
|
}
|
|
|
|
// Thrall of Grazzt damage
|
|
nDamageAmount += SpellBetrayalDamage(oTarget, oCaster);
|
|
|
|
int nRace = MyPRCGetRacialType(oTarget);
|
|
|
|
//Bane Magic
|
|
if(GetHasBaneMagic(nRace))
|
|
nDamageAmount += d6(2);
|
|
|
|
//Eldritch Spellweave
|
|
if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))
|
|
{
|
|
//Bane blast essence is active ;)
|
|
if(nRace == ((GetLocalInt(oCaster, "EssenceData") >>> 16) & 0xFF) - 1)
|
|
{
|
|
DeleteLocalObject(oCaster, "SPELLWEAVE_TARGET");
|
|
nDamageAmount += d6(2);
|
|
}
|
|
}
|
|
|
|
// Piercing Evocation
|
|
if(GetHasFeat(FEAT_PIERCING_EVOCATION) && GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR") == SPELL_SCHOOL_EVOCATION)
|
|
{
|
|
// Elemental damage only
|
|
if(IsSpellDamageElemental(nDamageType))
|
|
{
|
|
// Damage magical, max 10 to magical
|
|
if(nDamageAmount > 10)
|
|
{
|
|
nDamageAmount -= 10;
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(10), oTarget);
|
|
}
|
|
else
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamageAmount), oTarget);
|
|
effect eEffect;
|
|
return eEffect; // Null return
|
|
}
|
|
}
|
|
}
|
|
|
|
// This is done here so it affects all spells
|
|
if(GetLocalInt(oCaster, "Diabolism"))
|
|
{
|
|
//FloatingTextStringOnCreature("Diabolism is active", oCaster, FALSE);
|
|
int iDiabolistLevel = GetLevelByClass(CLASS_TYPE_DIABOLIST, oCaster);
|
|
DelayCommand(3.0, DeleteLocalInt(oCaster, "Diabolism"));
|
|
|
|
if(iDiabolistLevel)
|
|
{
|
|
int nDice = (iDiabolistLevel + 5) / 5;
|
|
int nDamage = d6(nDice);
|
|
int nDamageType = DAMAGE_TYPE_DIVINE;
|
|
|
|
if(GetLocalInt(oCaster, "VileDiabolism"))
|
|
{
|
|
//FloatingTextStringOnCreature("Vile Diabolism is active", oCaster, FALSE);
|
|
nDamage /= 2;
|
|
nDamageType = DAMAGE_TYPE_POSITIVE;
|
|
DeleteLocalInt(oCaster, "VileDiabolism");
|
|
}
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectDamage(nDamage, nDamageType), oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_LOS_EVIL_10), oTarget);
|
|
}
|
|
}
|
|
|
|
//:: Get the current spell being cast
|
|
int nCurrentSpell = PRCGetSpellId();
|
|
|
|
//:: Skip Reserve Feat spellIDs
|
|
if (nCurrentSpell < 19359 || nCurrentSpell > 19396)
|
|
{
|
|
//:: Piercing Cold for the Frost Mage
|
|
if (GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) >= 4 && nDamageType == DAMAGE_TYPE_COLD)
|
|
{
|
|
int nCurrentHP = GetCurrentHitPoints(oTarget);
|
|
DelayCommand(0.1, DoPiercingCold(oCaster, oTarget, nDamageAmount, nCurrentHP));
|
|
}
|
|
}
|
|
|
|
/* // Piercing Cold for the Frost Mage
|
|
if (GetLevelByClass(CLASS_TYPE_FROST_MAGE, oCaster) >= 4 && nDamageType == DAMAGE_TYPE_COLD)
|
|
{
|
|
int nCurrentHP = GetCurrentHitPoints(oTarget);
|
|
DelayCommand(0.1, DoPiercingCold(oCaster, oTarget, nDamageAmount, nCurrentHP));
|
|
} */
|
|
|
|
// Die DR die
|
|
if (GetLocalInt(oCaster, "MoveIgnoreDR"))
|
|
{
|
|
int nCurrentHP = GetCurrentHitPoints(oTarget);
|
|
DelayCommand(0.1, BreakDR(oCaster, oTarget, nDamageAmount, nCurrentHP));
|
|
}
|
|
}
|
|
|
|
// Frostrager heals on cold damage when raging. 1 heal for every 2 cold damage.
|
|
if (GetLocalInt(oTarget, "Frostrage") && nDamageType == DAMAGE_TYPE_COLD)
|
|
{
|
|
nDamageAmount /= 2;
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nDamageAmount), oTarget);
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FROST_L), oTarget);
|
|
|
|
FloatingTextStringOnCreature("Absorb Cold healed " + IntToString(nDamageAmount), oTarget, FALSE);
|
|
effect eEffect;
|
|
return eEffect; //Doesn't hurt him
|
|
}
|
|
// Phoenix Belt gains fast healing when hit by fire damage
|
|
if (GetHasSpellEffect(MELD_PHOENIX_BELT, oTarget) && nDamageType == DAMAGE_TYPE_FIRE)
|
|
{
|
|
int nEssentia = GetEssentiaInvested(oTarget, MELD_PHOENIX_BELT);
|
|
int nResist = nEssentia * 5;
|
|
int nDur;
|
|
if (nDamageAmount >= nResist) nDur = nResist;
|
|
else nDur = nDamageAmount;
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectRegenerate(1, 6.0), oTarget, RoundsToSeconds(nDur+1));
|
|
}
|
|
|
|
return EffectDamage(nDamageAmount, nDamageType, nDamagePower);
|
|
}
|
|
|
|
// * Kovi. removes any effects from this type of spell
|
|
// * i.e., used in Mage Armor to remove any previous
|
|
// * mage armors
|
|
void PRCRemoveEffectsFromSpell(object oTarget, int SpellID)
|
|
{
|
|
effect eLook = GetFirstEffect(oTarget);
|
|
while(GetIsEffectValid(eLook))
|
|
{
|
|
if(GetEffectSpellId(eLook) == SpellID)
|
|
RemoveEffect(oTarget, eLook);
|
|
|
|
eLook = GetNextEffect(oTarget);
|
|
}
|
|
}
|
|
|
|
void PRCRemoveSpecificEffect(int nEffectTypeID, object oTarget)
|
|
{
|
|
//Search through the valid effects on the target.
|
|
effect eAOE = GetFirstEffect(oTarget);
|
|
while (GetIsEffectValid(eAOE))
|
|
{
|
|
if (GetEffectType(eAOE) == nEffectTypeID)
|
|
{
|
|
//If the effect was created by the spell then remove it
|
|
RemoveEffect(oTarget, eAOE);
|
|
}
|
|
//Get next effect on the target
|
|
eAOE = GetNextEffect(oTarget);
|
|
}
|
|
}
|
|
|
|
effect PRCGetScaledEffect(effect eStandard, object oTarget)
|
|
{
|
|
int nDiff = GetGameDifficulty();
|
|
int nEffType = GetEffectType(eStandard);
|
|
|
|
if(GetIsPC(oTarget) || GetIsPC(GetMaster(oTarget)))
|
|
{
|
|
if(nEffType == EFFECT_TYPE_FRIGHTENED)
|
|
{
|
|
if(nDiff == GAME_DIFFICULTY_VERY_EASY)
|
|
{
|
|
return EffectAttackDecrease(-2);
|
|
}
|
|
else if(nDiff == GAME_DIFFICULTY_EASY)
|
|
{
|
|
return EffectAttackDecrease(-4);
|
|
}
|
|
}
|
|
if(nDiff == GAME_DIFFICULTY_VERY_EASY &&
|
|
(nEffType == EFFECT_TYPE_PARALYZE ||
|
|
nEffType == EFFECT_TYPE_STUNNED ||
|
|
nEffType == EFFECT_TYPE_CONFUSED))
|
|
{
|
|
return EffectDazed();
|
|
}
|
|
if(nEffType == EFFECT_TYPE_CHARMED
|
|
|| nEffType == EFFECT_TYPE_DOMINATED)
|
|
{
|
|
return EffectDazed();
|
|
}
|
|
}
|
|
return eStandard;
|
|
}
|
|
|
|
int PRCAmIAHumanoid(object oTarget)
|
|
{
|
|
int nRacial = MyPRCGetRacialType(oTarget);
|
|
if(nRacial == RACIAL_TYPE_DWARF
|
|
|| nRacial == RACIAL_TYPE_HALFELF
|
|
|| nRacial == RACIAL_TYPE_HALFORC
|
|
|| nRacial == RACIAL_TYPE_ELF
|
|
|| nRacial == RACIAL_TYPE_GNOME
|
|
|| nRacial == RACIAL_TYPE_HUMANOID_GOBLINOID
|
|
|| nRacial == RACIAL_TYPE_HALFLING
|
|
|| nRacial == RACIAL_TYPE_HUMAN
|
|
|| nRacial == RACIAL_TYPE_HUMANOID_MONSTROUS
|
|
|| nRacial == RACIAL_TYPE_HUMANOID_ORC
|
|
|| nRacial == RACIAL_TYPE_HUMANOID_REPTILIAN)
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
int PRCGetScaledDuration(int nActualDuration, object oTarget)
|
|
{
|
|
int nDiff = GetGameDifficulty();
|
|
int nNew = nActualDuration;
|
|
if(GetIsPC(oTarget) && nActualDuration > 3)
|
|
{
|
|
if(nDiff == GAME_DIFFICULTY_VERY_EASY
|
|
|| nDiff == GAME_DIFFICULTY_EASY)
|
|
{
|
|
nNew = nActualDuration / 4;
|
|
}
|
|
else if(nDiff == GAME_DIFFICULTY_NORMAL)
|
|
{
|
|
nNew = nActualDuration / 2;
|
|
}
|
|
if(nNew == 0)
|
|
{
|
|
nNew = 1;
|
|
}
|
|
}
|
|
return nNew;
|
|
}
|
|
|
|
effect PRCCreateProtectionFromAlignmentLink(int nAlignment, int nPower = 1)
|
|
{
|
|
int nFinal = nPower * 2;
|
|
int nAlignmentLC;
|
|
int nAlignmentGE;
|
|
effect eDur;
|
|
|
|
switch(nAlignment)
|
|
{
|
|
case ALIGNMENT_LAWFUL:{
|
|
nAlignmentLC = ALIGNMENT_LAWFUL;
|
|
nAlignmentGE = ALIGNMENT_ALL;
|
|
eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR);
|
|
break;}
|
|
case ALIGNMENT_CHAOTIC:{
|
|
nAlignmentLC = ALIGNMENT_CHAOTIC;
|
|
nAlignmentGE = ALIGNMENT_ALL;
|
|
eDur = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR);
|
|
break;}
|
|
case ALIGNMENT_GOOD:{
|
|
nAlignmentLC = ALIGNMENT_ALL;
|
|
nAlignmentGE = ALIGNMENT_GOOD;
|
|
eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR);
|
|
break;}
|
|
case ALIGNMENT_EVIL:{
|
|
nAlignmentLC = ALIGNMENT_ALL;
|
|
nAlignmentGE = ALIGNMENT_EVIL;
|
|
eDur = EffectVisualEffect(VFX_DUR_PROTECTION_GOOD_MINOR);
|
|
break;}
|
|
}
|
|
|
|
//construct final effect
|
|
effect eLink = EffectACIncrease(nFinal, AC_DEFLECTION_BONUS);
|
|
eLink = EffectLinkEffects(eLink, EffectSavingThrowIncrease(SAVING_THROW_ALL, nFinal));
|
|
eLink = EffectLinkEffects(eLink, EffectImmunity(IMMUNITY_TYPE_MIND_SPELLS));
|
|
//make it vs alignment
|
|
eLink = VersusAlignmentEffect(eLink, nAlignmentLC, nAlignmentGE);
|
|
//add duration vfx
|
|
eLink = EffectLinkEffects(eLink, EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE));
|
|
eLink = EffectLinkEffects(eLink, eDur);
|
|
|
|
return eLink;
|
|
}
|
|
|
|
float PRCGetSpellEffectDelay(location SpellTargetLocation, object oTarget)
|
|
{
|
|
float fDelay = GetDistanceBetweenLocations(SpellTargetLocation, GetLocation(oTarget))/20;
|
|
return fDelay;
|
|
}
|
|
|
|
// * returns true if the creature has flesh
|
|
int PRCIsImmuneToPetrification(object oCreature)
|
|
{
|
|
int nAppearance = GetAppearanceType(oCreature);
|
|
int bImmune = FALSE;
|
|
|
|
if (GetHasSpellEffect(VESTIGE_HAAGENTI, oCreature) && GetLocalInt(oCreature, "ExploitVestige") != VESTIGE_HAAGENTI_IMMUNE_TRANS) bImmune = TRUE;
|
|
|
|
switch (nAppearance)
|
|
{
|
|
case APPEARANCE_TYPE_BASILISK:
|
|
case APPEARANCE_TYPE_COCKATRICE:
|
|
case APPEARANCE_TYPE_MEDUSA:
|
|
case APPEARANCE_TYPE_ALLIP:
|
|
case APPEARANCE_TYPE_ELEMENTAL_AIR:
|
|
case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER:
|
|
case APPEARANCE_TYPE_ELEMENTAL_EARTH:
|
|
case APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER:
|
|
case APPEARANCE_TYPE_ELEMENTAL_FIRE:
|
|
case APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER:
|
|
case APPEARANCE_TYPE_ELEMENTAL_WATER:
|
|
case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER:
|
|
case APPEARANCE_TYPE_GOLEM_STONE:
|
|
case APPEARANCE_TYPE_GOLEM_IRON:
|
|
case APPEARANCE_TYPE_GOLEM_CLAY:
|
|
case APPEARANCE_TYPE_GOLEM_BONE:
|
|
case APPEARANCE_TYPE_GORGON:
|
|
case APPEARANCE_TYPE_HEURODIS_LICH:
|
|
case APPEARANCE_TYPE_LANTERN_ARCHON:
|
|
case APPEARANCE_TYPE_SHADOW:
|
|
case APPEARANCE_TYPE_SHADOW_FIEND:
|
|
case APPEARANCE_TYPE_SHIELD_GUARDIAN:
|
|
case APPEARANCE_TYPE_SKELETAL_DEVOURER:
|
|
case APPEARANCE_TYPE_SKELETON_CHIEFTAIN:
|
|
case APPEARANCE_TYPE_SKELETON_COMMON:
|
|
case APPEARANCE_TYPE_SKELETON_MAGE:
|
|
case APPEARANCE_TYPE_SKELETON_PRIEST:
|
|
case APPEARANCE_TYPE_SKELETON_WARRIOR:
|
|
case APPEARANCE_TYPE_SKELETON_WARRIOR_1:
|
|
case APPEARANCE_TYPE_SPECTRE:
|
|
case APPEARANCE_TYPE_WILL_O_WISP:
|
|
case APPEARANCE_TYPE_WRAITH:
|
|
case APPEARANCE_TYPE_BAT_HORROR:
|
|
case 405: // Dracolich:
|
|
case 415: // Alhoon
|
|
case 418: // shadow dragon
|
|
case 420: // mithral golem
|
|
case 421: // admantium golem
|
|
case 430: // Demi Lich
|
|
case 469: // animated chest
|
|
case 474: // golems
|
|
case 475: // golems
|
|
bImmune = TRUE;
|
|
}
|
|
|
|
int nRacialType = MyPRCGetRacialType(oCreature);
|
|
switch(nRacialType)
|
|
{
|
|
case RACIAL_TYPE_ELEMENTAL:
|
|
case RACIAL_TYPE_CONSTRUCT:
|
|
case RACIAL_TYPE_OOZE:
|
|
case RACIAL_TYPE_UNDEAD:
|
|
bImmune = TRUE;
|
|
}
|
|
|
|
// 01/08/07 Racial feat for petrification immunity
|
|
if(GetHasFeat(FEAT_IMMUNE_PETRIFICATION)) bImmune = TRUE;
|
|
|
|
// 03/07/2005 CraigW - Petrification immunity can also be granted as an item property.
|
|
if ( ResistSpell(OBJECT_SELF,oCreature) == 2 )
|
|
{
|
|
bImmune = TRUE;
|
|
}
|
|
|
|
// * GZ: Sept 2003 - Prevent people from petrifying DM, resulting in GUI even when
|
|
// effect is not successful.
|
|
if (!GetPlotFlag(oCreature) && GetIsDM(oCreature))
|
|
{
|
|
bImmune = FALSE;
|
|
}
|
|
return bImmune;
|
|
}
|
|
|
|
// * This is a wrapper for how Petrify will work in Expansion Pack 1
|
|
// * Scripts affected: flesh to stone, breath petrification, gaze petrification, touch petrification
|
|
// * nPower : This is the Hit Dice of a Monster using Gaze, Breath or Touch OR it is the Caster Spell of
|
|
// * a spellcaster
|
|
// * nFortSaveDC: pass in this number from the spell script
|
|
void PRCDoPetrification(int nPower, object oSource, object oTarget, int nSpellID, int nFortSaveDC)
|
|
{
|
|
|
|
if(!GetIsReactionTypeFriendly(oTarget))
|
|
{
|
|
// * exit if creature is immune to petrification
|
|
if(PRCIsImmuneToPetrification(oTarget))
|
|
return;
|
|
|
|
float fDifficulty = 0.0;
|
|
int bIsPC = GetIsPC(oTarget);
|
|
int bShowPopup = FALSE;
|
|
|
|
// * calculate Duration based on difficulty settings
|
|
int nGameDiff = GetGameDifficulty();
|
|
switch (nGameDiff)
|
|
{
|
|
case GAME_DIFFICULTY_VERY_EASY:
|
|
case GAME_DIFFICULTY_EASY:
|
|
case GAME_DIFFICULTY_NORMAL:
|
|
fDifficulty = RoundsToSeconds(nPower); // One Round per hit-die or caster level
|
|
break;
|
|
case GAME_DIFFICULTY_CORE_RULES:
|
|
case GAME_DIFFICULTY_DIFFICULT:
|
|
bShowPopup = TRUE;
|
|
break;
|
|
}
|
|
|
|
int nSaveDC = nFortSaveDC;
|
|
effect ePetrify = EffectPetrify();
|
|
|
|
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE);
|
|
|
|
effect eLink = EffectLinkEffects(eDur, ePetrify);
|
|
|
|
// Let target know the negative spell has been cast
|
|
SignalEvent(oTarget,
|
|
EventSpellCastAt(OBJECT_SELF, nSpellID));
|
|
//SpeakString(IntToString(nSpellID));
|
|
|
|
// Do a fortitude save check
|
|
if (!PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSaveDC))
|
|
{
|
|
// Save failed; apply paralyze effect and VFX impact
|
|
|
|
/// * The duration is permanent against NPCs but only temporary against PCs
|
|
if (bIsPC == TRUE)
|
|
{
|
|
if (bShowPopup == TRUE)
|
|
{
|
|
// * under hardcore rules or higher, this is an instant death
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget);
|
|
//only pop up death panel if switch is not set
|
|
if(!GetPRCSwitch(PRC_NO_PETRIFY_GUI))
|
|
DelayCommand(2.75, PopUpDeathGUIPanel(oTarget, FALSE , TRUE, 40579));
|
|
//run the PRC Ondeath code
|
|
//no way to run the normal module ondeath code too
|
|
//so a execute script has been added for builders to take advantage of
|
|
DelayCommand(2.75, ExecuteScript("prc_ondeath", oTarget));
|
|
DelayCommand(2.75, ExecuteScript("prc_pw_petrific", oTarget));
|
|
// if in hardcore, treat the player as an NPC
|
|
bIsPC = FALSE;
|
|
//fDifficulty = TurnsToSeconds(nPower); // One turn per hit-die
|
|
}
|
|
else
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oTarget, fDifficulty);
|
|
}
|
|
else
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oTarget);
|
|
}
|
|
// April 2003: Clearing actions to kick them out of conversation when petrified
|
|
AssignCommand(oTarget, ClearAllActions(TRUE));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// GZ: 2003-Oct-15
|
|
// A different approach for timing these spells that has the positive side
|
|
// effects of making the spell dispellable as well.
|
|
// I am using the VFX applied by the spell to track the remaining duration
|
|
// instead of adding the remaining runtime on the stack
|
|
//
|
|
// This function returns FALSE if a delayed Spell effect from nSpell_ID has
|
|
// expired. See x2_s0_bigby4.nss for details
|
|
//------------------------------------------------------------------------------
|
|
int PRCGetDelayedSpellEffectsExpired(int nSpell_ID, object oTarget, object oCaster)
|
|
{
|
|
|
|
if (!GetHasSpellEffect(nSpell_ID,oTarget) )
|
|
{
|
|
DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID));
|
|
return TRUE;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// GZ: 2003-Oct-15
|
|
// If the caster is dead or no longer there, cancel the spell, as it is
|
|
// directed
|
|
//--------------------------------------------------------------------------
|
|
if( !GetIsObjectValid(oCaster))
|
|
{
|
|
GZPRCRemoveSpellEffects(nSpell_ID, oTarget);
|
|
DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID));
|
|
return TRUE;
|
|
}
|
|
|
|
if (GetIsDead(oCaster))
|
|
{
|
|
DeleteLocalInt(oTarget,"XP2_L_SPELL_SAVE_DC_" + IntToString (nSpell_ID));
|
|
GZPRCRemoveSpellEffects(nSpell_ID, oTarget);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
// Much similar to PRCGetHasSpell, but used for JPM to get spells left not counting metamagic
|
|
int PRCGetSpellUsesLeft(int nRealSpellID, object oCreature = OBJECT_SELF)
|
|
{
|
|
if(!PRCGetIsRealSpellKnown(nRealSpellID, oCreature))
|
|
return 0;
|
|
int nUses = GetHasSpell(nRealSpellID, oCreature);
|
|
|
|
int nClass, nSpellbookID, nCount, i, j;
|
|
int nSpellbookType, nSpellLevel;
|
|
string sFile, sFeat;
|
|
for(i = 1; i <= 8; i++)
|
|
{
|
|
nClass = GetClassByPosition(i, oCreature);
|
|
sFile = GetFileForClass(nClass);
|
|
nSpellbookType = GetSpellbookTypeForClass(nClass);
|
|
nSpellbookID = RealSpellToSpellbookID(nClass, nRealSpellID);
|
|
if (nSpellbookID != -1)
|
|
{ //non-spellbook classes should return -1
|
|
sFeat = Get2DACache(sFile, "ReqFeat", j);
|
|
if(sFeat != "")
|
|
{
|
|
if(!GetHasFeat(StringToInt(sFeat), oCreature))
|
|
continue;
|
|
}
|
|
if(nSpellbookType == SPELLBOOK_TYPE_PREPARED)
|
|
{
|
|
nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j);
|
|
if(DEBUG) DoDebug("PRCGetHasSpell(Prepared Caster): NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount));
|
|
if(nCount > 0)
|
|
{
|
|
nUses += nCount;
|
|
}
|
|
}
|
|
else if(nSpellbookType == SPELLBOOK_TYPE_SPONTANEOUS)
|
|
{
|
|
nSpellLevel = StringToInt(Get2DACache(sFile, "Level", j));
|
|
nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), nSpellLevel);
|
|
if(DEBUG) DoDebug("PRCGetHasSpell(Spontaneous Caster): NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount));
|
|
if(nCount > 0)
|
|
{
|
|
nUses += nCount;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(DEBUG) DoDebug("PRCGetHasSpell: RealSpellID = " + IntToString(nRealSpellID) + ", Uses = " + IntToString(nUses));
|
|
return nUses;
|
|
}
|
|
|
|
// * Applies the effects of FEAT_AUGMENT_SUMMON to summoned creatures.
|
|
void AugmentSummonedCreature(string sResRef)
|
|
{
|
|
if(GetHasFeat(FEAT_AUGMENT_SUMMON))
|
|
{
|
|
int i = 1;
|
|
object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF);
|
|
while(GetIsObjectValid(oSummon))
|
|
{
|
|
if(GetResRef(oSummon) == sResRef && !GetLocalInt(oSummon, "Augmented"))
|
|
{
|
|
effect eLink = EffectAbilityIncrease(ABILITY_STRENGTH, 4);
|
|
eLink = EffectLinkEffects(eLink, EffectAbilityIncrease(ABILITY_CONSTITUTION, 4));
|
|
eLink = SupernaturalEffect(eLink);
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oSummon);
|
|
SetLocalInt(oSummon, "Augmented", TRUE);
|
|
}
|
|
i++;
|
|
oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i);
|
|
}
|
|
}
|
|
if(sResRef == "prc_sum_treant")
|
|
{
|
|
int i = 1;
|
|
object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF);
|
|
while(GetIsObjectValid(oSummon))
|
|
{
|
|
if(GetResRef(oSummon) == sResRef)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectVisualEffect(VFX_DUR_PROT_BARKSKIN), oSummon);
|
|
}
|
|
i++;
|
|
oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i);
|
|
}
|
|
}
|
|
if(GetHasFeat(FEAT_BECKON_THE_FROZEN))
|
|
{
|
|
int i = 1;
|
|
object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF);
|
|
while(GetIsObjectValid(oSummon))
|
|
{
|
|
if(GetResRef(oSummon) == sResRef && !GetLocalInt(oSummon, "BeckonTheFrozen"))
|
|
{
|
|
effect eLink = EffectVisualEffect(VFX_DUR_ELEMENTAL_SHIELD);
|
|
eLink = EffectLinkEffects(eLink, EffectDamageImmunityDecrease(DAMAGE_TYPE_FIRE, 50));
|
|
eLink = EffectLinkEffects(eLink, EffectDamageImmunityIncrease(DAMAGE_TYPE_COLD, 100));
|
|
eLink = SupernaturalEffect(eLink);
|
|
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eLink, oSummon);
|
|
SetLocalInt(oSummon, "BeckonTheFrozen", TRUE);
|
|
}
|
|
i++;
|
|
oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED, OBJECT_SELF, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
object GetAreaOfEffectObject(location lTarget, string sTag, object oCaster = OBJECT_SELF)
|
|
{
|
|
object oAoE = GetFirstObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT);
|
|
while(GetIsObjectValid(oAoE))
|
|
{
|
|
if((GetAreaOfEffectCreator(oAoE) == oCaster) //was created by oCaster
|
|
&& GetTag(oAoE) == sTag //has required tag
|
|
&& !GetLocalInt(oAoE, "X2_AoE_BaseSaveDC")) //and wasn't setup before
|
|
{
|
|
return oAoE;
|
|
}
|
|
oAoE = GetNextObjectInShape(SHAPE_SPHERE, 1.0f, lTarget, FALSE, OBJECT_TYPE_AREA_OF_EFFECT);
|
|
}
|
|
return OBJECT_INVALID;
|
|
}
|
|
|
|
string GetAreaOfEffectTag(int nAoE)
|
|
{
|
|
return Get2DACache("vfx_persistent", "LABEL", nAoE);
|
|
}
|
|
|
|
int CheckTurnUndeadUses(object oPC, int nUses)
|
|
{
|
|
int i;
|
|
while(i < nUses)
|
|
{
|
|
if(GetHasFeat(FEAT_TURN_UNDEAD, oPC))
|
|
{
|
|
DecrementRemainingFeatUses(oPC, FEAT_TURN_UNDEAD);
|
|
i++;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
if(i < nUses)
|
|
{
|
|
while(i)
|
|
{
|
|
IncrementRemainingFeatUses(oPC, FEAT_TURN_UNDEAD);
|
|
i--;
|
|
}
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// this will execute the prespellcastcode, whose full functionality is incoded in X2PreSpellCastCode2(),
|
|
// as a script, to save loading time for spells scripts and reduce memory usage of NWN
|
|
// the prespellcode takes up roughly 250 kByte compiled code, meaning that every spell script that
|
|
// calls it directly as a function (e.g.: X2PreSpellCastCode2) will be between 100 kByte to 250 kByte
|
|
// larger, than a spell script calling the prespellcode via ExecuteScript (e.g. X2PreSpellCastCode)
|
|
// Although ExecuteScript is slightly slower than a direct function call, quite likely overall performance is
|
|
// increased, because for every new spell 100-250 kByte less code need to be loaded into memory
|
|
// and NWN has more free memory available to keep more spells scripts (and other crucial scripts)
|
|
//in RAM
|
|
|
|
int X2PreSpellCastCode()
|
|
{
|
|
object oCaster = OBJECT_SELF;
|
|
|
|
// SetLocalInt(oCaster, "PSCC_Ret", 0);
|
|
ExecuteScript("prc_prespell", oCaster);
|
|
|
|
int nReturn = GetLocalInt(oCaster, "PSCC_Ret");
|
|
// DeleteLocalInt(oCaster, "PSCC_Ret");
|
|
|
|
return nReturn;
|
|
}
|
|
|
|
//:: Test Void
|
|
// void main (){} |