2025/12/11 Update
Inscribe Rune should be Craft Armor 1 not Craft General 1.
This commit is contained in:
@@ -2463,7 +2463,7 @@
|
||||
2459 TactileTrapsmith 16824612 16824613 ife_lightflex **** **** **** **** **** **** **** **** **** **** 0 0 1 **** **** **** **** 1 **** **** **** **** **** **** **** **** **** **** **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 1
|
||||
2460 AugmentHealing 16824614 16824615 ife_X1ResDis **** **** **** **** **** **** **** **** **** **** 0 0 1 **** **** **** **** 1 **** **** **** **** **** **** **** **** 4 4 **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 1
|
||||
2461 DetectEvil 16827574 16827575 is_detect_evil **** **** **** **** **** **** **** **** **** **** 0 0 0 15 5 3201 **** 0.5 -1 **** **** **** **** **** **** **** **** **** **** **** FEAT_DEATH_DOMAIN_POWER 6 **** **** **** **** **** 0 1
|
||||
2462 InscribeRune 16831916 16831917 ife_X2PlnrTurn **** **** **** 13 **** **** **** **** **** **** 0 0 1 **** **** 1621 **** 1 **** **** 1 **** **** **** **** **** 38 1 **** **** FEAT_INSCRIBE_RUNE 4 **** **** **** **** **** 0 0
|
||||
2462 InscribeRune 16831916 16831917 ife_X2PlnrTurn **** **** **** 13 **** **** **** **** **** **** 0 0 1 **** **** 1621 **** 1 **** **** 1 **** **** **** **** **** 25 1 **** **** FEAT_INSCRIBE_RUNE 4 **** **** **** **** **** 0 0
|
||||
2463 RuneCraft 16831973 16831974 ife_legendaryart **** **** **** **** **** **** **** **** **** **** 0 0 0 **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 1
|
||||
2464 RunePower 16831975 16831976 Ife_epMagmaBurst **** **** **** **** **** **** **** **** **** **** 0 0 0 **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 1
|
||||
2465 ImprovedRunecasting 16831977 16831978 ife_dweomerthief **** **** **** **** **** **** **** **** **** **** 0 0 0 **** **** 1624 **** 1 -1 **** 1 **** **** **** **** **** **** **** **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 0
|
||||
|
||||
33
nwn/nwnprc/trunk/newcompilespells.bat
Normal file
33
nwn/nwnprc/trunk/newcompilespells.bat
Normal file
@@ -0,0 +1,33 @@
|
||||
REM echo Current dir: %cd%
|
||||
REM dir ..
|
||||
REM dir ..\psionics
|
||||
REM pause
|
||||
|
||||
@ECHO OFF
|
||||
|
||||
cls
|
||||
|
||||
REM nwn_script_comp.exe -c -j4 -y -O0 --root "C:\Games\Steam\steamapps\common\Neverwinter Nights" --dirs "C:\Users\Jason\Documents\Neverwinter Nights\modules\temp0,D:\NWN Repos\PRC8\nwn\nwnprc\trunk\include" "C:\Users\Jason\Documents\Neverwinter Nights\modules\temp0"
|
||||
|
||||
REM nwn_script_comp.exe -c -j4 -y --max-include-depth=32 ^
|
||||
REM --root "C:\Games\Steam\steamapps\common\Neverwinter Nights" ^
|
||||
REM --dirs ".\psionics,.\include" ^
|
||||
REM -d ".\psionicsobjs" ^
|
||||
REM ".\psionics"
|
||||
|
||||
nwn_script_comp.exe -c -j4 -y . --max-include-depth=32 -d ".\spellobjs" --dirs ".\spells,.\include" ".\spells"
|
||||
|
||||
|
||||
REM :loop
|
||||
REM "C:\NWN Work\nwnsc.exe" -s -o -w -n "C:\Games\Steam\steamapps\common\Neverwinter Nights" -i "C:\Users\Jason\Documents\Neverwinter Nights\modules\temp0";"D:\NWN Repos\PRC8\nwn\nwnprc\trunk\include" "C:\Users\Jason\Documents\Neverwinter Nights\modules\temp0\*.nss"
|
||||
REM if %errorLevel% == -1 goto :loop
|
||||
pause
|
||||
|
||||
|
||||
|
||||
REM @echo on
|
||||
|
||||
REM tools\nwnsc -w -i "include" -n "C:\Games\Steam\steamapps\common\Neverwinter Nights" -b "psionicsobjs" "psionics\*.nss"
|
||||
REM tools\nwn_erf.exe -e hak --quiet -c -f CompiledResources\prc8_psionics.hak .\psionics .\psionicsobjs
|
||||
REM pause
|
||||
REM :end
|
||||
Binary file not shown.
@@ -40753,7 +40753,7 @@ Epic Levels:
|
||||
<entry id="54699" lang="en" sex="m">####END_OF_NEW_SPELLBOOK_RESERVE</entry>
|
||||
<entry id="54700" lang="en" sex="m">Inscribe Rune</entry>
|
||||
<entry id="54701" lang="en" sex="m">Type of Feat: Item Creation
|
||||
Prerequisite: Intelligence 13, Divine Spells level 2.
|
||||
Prerequisite: Intelligence 13, Divine Spells level 2, Craft Armor 1 rank
|
||||
Specifics: You can cast any divine spell you have prepared as a rune. The caster must have the spell memorized to be able to create the rune. Crafting a rune costs Spell level * Caster level * 50 in gold pieces for a normal caster (half the base cost of Spell level * Caster level * 100), and Spell level * Caster level * 25 for a Runecaster (half again). It also takes XP equal to 1/25th of the base cost. If you cannot pay the casting cost or the XP loss would drop you a level, the crafting will fail, and the spell will be lost. Crafting the rune requires a craft check vs DC 20 + Spell Level. If the craft check fails, the spell is lost, but the caster does not pay the gold and experience. A rune is permanent until used, and all spells are centred on the user.
|
||||
Use: Selected.</entry>
|
||||
<entry id="54702" lang="en" sex="m">Cast Rune</entry>
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,274 @@
|
||||
|
||||
int GetFeatForSeed(int nSeedID);
|
||||
int GetIPForSeed(int nSeedID);
|
||||
int GetDCForSeed(int nSeedID);
|
||||
int GetClassForSeed(int nSeedID);
|
||||
int GetCanLearnSeed(object oPC, int nSeedID);
|
||||
int GetSeedFromAbrev(string sAbrev);
|
||||
string GetNameForSeed(int nSeedID);
|
||||
|
||||
int GetDCForSpell(int nSpellID);
|
||||
int GetFeatForSpell(int nSpellID);
|
||||
int GetResearchFeatForSpell(int nSpellID);
|
||||
int GetIPForSpell(int nSpellID);
|
||||
int GetResearchIPForSpell(int nSpellID);
|
||||
int GetCastXPForSpell(int nSpellID);
|
||||
string GetSchoolForSpell(int nSpellID);
|
||||
int GetR1ForSpell(int nSpellID);
|
||||
int GetR2ForSpell(int nSpellID);
|
||||
int GetR3ForSpell(int nSpellID);
|
||||
int GetR4ForSpell(int nSpellID);
|
||||
string GetNameForSpell(int nSpellID);
|
||||
int GetSpellFromAbrev(string sAbrev);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Includes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
#include "inc_utility"
|
||||
//#include "inc_epicspelldef"
|
||||
#include "inc_epicspells"
|
||||
|
||||
// SEED FUNCTIONS
|
||||
|
||||
int GetFeatForSeed(int nSeedID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspellseeds", "FeatID", nSeedID));
|
||||
}
|
||||
|
||||
int GetIPForSeed(int nSeedID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspellseeds", "FeatIPID", nSeedID));
|
||||
}
|
||||
|
||||
int GetDCForSeed(int nSeedID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspellseeds", "DC", nSeedID));
|
||||
}
|
||||
|
||||
int GetClassForSeed(int nSeedID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspellseeds", "Class", nSeedID));
|
||||
}
|
||||
|
||||
int GetSeedFromAbrev(string sAbrev)
|
||||
{
|
||||
sAbrev = GetStringLowerCase(sAbrev);
|
||||
if(GetStringLeft(sAbrev, 8) == "epic_sd_")
|
||||
sAbrev = GetStringRight(sAbrev, GetStringLength(sAbrev)-8);
|
||||
int i = 0;
|
||||
string sLabel = GetStringLowerCase(Get2DACache("epicspellseeds", "LABEL", i));
|
||||
while(sLabel != "")
|
||||
{
|
||||
if(sAbrev == sLabel)
|
||||
return i;
|
||||
i++;
|
||||
sLabel = GetStringLowerCase(Get2DACache("epicspellseeds", "LABEL", i));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
string GetNameForSeed(int nSeedID)
|
||||
{
|
||||
int nFeat = GetFeatForSeed(nSeedID);
|
||||
string sName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat)));
|
||||
return sName;
|
||||
}
|
||||
|
||||
/*
|
||||
Bit-flags set in epicspellseeds.2da in Class column
|
||||
used to restrict access to epic spell seeds for some classes
|
||||
ie: 13 means that only clerics, sorcerers and wizards can learn that seed (1 + 4 + 8),
|
||||
all classes can use == 32767
|
||||
*/
|
||||
int _Class2BitFlag(int nClass)
|
||||
{
|
||||
switch(nClass)
|
||||
{
|
||||
case CLASS_TYPE_CLERIC: return 1;
|
||||
case CLASS_TYPE_DRUID: return 2;
|
||||
case CLASS_TYPE_SORCERER: return 4;
|
||||
case CLASS_TYPE_WIZARD: return 8;
|
||||
case CLASS_TYPE_HEALER: return 16;
|
||||
case CLASS_TYPE_BEGUILER: return 32;
|
||||
case CLASS_TYPE_SUBLIME_CHORD: return 64;
|
||||
case CLASS_TYPE_DREAD_NECROMANCER: return 128;
|
||||
case CLASS_TYPE_MYSTIC: return 256;
|
||||
case CLASS_TYPE_ARCHIVIST: return 512;
|
||||
case CLASS_TYPE_SHAMAN: return 4096;
|
||||
case CLASS_TYPE_FAVOURED_SOUL: return 8192;
|
||||
case CLASS_TYPE_WARMAGE: return 16384;
|
||||
case CLASS_TYPE_UR_PRIEST: return 1;
|
||||
case CLASS_TYPE_BLIGHTER: return 2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _CheckEpicSpellcastingForClass(object oPC, int nClass)
|
||||
{
|
||||
if(GetHitDice(oPC) < 21)
|
||||
return FALSE;
|
||||
|
||||
switch(nClass)
|
||||
{
|
||||
case CLASS_TYPE_CLERIC: return GetIsEpicCleric(oPC);
|
||||
case CLASS_TYPE_DRUID: return GetIsEpicDruid(oPC);
|
||||
case CLASS_TYPE_SORCERER: return GetIsEpicSorcerer(oPC);
|
||||
case CLASS_TYPE_WIZARD: return GetIsEpicWizard(oPC);
|
||||
case CLASS_TYPE_HEALER: return GetIsEpicHealer(oPC);
|
||||
case CLASS_TYPE_BEGUILER: return GetIsEpicBeguiler(oPC);
|
||||
case CLASS_TYPE_SUBLIME_CHORD: return GetIsEpicSublimeChord(oPC);
|
||||
case CLASS_TYPE_DREAD_NECROMANCER: return GetIsEpicDreadNecromancer(oPC);
|
||||
case CLASS_TYPE_ARCHIVIST: return GetIsEpicArchivist(oPC);
|
||||
case CLASS_TYPE_SHAMAN: return GetIsEpicShaman(oPC);
|
||||
case CLASS_TYPE_FAVOURED_SOUL: return GetIsEpicFavSoul(oPC);
|
||||
case CLASS_TYPE_WARMAGE: return GetIsEpicWarmage(oPC);
|
||||
case CLASS_TYPE_BLIGHTER: return GetIsEpicBlighter(oPC);
|
||||
case CLASS_TYPE_UR_PRIEST: return GetIsEpicUrPriest(oPC);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int GetCanLearnSeed(object oPC, int nSeedID)
|
||||
{
|
||||
int nRestr = GetClassForSeed(nSeedID);
|
||||
int i, nClass;
|
||||
for(i = 1; i <= 8; i++)
|
||||
{
|
||||
nClass = GetClassByPosition(i, oPC);
|
||||
if(_CheckEpicSpellcastingForClass(oPC, nClass)//this class has epic spellcasting
|
||||
&& (nRestr & _Class2BitFlag(nClass)))//and was added to class column in epicspellseeds.2da
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// SPELL FUNCTIONS
|
||||
|
||||
int GetDCForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "DC", nSpellID));
|
||||
}
|
||||
|
||||
int GetFeatForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "SpellFeatID", nSpellID));
|
||||
}
|
||||
|
||||
int GetResearchFeatForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "ResFeatID", nSpellID));
|
||||
}
|
||||
|
||||
int GetIPForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "SpellFeatIPID", nSpellID));
|
||||
}
|
||||
|
||||
int GetResearchIPForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "ResFeatIPID", nSpellID));
|
||||
}
|
||||
|
||||
int GetCastXPForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "CastingXP", nSpellID));
|
||||
}
|
||||
|
||||
string GetSchoolForSpell(int nSpellID)
|
||||
{
|
||||
return Get2DACache("epicspells", "School", nSpellID);
|
||||
}
|
||||
|
||||
int GetR1ForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "Prereq1", nSpellID));
|
||||
}
|
||||
|
||||
int GetR2ForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "Prereq2", nSpellID));
|
||||
}
|
||||
|
||||
int GetR3ForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "Prereq3", nSpellID));
|
||||
}
|
||||
|
||||
int GetR4ForSpell(int nSpellID)
|
||||
{
|
||||
return StringToInt(Get2DACache("epicspells", "Prereq4", nSpellID));
|
||||
}
|
||||
|
||||
int GetS1ForSpell(int nSpellID)
|
||||
{
|
||||
string sSeed = Get2DACache("epicspells", "PrereqSeed1", nSpellID);
|
||||
if(sSeed == "")
|
||||
return -1;
|
||||
return StringToInt(sSeed);
|
||||
}
|
||||
|
||||
int GetS2ForSpell(int nSpellID)
|
||||
{
|
||||
string sSeed = Get2DACache("epicspells", "PrereqSeed2", nSpellID);
|
||||
if(sSeed == "")
|
||||
return -1;
|
||||
return StringToInt(sSeed);
|
||||
}
|
||||
|
||||
int GetS3ForSpell(int nSpellID)
|
||||
{
|
||||
string sSeed = Get2DACache("epicspells", "PrereqSeed3", nSpellID);
|
||||
if(sSeed == "")
|
||||
return -1;
|
||||
return StringToInt(sSeed);
|
||||
}
|
||||
|
||||
int GetS4ForSpell(int nSpellID)
|
||||
{
|
||||
string sSeed = Get2DACache("epicspells", "PrereqSeed4", nSpellID);
|
||||
if(sSeed == "")
|
||||
return -1;
|
||||
return StringToInt(sSeed);
|
||||
}
|
||||
|
||||
int GetS5ForSpell(int nSpellID)
|
||||
{
|
||||
string sSeed = Get2DACache("epicspells", "PrereqSeed5", nSpellID);
|
||||
if(sSeed == "")
|
||||
return -1;
|
||||
return StringToInt(sSeed);
|
||||
}
|
||||
|
||||
int GetSpellFromAbrev(string sAbrev)
|
||||
{
|
||||
sAbrev = GetStringLowerCase(sAbrev);
|
||||
if(GetStringLeft(sAbrev, 8) == "epic_sp_")
|
||||
sAbrev = GetStringRight(sAbrev, GetStringLength(sAbrev)-8);
|
||||
if(DEBUG) DoDebug("sAbrev to check vs: " + sAbrev);
|
||||
int i = 0;
|
||||
string sLabel = GetStringLowerCase(Get2DACache("epicspells", "LABEL", i));
|
||||
while(sLabel != "")
|
||||
{
|
||||
if(DEBUG) DoDebug("sLabel to check vs: " + sLabel);
|
||||
if(sAbrev == sLabel)
|
||||
{
|
||||
if(DEBUG) DoDebug("SpellID: " + IntToString(i));
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
sLabel = GetStringLowerCase(Get2DACache("epicspells", "LABEL", i));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
string GetNameForSpell(int nSpellID)
|
||||
{
|
||||
int nFeat = GetFeatForSpell(nSpellID);
|
||||
string sName = GetStringByStrRef(StringToInt(Get2DACache("feat", "FEAT", nFeat)));
|
||||
return sName;
|
||||
}
|
||||
|
||||
//:: void main (){}
|
||||
@@ -0,0 +1,239 @@
|
||||
//::///////////////////////////////////////////////
|
||||
//:: Rend OnHit include
|
||||
//:: inc_rend
|
||||
//:://////////////////////////////////////////////
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Created By: Ornedan
|
||||
//:: Created On: 23.01.2005
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Constant defintions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const string REND_1ST_HIT_DONE = "RendingHit1stHit";
|
||||
const string REND_DONE = "RendingHitDone";
|
||||
|
||||
const string FROST_1ST_HIT_DONE = "FrostRendHit1stHit";
|
||||
const string FROST_DONE = "FrostRendHitDone";
|
||||
|
||||
const string SPINE_1ST_HIT_DONE = "SpineRendHit1stHit";
|
||||
const string SPINE_DONE = "SpineRendHitDone";
|
||||
|
||||
int GetMainHandAttacks(object oPC, int iBABBonus = 0, int bIgnoreCrossBow = FALSE);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function prototypes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
void DoRend(object oTarget, object oAttacker, object oWeapon);
|
||||
int GetDamageFromConstant(int nIPConst);
|
||||
|
||||
void DoFrostRend(object oTarget, object oAttacker, object oWeapon);
|
||||
|
||||
#include "moi_inc_moifunc"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function defintions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
void DoRend(object oTarget, object oAttacker, object oWeapon)
|
||||
{
|
||||
if (DEBUG) DoDebug("DoRend running");
|
||||
// Only one rend allowed per round for the sake of clearness
|
||||
if(GetLocalInt(oAttacker, REND_DONE))
|
||||
return;
|
||||
|
||||
if(GetLocalObject(oAttacker, REND_1ST_HIT_DONE) == oTarget)
|
||||
{
|
||||
if (DEBUG) DoDebug("DoRend second hit");
|
||||
// First, find the weapon base damage
|
||||
int nIPConst;
|
||||
itemproperty ipCheck = GetFirstItemProperty(oWeapon);
|
||||
while(GetIsItemPropertyValid(ipCheck))
|
||||
{
|
||||
if(GetItemPropertyType(ipCheck) == ITEM_PROPERTY_MONSTER_DAMAGE)
|
||||
{
|
||||
nIPConst = GetItemPropertyCostTableValue(ipCheck);
|
||||
break;
|
||||
}
|
||||
ipCheck = GetNextItemProperty(oWeapon);
|
||||
}
|
||||
|
||||
int nDamage = GetDamageFromConstant(nIPConst);
|
||||
int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oAttacker);
|
||||
nStrBon = nStrBon < 0 ? 0 : nStrBon;
|
||||
nDamage += nStrBon;
|
||||
if (GetLevelByClass(CLASS_TYPE_BLACK_BLOOD_CULTIST, oAttacker) >= 6) nDamage *= 2;
|
||||
if (GetIsMeldBound(oAttacker, MELD_GIRALLON_ARMS) == CHAKRA_ARMS) nDamage *= 2;
|
||||
if (GetHasSpellEffect(VESTIGE_IPOS, oAttacker) >= 6) nDamage *= 2;
|
||||
|
||||
int nDamageType;
|
||||
switch(GetBaseItemType(oWeapon))
|
||||
{
|
||||
case BASE_ITEM_CBLUDGWEAPON:
|
||||
nDamageType = DAMAGE_TYPE_BLUDGEONING;
|
||||
break;
|
||||
case BASE_ITEM_CPIERCWEAPON:
|
||||
nDamageType = DAMAGE_TYPE_PIERCING;
|
||||
break;
|
||||
// Both slashing and slashing & piercing weapons do slashing damage from rend
|
||||
// because it's not possible to make the damage be of both types in any
|
||||
// elegant way
|
||||
case BASE_ITEM_CSLASHWEAPON:
|
||||
case BASE_ITEM_CSLSHPRCWEAP:
|
||||
nDamageType = DAMAGE_TYPE_SLASHING;
|
||||
break;
|
||||
|
||||
default:
|
||||
WriteTimestampedLogEntry("Unexpected weapon type in DoRend()!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply damage and VFX
|
||||
effect eDamage = EffectDamage(nDamage, nDamageType);
|
||||
effect eLink = EffectLinkEffects(eDamage, EffectVisualEffect(VFX_COM_BLOOD_CRT_RED));
|
||||
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget);
|
||||
|
||||
// Tell people what happened
|
||||
// * AttackerName rends TargetName *
|
||||
FloatingTextStringOnCreature("* " + GetName(oAttacker) + " " + GetStringByStrRef(0x01000000 + 51197) + " " + GetName(oTarget) + " *", oAttacker, TRUE);
|
||||
|
||||
// Note the rend having happened in the locals
|
||||
SetLocalInt(oAttacker, REND_DONE, TRUE);
|
||||
DelayCommand(4.5, DeleteLocalInt(oAttacker, REND_DONE));
|
||||
}// end if - the target had a local signifying that rend is possible
|
||||
else
|
||||
{
|
||||
SetLocalObject(oAttacker, REND_1ST_HIT_DONE, oTarget);
|
||||
if (DEBUG) DoDebug("DoRend first hit");
|
||||
}
|
||||
}
|
||||
|
||||
void DoFrostRend(object oTarget, object oAttacker, object oWeapon)
|
||||
{
|
||||
// Only one rend allowed per round for the sake of clearness
|
||||
if(GetLocalInt(oAttacker, FROST_DONE))
|
||||
return;
|
||||
|
||||
float fDelay1 = 6.0 - (6.0 / GetMainHandAttacks(oAttacker));
|
||||
float fDelay2 = 6.0 - 2 * (6.0 - fDelay1);
|
||||
|
||||
if(GetLocalObject(oAttacker, FROST_1ST_HIT_DONE) == oTarget)
|
||||
{
|
||||
// First, find the weapon base damage
|
||||
int nIPConst;
|
||||
itemproperty ipCheck = GetFirstItemProperty(oWeapon);
|
||||
while(GetIsItemPropertyValid(ipCheck))
|
||||
{
|
||||
if(GetItemPropertyType(ipCheck) == ITEM_PROPERTY_MONSTER_DAMAGE)
|
||||
{
|
||||
nIPConst = GetItemPropertyCostTableValue(ipCheck);
|
||||
break;
|
||||
}
|
||||
ipCheck = GetNextItemProperty(oWeapon);
|
||||
}
|
||||
|
||||
|
||||
int nDamage = GetDamageFromConstant(nIPConst);
|
||||
int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oAttacker);
|
||||
nStrBon = nStrBon < 0 ? 0 : nStrBon;
|
||||
// nDamage += nStrBon;
|
||||
// nDamage += FloatToInt(nStrBon/2); //1.5x Strength damage
|
||||
nDamage += FloatToInt(nStrBon * 1.5);
|
||||
|
||||
int nDamageType = DAMAGE_TYPE_BLUDGEONING; // It's an unarmed strike, so always bludgeoning for this
|
||||
|
||||
// Apply damage and VFX
|
||||
effect eDamage = EffectDamage(nDamage, nDamageType);
|
||||
effect eLink = EffectLinkEffects(eDamage, EffectVisualEffect(VFX_IMP_FROST_L));
|
||||
eLink = EffectLinkEffects(eLink, EffectDamage(d6(), DAMAGE_TYPE_COLD));
|
||||
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget);
|
||||
|
||||
// Tell people what happened
|
||||
// * AttackerName rends TargetName *
|
||||
FloatingTextStringOnCreature("* " + GetName(oAttacker) + " " + GetStringByStrRef(0x01000000 + 51197) + " " + GetName(oTarget) + " *",
|
||||
oAttacker,
|
||||
TRUE);
|
||||
|
||||
// Note the rend having happened in the locals
|
||||
SetLocalInt(oAttacker, FROST_DONE, TRUE);
|
||||
DelayCommand(fDelay2, DeleteLocalInt(oAttacker, FROST_DONE));
|
||||
}// end if - the target had a local signifying that rend is possible
|
||||
else
|
||||
{
|
||||
SetLocalObject(oAttacker, FROST_1ST_HIT_DONE, oTarget);
|
||||
DelayCommand(fDelay1, DeleteLocalObject(oAttacker, FROST_1ST_HIT_DONE));
|
||||
}
|
||||
}
|
||||
|
||||
void DoSpineRend(object oTarget, object oAttacker, object oWeapon)
|
||||
{
|
||||
// Only one rend allowed per round for the sake of clearness
|
||||
if(GetLocalInt(oAttacker, SPINE_DONE))
|
||||
return;
|
||||
|
||||
float fDelay1 = 6.0 - (6.0 / GetMainHandAttacks(oAttacker));
|
||||
float fDelay2 = 6.0 - 2 * (6.0 - fDelay1);
|
||||
|
||||
if(GetLocalObject(oAttacker, SPINE_1ST_HIT_DONE) == oTarget)
|
||||
{
|
||||
int nStrBon = GetAbilityModifier(ABILITY_STRENGTH, oAttacker);
|
||||
nStrBon = nStrBon < 0 ? 0 : nStrBon;
|
||||
int nDamage = FloatToInt(nStrBon * 1.5);
|
||||
|
||||
// Apply damage and VFX
|
||||
effect eDamage = EffectDamage(nDamage + d6(2), DAMAGE_TYPE_PIERCING);
|
||||
effect eLink = EffectLinkEffects(eDamage, EffectVisualEffect(VFX_COM_BLOOD_SPARK_MEDIUM));
|
||||
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, eLink, oTarget);
|
||||
|
||||
// Tell people what happened
|
||||
// * AttackerName rends TargetName *
|
||||
FloatingTextStringOnCreature("* " + GetName(oAttacker) + " " + GetStringByStrRef(0x01000000 + 51197) + " " + GetName(oTarget) + " *", oAttacker, TRUE);
|
||||
|
||||
// Note the rend having happened in the locals
|
||||
SetLocalInt(oAttacker, SPINE_DONE, TRUE);
|
||||
DelayCommand(fDelay2, DeleteLocalInt(oAttacker, SPINE_DONE));
|
||||
}// end if - the target had a local signifying that rend is possible
|
||||
else
|
||||
{
|
||||
SetLocalObject(oAttacker, SPINE_1ST_HIT_DONE, oTarget);
|
||||
DelayCommand(fDelay1, DeleteLocalObject(oAttacker, SPINE_1ST_HIT_DONE));
|
||||
}
|
||||
}
|
||||
|
||||
int GetDamageFromConstant(int nIPConst)
|
||||
{
|
||||
// First, handle the values outside the main series
|
||||
switch(nIPConst)
|
||||
{
|
||||
case IP_CONST_MONSTERDAMAGE_1d2: return d2(1);
|
||||
case IP_CONST_MONSTERDAMAGE_1d3: return d3(1);
|
||||
case IP_CONST_MONSTERDAMAGE_1d4: return d4(1);
|
||||
case IP_CONST_MONSTERDAMAGE_2d4: return d4(2);
|
||||
case IP_CONST_MONSTERDAMAGE_3d4: return d4(3);
|
||||
case IP_CONST_MONSTERDAMAGE_4d4: return d4(4);
|
||||
case IP_CONST_MONSTERDAMAGE_5d4: return d4(5);
|
||||
case IP_CONST_MONSTERDAMAGE_7d4: return d4(7);
|
||||
}
|
||||
|
||||
|
||||
int nDieNum = ((nIPConst - 8) % 10) + 1;
|
||||
|
||||
switch((nIPConst - 8) / 10)
|
||||
{
|
||||
case 0: return d6(nDieNum);
|
||||
case 1: return d8(nDieNum);
|
||||
case 2: return d10(nDieNum);
|
||||
case 3: return d12(nDieNum);
|
||||
case 4: return d20(nDieNum);
|
||||
}
|
||||
|
||||
WriteTimestampedLogEntry("Unknown IP_CONST_MONSTERDAMAGE_* constant passed to GetDamageFromConstant()!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//:: void main (){}
|
||||
@@ -0,0 +1,384 @@
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Name: new spellbook spellgain / spell memorization include
|
||||
//:: File: inc_sp_gain_mem.nss
|
||||
//:://////////////////////////////////////////////
|
||||
/**
|
||||
contains helper functions for the two dynamic conversation scripts
|
||||
- prc_s_spellb.nss
|
||||
- prc_s_spellgain.nss
|
||||
that handle learning / gaining new spells at level up (prc_s_spellgain)
|
||||
or preparing what spells to learn at next rest (prc_s_spellb)
|
||||
|
||||
Author: motu99
|
||||
Created: May 1, 2008
|
||||
*/
|
||||
|
||||
//#include "prc_inc_core" //granted access via parent (inc_newspellbook)
|
||||
#include "prc_inc_switch"
|
||||
#include "inc_debug"
|
||||
|
||||
//:: Updated for .35 by Jaysyn 2023/03/11
|
||||
|
||||
//:: Test Void
|
||||
//void main (){}
|
||||
|
||||
//:://////////////////////////////////////////////
|
||||
//:: Constants
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
// max. number of classes a PC (or creature) can take (8 for NWN, 4 for NWN2)
|
||||
const int MAX_CLASSES = 8;
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Aid functions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
string GetNSBDefinitionFileName(int nClass);
|
||||
int GetCasterLevelByClass(int nClass, object oPC);
|
||||
|
||||
int GetSpellsKnown_MaxCount(int nCasterLevel, int nClass, int nSpellLevel, object oPC);
|
||||
int GetSpellsInClassSpellbook_Count(int nClass, int nSpellLevel);
|
||||
string GetClassString(int nClass);
|
||||
int GetMaxSpellLevelForCasterLevel(int nClass, int nCasterLevel);
|
||||
int GetMinSpellLevelForCasterLevel(int nClass, int nCasterLevel);
|
||||
|
||||
void WipeSpellFromHide(int nIPFeatID, object oPC);
|
||||
|
||||
string GetSpellsKnown_Array(int nClass, int nSpellLevel = -1);
|
||||
object GetSpellsOfClass_Token(int nClass, int nSpellLevel);
|
||||
string GetSpellsOfClass_Array();
|
||||
string GetSpellsMemorized_Array(int nClass);
|
||||
string GetSpellsToBeMemorized_Array(int nClass, int nSpellSlotLevel);
|
||||
|
||||
void array_set_size(object oPC, string sArrayName, int nSize);
|
||||
int array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0);
|
||||
int array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0);
|
||||
int persistant_array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0);
|
||||
int persistant_array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0);
|
||||
int array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0);
|
||||
int array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0);
|
||||
int persistant_array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0);
|
||||
int persistant_array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0);
|
||||
|
||||
string GetMetaMagicString_Short(int nMetaMagic);
|
||||
string GetMetaMagicString(int nMetaMagic);
|
||||
int GetMetaMagicFromFeat(int nFeat);
|
||||
int GetMetaMagicOfCaster(object oPC = OBJECT_SELF);
|
||||
|
||||
string DebugIProp2Str(itemproperty iprop);
|
||||
void DoDebug(string sString, object oAdditionalRecipient = OBJECT_INVALID);
|
||||
int GetSpellbookTypeForClass(int nClass);
|
||||
string GetFileForClass(int nClass);
|
||||
int GetSpellslotLevel(int nClass, object oPC);
|
||||
int GetSpellKnownMaxCount(int nCasterLevel, int nSpellLevel, int nClass, object oPC);
|
||||
int persistant_array_get_size(object store, string name);
|
||||
string Get2DACache(string s2DA, string sColumn, int nRow);
|
||||
object GetPCSkin(object oPC);
|
||||
void SetPersistantLocalInt(object oPC, string sName, int nValue);
|
||||
int GetPersistantLocalInt(object oPC, string sName);
|
||||
void SetPersistantLocalString(object oPC, string sName, string sValue);
|
||||
string GetPersistantLocalString(object oPC, string sName);
|
||||
|
||||
|
||||
|
||||
// name of the new spellbook file (cls_spell_*)
|
||||
string GetNSBDefinitionFileName(int nClass)
|
||||
{
|
||||
return GetFileForClass(nClass);
|
||||
}
|
||||
|
||||
// gets the caster level (without special modifications due to feats), by which the max spell slot level is determined
|
||||
int GetCasterLevelByClass(int nClass, object oPC)
|
||||
{
|
||||
return GetSpellslotLevel(nClass, oPC);
|
||||
// return GetPrCAdjustedCasterLevel(nClass, oPC, TRUE);
|
||||
}
|
||||
|
||||
// gets the maximum nr of spells that oPC can know with a given nCasterLevel, nClass and nSpellLevel
|
||||
int GetSpellsKnown_MaxCount(int nCasterLevel, int nClass, int nSpellLevel, object oPC)
|
||||
{
|
||||
return GetSpellKnownMaxCount(nCasterLevel, nSpellLevel, nClass, oPC);
|
||||
}
|
||||
|
||||
|
||||
// gets the total nr of spells available at nSpellLevel for nClass
|
||||
int GetSpellsInClassSpellbook_Count(int nClass, int nSpellLevel)
|
||||
{
|
||||
return persistant_array_get_size(GetSpellsOfClass_Token(nClass, nSpellLevel), GetSpellsOfClass_Array());
|
||||
}
|
||||
|
||||
string GetClassString(int nClass)
|
||||
{
|
||||
// get the name of the feats table 2da
|
||||
string sClass = Get2DACache("classes", "FeatsTable", nClass);
|
||||
// truncate the first 8 characters (the "cls_feat" part), leaving the "_<class>" part
|
||||
sClass = GetStringRight(sClass, GetStringLength(sClass) - 8);
|
||||
return sClass;
|
||||
}
|
||||
|
||||
// gets the maximum spell level that nClass can cast at nCasterLevel
|
||||
int GetMaxSpellLevelForCasterLevel(int nClass, int nCasterLevel)
|
||||
{
|
||||
string sFile;
|
||||
// Bioware casters use their classes.2da-specified tables
|
||||
//if(GetIsBioSpellCastClass(nClass))
|
||||
//{
|
||||
sFile = Get2DACache("classes", "SpellGainTable", nClass);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// sFile = "cls_spbk" + GetClassString(nClass);
|
||||
//}
|
||||
|
||||
// row nr in the files is nCasterLevel minus 1
|
||||
nCasterLevel--;
|
||||
int nSpellLevel;
|
||||
|
||||
if (Get2DACache(sFile, "NumSpellLevels", 9) != "")
|
||||
{
|
||||
string sTemp = Get2DACache(sFile, "NumSpellLevels", nCasterLevel);
|
||||
if (sTemp != "")
|
||||
{
|
||||
nSpellLevel = StringToInt(sTemp)-1;
|
||||
if (nSpellLevel <= 0) nSpellLevel = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (nSpellLevel=9; nSpellLevel >= 0; nSpellLevel--)
|
||||
{
|
||||
string sTemp = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nCasterLevel);
|
||||
if (sTemp != "")
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nSpellLevel;
|
||||
}
|
||||
|
||||
// gets the minimum spell level that nClass can cast at nCasterLevel
|
||||
int GetMinSpellLevelForCasterLevel(int nClass, int nCasterLevel)
|
||||
{
|
||||
string sFile;
|
||||
// Bioware casters use their classes.2da-specified tables
|
||||
//if(GetIsBioSpellCastClass(nClass))
|
||||
//{
|
||||
sFile = Get2DACache("classes", "SpellGainTable", nClass);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// sFile = "cls_spbk" + GetClassString(nClass);
|
||||
//}
|
||||
|
||||
// row nr in the files is nCasterLevel minus 1
|
||||
nCasterLevel--;
|
||||
|
||||
int bFound = 0;
|
||||
|
||||
int nSpellLevel;
|
||||
for (nSpellLevel=0; nSpellLevel <= 9; nSpellLevel++)
|
||||
{
|
||||
string sTemp = Get2DACache(sFile, "SpellLevel" + IntToString(nSpellLevel), nCasterLevel);
|
||||
if (sTemp != "")
|
||||
{
|
||||
bFound = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bFound) nSpellLevel = -1;
|
||||
return nSpellLevel;
|
||||
}
|
||||
|
||||
// wipes the IPbonusfeat from the hide
|
||||
void WipeSpellFromHide(int nIPFeatID, object oPC)
|
||||
{
|
||||
// go through all item properties on the hide
|
||||
object oHide = GetPCSkin(oPC);
|
||||
itemproperty ipTest = GetFirstItemProperty(oHide);
|
||||
while(GetIsItemPropertyValid(ipTest))
|
||||
{
|
||||
// is it a bonus feat?
|
||||
if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT)
|
||||
{
|
||||
// get the row nr of the bonus feat in iprp_feat.2da
|
||||
// is it the ipfeat to delete?
|
||||
if (GetItemPropertySubType(ipTest) == nIPFeatID)
|
||||
{
|
||||
RemoveItemProperty(oHide, ipTest);
|
||||
if(DEBUG) DoDebug("WipeSpellFromHide: Removing item property " + DebugIProp2Str(ipTest));
|
||||
}
|
||||
}
|
||||
ipTest = GetNextItemProperty(oHide);
|
||||
}
|
||||
}
|
||||
|
||||
// one array for each class (array holds all spell levels, but only non-metamagicked masterspells)
|
||||
string GetSpellsKnown_Array(int nClass, int nSpellLevel = -1)
|
||||
{
|
||||
int nSpellbookType = GetSpellbookTypeForClass(nClass);
|
||||
if(nSpellbookType == 1) //SPELLBOOK_TYPE_PREPARED
|
||||
return "Spellbook_Known_" + IntToString(nClass) + "_" + IntToString(nSpellLevel);
|
||||
return "Spellbook" + IntToString(nClass);
|
||||
}
|
||||
|
||||
// class spellbook (one storage token for each spell level and class)
|
||||
object GetSpellsOfClass_Token(int nClass, int nSpellLevel)
|
||||
{
|
||||
return GetObjectByTag("SpellLvl_" + IntToString(nClass) + "_Level_" + IntToString(nSpellLevel));
|
||||
}
|
||||
|
||||
string GetSpellsOfClass_Array()
|
||||
{
|
||||
return "Lkup";
|
||||
}
|
||||
|
||||
string GetSpellsMemorized_Array(int nClass)
|
||||
{
|
||||
return "NewSpellbookMem_" + IntToString(nClass);
|
||||
}
|
||||
|
||||
string GetSpellsToBeMemorized_Array(int nClass, int nSpellSlotLevel)
|
||||
{
|
||||
return "Spellbook" + IntToString(nSpellSlotLevel) + "_" + IntToString(nClass);
|
||||
}
|
||||
|
||||
|
||||
void array_set_size(object oPC, string sArrayName, int nSize)
|
||||
{
|
||||
SetPersistantLocalInt(oPC, sArrayName, nSize+1);
|
||||
}
|
||||
|
||||
int array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0)
|
||||
{
|
||||
// get array size, if size not already supplied
|
||||
if (nSize == 0) nSize = GetPersistantLocalInt(oPC, sArrayName) -1;
|
||||
int i;
|
||||
|
||||
for (i = nFirst; i < nSize; i++)
|
||||
{
|
||||
if (sValue == GetPersistantLocalString(oPC, sArrayName + "_" + IntToString(i)))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0)
|
||||
{
|
||||
// array values are stored as strings, so convert nValue to a string
|
||||
return array_has_string(oPC, sArrayName, IntToString(nValue), nFirst, nSize);
|
||||
}
|
||||
|
||||
int persistant_array_has_string(object oPC, string sArrayName, string sValue, int nFirst = 0, int nSize = 0)
|
||||
{
|
||||
return array_has_string(oPC, sArrayName, sValue, nFirst, nSize);
|
||||
}
|
||||
|
||||
int persistant_array_has_int(object oPC, string sArrayName, int nValue, int nFirst = 0, int nSize = 0)
|
||||
{
|
||||
return array_has_string(oPC, sArrayName, IntToString(nValue), nFirst, nSize);
|
||||
}
|
||||
|
||||
int array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0)
|
||||
{
|
||||
// get array size
|
||||
int nSize = GetPersistantLocalInt(oPC, sArrayName)-1;
|
||||
if (nSize <= nFirst) return -1;
|
||||
|
||||
// position of the first found; -1 if not found
|
||||
int nPos = array_has_string(oPC, sArrayName, sValue, nFirst, nSize);
|
||||
if (nPos < 0) return -1;
|
||||
|
||||
// Is is not the last element?
|
||||
if (nPos < nSize-1)
|
||||
{
|
||||
// then swap nPos (or rather nPos-1) with the last element (nSize-1)
|
||||
string sTemp = GetPersistantLocalString(oPC, sArrayName + "_" + IntToString(nSize-1));
|
||||
SetPersistantLocalString(oPC, sArrayName + "_" + IntToString(nPos), sTemp);
|
||||
}
|
||||
|
||||
// now decrement the array size (note that we already subtracted one in the beginning)
|
||||
SetPersistantLocalInt(oPC, sArrayName, nSize);
|
||||
|
||||
return nPos;
|
||||
}
|
||||
|
||||
// extracts the integer value nValue from a persistant sArray on oPC
|
||||
// extracts the first instance of nValue that it finds
|
||||
// extracts it by swapping the last array element to the position of the extracted element and reducing the array size by one
|
||||
// returns the position where the extracted element was found
|
||||
int array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0)
|
||||
{
|
||||
// array values are stored as strings, so convert nValue to a string
|
||||
return array_extract_string(oPC, sArrayName, IntToString(nValue), nFirst);
|
||||
}
|
||||
|
||||
int persistant_array_extract_string(object oPC, string sArrayName, string sValue, int nFirst = 0)
|
||||
{
|
||||
return array_extract_string(oPC, sArrayName, sValue, nFirst);
|
||||
}
|
||||
|
||||
int persistant_array_extract_int(object oPC, string sArrayName, int nValue, int nFirst = 0)
|
||||
{
|
||||
// array values are stored as strings, so convert nValue to a string
|
||||
return array_extract_string(oPC, sArrayName, IntToString(nValue), nFirst);
|
||||
}
|
||||
|
||||
string GetMetaMagicString_Short(int nMetaMagic)
|
||||
{
|
||||
string s;
|
||||
if (nMetaMagic == 0) return s;
|
||||
|
||||
if (nMetaMagic & METAMAGIC_EXTEND) s += "ext ";
|
||||
if (nMetaMagic & METAMAGIC_SILENT) s += "sil ";
|
||||
if (nMetaMagic & METAMAGIC_STILL) s += "sti ";
|
||||
if (nMetaMagic & METAMAGIC_EMPOWER) s += "emp ";
|
||||
if (nMetaMagic & METAMAGIC_MAXIMIZE) s += "max ";
|
||||
if (nMetaMagic & METAMAGIC_QUICKEN) s += "qui ";
|
||||
|
||||
return GetStringLeft(s, GetStringLength(s)-1);
|
||||
}
|
||||
|
||||
string GetMetaMagicString(int nMetaMagic)
|
||||
{
|
||||
string s;
|
||||
if (nMetaMagic == 0) return s;
|
||||
|
||||
if (nMetaMagic & METAMAGIC_EXTEND) s += "extend ";
|
||||
if (nMetaMagic & METAMAGIC_SILENT) s += "silent ";
|
||||
if (nMetaMagic & METAMAGIC_STILL) s += "still ";
|
||||
if (nMetaMagic & METAMAGIC_EMPOWER) s += "empower ";
|
||||
if (nMetaMagic & METAMAGIC_MAXIMIZE) s += "maximize ";
|
||||
if (nMetaMagic & METAMAGIC_QUICKEN) s += "quicken ";
|
||||
|
||||
return GetStringLeft(s, GetStringLength(s)-1);
|
||||
}
|
||||
|
||||
int GetMetaMagicFromFeat(int nFeat)
|
||||
{
|
||||
switch(nFeat)
|
||||
{
|
||||
case FEAT_EMPOWER_SPELL: return METAMAGIC_EMPOWER;
|
||||
case FEAT_EXTEND_SPELL: return METAMAGIC_EXTEND;
|
||||
case FEAT_MAXIMIZE_SPELL: return METAMAGIC_MAXIMIZE;
|
||||
case FEAT_QUICKEN_SPELL: return METAMAGIC_QUICKEN;
|
||||
case FEAT_SILENCE_SPELL: return METAMAGIC_SILENT;
|
||||
case FEAT_STILL_SPELL: return METAMAGIC_STILL;
|
||||
}
|
||||
return METAMAGIC_NONE;
|
||||
}
|
||||
|
||||
int GetMetaMagicOfCaster(object oPC = OBJECT_SELF)
|
||||
{
|
||||
int nMetaMagic;
|
||||
|
||||
if (GetHasFeat(FEAT_EMPOWER_SPELL, oPC)) nMetaMagic |= METAMAGIC_EMPOWER;
|
||||
if (GetHasFeat(FEAT_EXTEND_SPELL, oPC)) nMetaMagic |= METAMAGIC_EXTEND;
|
||||
if (GetHasFeat(FEAT_MAXIMIZE_SPELL, oPC)) nMetaMagic |= METAMAGIC_MAXIMIZE;
|
||||
if (GetHasFeat(FEAT_QUICKEN_SPELL, oPC)) nMetaMagic |= METAMAGIC_QUICKEN;
|
||||
if (GetHasFeat(FEAT_SILENCE_SPELL, oPC)) nMetaMagic |= METAMAGIC_SILENT;
|
||||
if (GetHasFeat(FEAT_STILL_SPELL, oPC)) nMetaMagic |= METAMAGIC_STILL;
|
||||
|
||||
return nMetaMagic;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,166 @@
|
||||
#include "prc_inc_clsfunc"
|
||||
|
||||
int SpellSneakAttackDamage(object oCaster, object oTarget);
|
||||
|
||||
int GetBlastDamageDices(object oInvoker, int nInvokerLevel)
|
||||
{
|
||||
int nDmgDice;
|
||||
if(nInvokerLevel < 13)
|
||||
nDmgDice = (nInvokerLevel + 1) / 2;
|
||||
else if(nInvokerLevel < 20)
|
||||
nDmgDice = (nInvokerLevel + 7) / 3;
|
||||
else
|
||||
nDmgDice = 9 + (nInvokerLevel - 20) / 2;
|
||||
|
||||
//check for the epic feats
|
||||
if(GetHasFeat(FEAT_EPIC_ELDRITCH_BLAST_I, oInvoker))
|
||||
{
|
||||
int nFeatAmt = 0;
|
||||
int bDone = FALSE;
|
||||
while(!bDone)
|
||||
{ if(nFeatAmt >= 9)
|
||||
bDone = TRUE;
|
||||
else if(GetHasFeat(FEAT_EPIC_ELDRITCH_BLAST_II + nFeatAmt, oInvoker))
|
||||
nFeatAmt++;
|
||||
else
|
||||
bDone = TRUE;
|
||||
}
|
||||
nDmgDice += nFeatAmt;
|
||||
}
|
||||
|
||||
return nDmgDice;
|
||||
}
|
||||
|
||||
// Spellblast should use only AoE spells but Dispel Magic can be cast as AoE or single target
|
||||
// we make sure here that we use AoE version
|
||||
int CheckSpecialTarget(int nSpellID)
|
||||
{
|
||||
return nSpellID == SPELL_DISPEL_MAGIC
|
||||
|| nSpellID == SPELL_GREATER_DISPELLING
|
||||
|| nSpellID == SPELL_LESSER_DISPEL
|
||||
|| nSpellID == SPELL_MORDENKAINENS_DISJUNCTION
|
||||
|| nSpellID == SPELL_POWER_WORD_KILL;
|
||||
}
|
||||
|
||||
void DoSpellBlast(object oPC, int bHit)
|
||||
{
|
||||
int nSpellbookID = GetLocalInt(oPC, "ET_SPELL_CURRENT");
|
||||
//DoDebug("nSpellbookID = "+IntToString(nSpellbookID));
|
||||
if(nSpellbookID)
|
||||
{
|
||||
object oTarget = GetSpellTargetObject();
|
||||
if(GetIsObjectValid(oTarget))
|
||||
{
|
||||
nSpellbookID--;
|
||||
DeleteLocalInt(oPC, "ET_SPELL_CURRENT");
|
||||
int nSpellID = GetLocalInt(oPC, "ET_REAL_SPELL_CURRENT");
|
||||
//DoDebug("nSpellID = "+IntToString(nSpellID));
|
||||
string sArray = GetLocalString(oPC, "ET_SPELL_CURRENT");
|
||||
//DoDebug("sArray = "+sArray);
|
||||
int nUses = sArray == "" ? GetHasSpell(nSpellbookID, oPC) :
|
||||
persistant_array_get_int(oPC, sArray, nSpellbookID);
|
||||
|
||||
if(nUses)
|
||||
{
|
||||
// expend spell use
|
||||
if(sArray == "")
|
||||
{
|
||||
DecrementRemainingSpellUses(oPC, nSpellID);
|
||||
}
|
||||
else
|
||||
{
|
||||
nUses--;
|
||||
persistant_array_set_int(oPC, sArray, nSpellbookID, nUses);
|
||||
}
|
||||
|
||||
// use AoE Dispel Magic
|
||||
int bTargetOverride = CheckSpecialTarget(nSpellID);
|
||||
|
||||
if(bHit)
|
||||
{
|
||||
int nCastingClass = GetETArcaneClass(oPC);
|
||||
int nDC = 10 + PRCGetSpellLevelForClass(nSpellID, nCastingClass) + GetDCAbilityModForClass(nCastingClass, oPC);
|
||||
//clear action queue to apply spell effect right after blast effect
|
||||
ClearAllActions();
|
||||
//override PRCDoMeleeTouchAttack() - we already know that blast hit
|
||||
ActionDoCommand(SetLocalInt(oPC, "AttackHasHit", bHit));
|
||||
SetLocalInt(oPC, "EldritchSpellBlast", TRUE);
|
||||
if(DEBUG) DoDebug("inv_inc_blast >> EldritchSpellBlast Set");
|
||||
ActionCastSpell(nSpellID, 0, nDC, 0, METAMAGIC_NONE, nCastingClass, FALSE, bTargetOverride);
|
||||
ActionDoCommand(DeleteLocalInt(oPC, "AttackHasHit"));
|
||||
DelayCommand(0.5, DeleteLocalInt(oPC, "EldritchSpellBlast"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyBlastDamage(object oCaster, object oTarget, int iAttackRoll, int iSR, int iDamage, int iDamageType, int iDamageType2, int nHellFire, int bSneak = TRUE, int nMsg = FALSE)
|
||||
{
|
||||
if (DEBUG) DoDebug("ApplyBlastDamage oCaster "+GetName(oCaster)+" oTarget "+GetName(oTarget)+" iAttackRoll "+IntToString(iAttackRoll)+" iSR "+IntToString(iSR)+" iDamage "+IntToString(iDamage)+" iDamageType "+IntToString(iDamageType)+" iDamageType2 "+IntToString(iDamageType2)+" nHellFire "+IntToString(nHellFire)+" bSneak "+IntToString(bSneak)+" nMsg "+IntToString(nMsg));
|
||||
|
||||
// Is it a critical hit?
|
||||
iDamage *= iAttackRoll;
|
||||
if(iAttackRoll)
|
||||
{
|
||||
// Heal the Undead
|
||||
if (iDamageType == DAMAGE_TYPE_NEGATIVE && (MyPRCGetRacialType(oTarget) == RACIAL_TYPE_UNDEAD || GetLocalInt(oTarget, "AcererakHealing") || (GetHasFeat(FEAT_TOMB_TAINTED_SOUL, oTarget) && GetAlignmentGoodEvil(oTarget) != ALIGNMENT_GOOD)))
|
||||
{
|
||||
//Set the heal effect
|
||||
effect eHeal = EffectHeal(iDamage);
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oTarget);
|
||||
}
|
||||
else // Other targets
|
||||
{
|
||||
if(!GetPRCSwitch(PRC_SPELL_SNEAK_DISABLE) && bSneak)
|
||||
iDamage += SpellSneakAttackDamage(oCaster, oTarget);
|
||||
|
||||
effect eDamage;
|
||||
if(!iSR)
|
||||
{
|
||||
if(iDamageType == iDamageType2)
|
||||
eDamage = EffectDamage(iDamage, iDamageType);
|
||||
else
|
||||
{
|
||||
eDamage = EffectDamage(iDamage / 2, iDamageType);
|
||||
eDamage = EffectLinkEffects(eDamage, EffectDamage(iDamage / 2, iDamageType2));
|
||||
}
|
||||
if(nHellFire)
|
||||
eDamage = EffectLinkEffects(eDamage, EffectDamage(d6(nHellFire), DAMAGE_TYPE_DIVINE));
|
||||
}
|
||||
else if(iDamageType == DAMAGE_TYPE_ACID || iDamageType2 == DAMAGE_TYPE_ACID)
|
||||
{
|
||||
if(iDamageType == iDamageType2)
|
||||
eDamage = EffectDamage(iDamage, iDamageType);
|
||||
else
|
||||
eDamage = EffectDamage(iDamage / 2, iDamageType);
|
||||
}
|
||||
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int HellFireConDamage(object oPC)
|
||||
{
|
||||
if(GetIsImmune(oPC, IMMUNITY_TYPE_ABILITY_DECREASE))
|
||||
{
|
||||
if(DEBUG) DoDebug("HellFireConDamage: Immune to ability damage!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ApplyAbilityDamage(oPC, ABILITY_CONSTITUTION, 1, DURATION_TYPE_TEMPORARY, TRUE, -1.0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int GetIsHellFireBlast(object oPC)
|
||||
{
|
||||
if(GetLocalInt(oPC, "INV_HELLFIRE"))
|
||||
{
|
||||
DeleteLocalInt(oPC, "INV_HELLFIRE");
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//:: void main (){}
|
||||
@@ -0,0 +1,531 @@
|
||||
//::///////////////////////////////////////////////
|
||||
//:: Invocation include: Invocations Known
|
||||
//:: inv_inc_invknown
|
||||
//::///////////////////////////////////////////////
|
||||
/** @file
|
||||
Defines functions for adding & removing
|
||||
Invocations known.
|
||||
|
||||
Data stored:
|
||||
|
||||
- For each Class list
|
||||
-- Total number of Invocations known
|
||||
-- A modifier value to maximum Invocations known on this list to account for feats and classes that add Invocations
|
||||
-- An array related to Invocations the knowledge of which is not dependent on character level
|
||||
--- Each array entry specifies the spells.2da row of the known Invocations's class-specific entry
|
||||
-- For each character level on which Invocations have been gained from this list
|
||||
--- An array of Invocations gained on this level
|
||||
---- Each array entry specifies the spells.2da row of the known Invocations's class-specific entry
|
||||
|
||||
@author Fox
|
||||
@date Created - 2008.01.25
|
||||
*/
|
||||
//:://////////////////////////////////////////////
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
// Included here to provide the values for the constants below
|
||||
#include "prc_class_const"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Constants */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const int INVOCATION_LIST_DRAGONFIRE_ADEPT = CLASS_TYPE_DRAGONFIRE_ADEPT;
|
||||
const int INVOCATION_LIST_WARLOCK = CLASS_TYPE_WARLOCK;
|
||||
const int INVOCATION_LIST_DRAGON_SHAMAN = CLASS_TYPE_DRAGON_SHAMAN;
|
||||
|
||||
/// Special Maneuver list. Maneuvers gained via Extra Invocation or other sources.
|
||||
const int INVOCATION_LIST_EXTRA = CLASS_TYPE_INVALID;//-1;
|
||||
const int INVOCATION_LIST_EXTRA_EPIC = /*CLASS_TYPE_INVALID - 1;*/-2; //needs a constant in there to compile properly
|
||||
|
||||
const string _INVOCATION_LIST_NAME_BASE = "PRC_InvocationList_";
|
||||
const string _INVOCATION_LIST_TOTAL_KNOWN = "_TotalKnown";
|
||||
const string _INVOCATION_LIST_MODIFIER = "_KnownModifier";
|
||||
const string _INVOCATION_LIST_EXTRA_ARRAY = "_InvocationsKnownExtraArray";
|
||||
const string _INVOCATION_LIST_EXTRA_EPIC_ARRAY = "_InvocationsKnownExtraEpicArray";
|
||||
const string _INVOCATION_LIST_LEVEL_ARRAY = "_InvocationsKnownLevelArray_";
|
||||
const string _INVOCATION_LIST_GENERAL_ARRAY = "_InvocationsKnownGeneralArray";
|
||||
|
||||
int GetPrimaryInvocationClass(object oCreature);
|
||||
int GetInvocationPRCLevels(object oCreature);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function prototypes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Gives the creature the control feats for the given Invocation and marks the Invocation
|
||||
* in a Invocations known array.
|
||||
* If the Invocation's data is already stored in one of the Invocations known arrays for
|
||||
* the list or adding the Invocation's data to the array fails, the function aborts.
|
||||
*
|
||||
* @param oCreature The creature to gain the Invocation
|
||||
* @param nList The list the Invocation comes from. One of INVOCATION_LIST_*
|
||||
* @param n2daRow The 2da row in the lists's 2da file that specifies the Invocation.
|
||||
* @param bLevelDependent If this is TRUE, the Invocation is tied to a certain level and can
|
||||
* be lost via level loss. If FALSE, the Invocation is not dependent
|
||||
* of a level and cannot be lost via level loss.
|
||||
* @param nLevelToTieTo If bLevelDependent is TRUE, this specifies the level the Invocation
|
||||
* is gained on. Otherwise, it's ignored.
|
||||
* The default value (-1) means that the current level of oCreature
|
||||
* will be used.
|
||||
*
|
||||
* @return TRUE if the Invocation was successfully stored and control feats added.
|
||||
* FALSE otherwise.
|
||||
*/
|
||||
int AddInvocationKnown(object oCreature, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1);
|
||||
|
||||
/**
|
||||
* Removes all Invocations gained from each list on the given level.
|
||||
*
|
||||
* @param oCreature The creature whose Invocations to remove
|
||||
* @param nLevel The level to clear
|
||||
*/
|
||||
void RemoveInvocationsKnownOnLevel(object oCreature, int nLevel);
|
||||
|
||||
/**
|
||||
* Gets the value of the Invocations known modifier, which is a value that is added
|
||||
* to the 2da-specified maximum Invocations known to determine the actual maximum.
|
||||
*
|
||||
* @param oCreature The creature whose modifier to get
|
||||
* @param nList The list the maximum Invocations known from which the modifier
|
||||
* modifies. One of INVOCATION_LIST_*
|
||||
*/
|
||||
int GetKnownInvocationsModifier(object oCreature, int nList);
|
||||
|
||||
/**
|
||||
* Sets the value of the Invocations known modifier, which is a value that is added
|
||||
* to the 2da-specified maximum Invocations known to determine the actual maximum.
|
||||
*
|
||||
* @param oCreature The creature whose modifier to set
|
||||
* @param nList The list the maximum Invocations known from which the modifier
|
||||
* modifies. One of INVOCATION_LIST_*
|
||||
*/
|
||||
void SetKnownInvocationsModifier(object oCreature, int nList, int nNewValue);
|
||||
|
||||
/**
|
||||
* Gets the number of Invocations a character character possesses from a
|
||||
* specific list and lexicon
|
||||
*
|
||||
* @param oCreature The creature whose Invocations to check
|
||||
* @param nList The list to check. One of INVOCATION_LIST_*
|
||||
* @return The number of Invocations known oCreature has from nList
|
||||
*/
|
||||
int GetInvocationCount(object oCreature, int nList);
|
||||
|
||||
/**
|
||||
* Gets the maximum number of Invocations a character may posses from a given list
|
||||
* at this time. Calculated based on class levels, feats and a misceallenous
|
||||
* modifier. There are three Types of Invocations, so it checks each seperately.
|
||||
*
|
||||
* @param oCreature Character to determine maximum Invocations for
|
||||
* @param nList INVOCATION_LIST_* of the list to determine maximum Invocations for
|
||||
* @return Maximum number of Invocations that oCreature may know from the given list.
|
||||
*/
|
||||
int GetMaxInvocationCount(object oCreature, int nList);
|
||||
|
||||
/**
|
||||
* Determines whether a character has a given Invocation, gained via some Invocation list.
|
||||
*
|
||||
* @param nInvocation INVOKE_* of the Invocation to test
|
||||
* @param oCreature Character to test for the possession of the Invocation
|
||||
* @return TRUE if the character has the Invocation, FALSE otherwise
|
||||
*/
|
||||
int GetHasInvocation(int nInvocation, object oCreature = OBJECT_SELF);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Includes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
#include "inc_item_props"
|
||||
#include "prc_x2_itemprop"
|
||||
#include "inc_lookups"
|
||||
#include "prc_inc_nwscript"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Internal functions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
void _InvocationRecurseRemoveArray(object oCreature, string sArrayName, string sInvocFile, int nArraySize, int nCurIndex)
|
||||
{
|
||||
if(DEBUG) DoDebug("_InvocationRecurseRemoveArray():\n"
|
||||
+ "oCreature = " + DebugObject2Str(oCreature) + "\n"
|
||||
+ "sArrayName = '" + sArrayName + "'\n"
|
||||
+ "sInvocFile = '" + sInvocFile + "'\n"
|
||||
+ "nArraySize = " + IntToString(nArraySize) + "\n"
|
||||
+ "nCurIndex = " + IntToString(nCurIndex) + "\n"
|
||||
);
|
||||
|
||||
// Determine whether we've already parsed the whole array or not
|
||||
if(nCurIndex >= nArraySize)
|
||||
{
|
||||
if(DEBUG) DoDebug("_InvocationRecurseRemoveArray(): Running itemproperty removal loop.");
|
||||
// Loop over itemproperties on the skin and remove each match
|
||||
object oSkin = GetPCSkin(oCreature);
|
||||
itemproperty ipTest = GetFirstItemProperty(oSkin);
|
||||
while(GetIsItemPropertyValid(ipTest))
|
||||
{
|
||||
// Check if the itemproperty is a bonus feat that has been marked for removal
|
||||
if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_BONUS_FEAT &&
|
||||
GetLocalInt(oCreature, "PRC_InvocFeatRemovalMarker_" + IntToString(GetItemPropertySubType(ipTest)))
|
||||
)
|
||||
{
|
||||
if(DEBUG) DoDebug("_InvocationRecurseRemoveArray(): Removing bonus feat itemproperty:\n" + DebugIProp2Str(ipTest));
|
||||
// If so, remove it
|
||||
RemoveItemProperty(oSkin, ipTest);
|
||||
}
|
||||
|
||||
ipTest = GetNextItemProperty(oSkin);
|
||||
}
|
||||
}
|
||||
// Still parsing the array
|
||||
else
|
||||
{
|
||||
// Set the marker
|
||||
string sName = "PRC_InvocFeatRemovalMarker_" + Get2DACache(sInvocFile, "IPFeatID",
|
||||
GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArrayName, nCurIndex))
|
||||
);
|
||||
if(DEBUG) DoDebug("_InvocationRecurseRemoveArray(): Recursing through array, marker set:\n" + sName);
|
||||
|
||||
SetLocalInt(oCreature, sName, TRUE);
|
||||
// Recurse to next array index
|
||||
_InvocationRecurseRemoveArray(oCreature, sArrayName, sInvocFile, nArraySize, nCurIndex + 1);
|
||||
// After returning, delete the local
|
||||
DeleteLocalInt(oCreature, sName);
|
||||
}
|
||||
}
|
||||
|
||||
void _RemoveInvocationArray(object oCreature, int nList, int nLevel)
|
||||
{
|
||||
if(DEBUG) DoDebug("_RemoveInvocationArray():\n"
|
||||
+ "oCreature = " + DebugObject2Str(oCreature) + "\n"
|
||||
+ "nList = " + IntToString(nList) + "\n"
|
||||
);
|
||||
|
||||
string sBase = _INVOCATION_LIST_NAME_BASE + IntToString(nList);
|
||||
string sArray = sBase + _INVOCATION_LIST_LEVEL_ARRAY + IntToString(nLevel);
|
||||
int nSize = persistant_array_get_size(oCreature, sArray);
|
||||
|
||||
// Reduce the total by the array size
|
||||
SetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN,
|
||||
GetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN) - nSize
|
||||
);
|
||||
|
||||
// Remove each Invocation in the array
|
||||
_InvocationRecurseRemoveArray(oCreature, sArray, GetAMSDefinitionFileName(nList), nSize, 0);
|
||||
|
||||
// Remove the array itself
|
||||
persistant_array_delete(oCreature, sArray);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function definitions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
int AddInvocationKnown(object oCreature, int nList, int n2daRow, int bLevelDependent = FALSE, int nLevelToTieTo = -1)
|
||||
{
|
||||
string sBase = _INVOCATION_LIST_NAME_BASE + IntToString(nList);
|
||||
string sArray = sBase;
|
||||
string sPowerFile = GetAMSDefinitionFileName(/*PowerListToClassType(*/nList/*)*/);
|
||||
if(nList == -2 || nList == CLASS_TYPE_INVALID)
|
||||
{
|
||||
sPowerFile = GetAMSDefinitionFileName(GetPrimaryInvocationClass(oCreature));
|
||||
}
|
||||
string sTestArray;
|
||||
int i, j, nSize, bReturn;
|
||||
|
||||
// Get the spells.2da row corresponding to the cls_psipw_*.2da row
|
||||
int nSpells2daRow = StringToInt(Get2DACache(sPowerFile, "SpellID", n2daRow));
|
||||
|
||||
// Determine the array name.
|
||||
if(bLevelDependent)
|
||||
{
|
||||
// If no level is specified, default to the creature's current level
|
||||
if(nLevelToTieTo == -1)
|
||||
nLevelToTieTo = GetHitDice(oCreature);
|
||||
|
||||
sArray += _INVOCATION_LIST_LEVEL_ARRAY + IntToString(nLevelToTieTo);
|
||||
}
|
||||
else
|
||||
{
|
||||
sArray += _INVOCATION_LIST_GENERAL_ARRAY;
|
||||
}
|
||||
|
||||
// Make sure the power isn't already in an array. If it is, abort and return FALSE
|
||||
// Loop over each level array and check that it isn't there.
|
||||
if(DEBUG) DoDebug("inv_inc_invknown: Checking first array set for duplicates.");
|
||||
for(i = 1; i <= GetHitDice(oCreature); i++)
|
||||
{
|
||||
sTestArray = sBase + _INVOCATION_LIST_LEVEL_ARRAY + IntToString(i);
|
||||
if(persistant_array_exists(oCreature, sTestArray))
|
||||
{
|
||||
nSize = persistant_array_get_size(oCreature, sTestArray);
|
||||
for(j = 0; j < nSize; j++)
|
||||
if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
// Check the non-level-dependent array
|
||||
if(DEBUG) DoDebug("inv_inc_invknown: Checking second array set for duplicates.");
|
||||
sTestArray = sBase + _INVOCATION_LIST_GENERAL_ARRAY;
|
||||
if(persistant_array_exists(oCreature, sTestArray))
|
||||
{
|
||||
nSize = persistant_array_get_size(oCreature, sTestArray);
|
||||
for(j = 0; j < nSize; j++)
|
||||
if(persistant_array_get_int(oCreature, sArray, j) == nSpells2daRow)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// All checks are made, now start adding the new power
|
||||
// Create the array if it doesn't exist yet
|
||||
if(!persistant_array_exists(oCreature, sArray))
|
||||
persistant_array_create(oCreature, sArray);
|
||||
|
||||
// Store the power in the array
|
||||
if(DEBUG) DoDebug("inv_inc_invknown: Adding to invocation array.");
|
||||
if(persistant_array_set_int(oCreature, sArray, persistant_array_get_size(oCreature, sArray), nSpells2daRow) != SDL_SUCCESS)
|
||||
{
|
||||
if(DEBUG) DoDebug("inv_inc_invknown: AddPowerKnown(): ERROR: Unable to add power to known array\n"
|
||||
+ "oCreature = " + DebugObject2Str(oCreature) + "\n"
|
||||
+ "nList = " + IntToString(nList) + "\n"
|
||||
+ "n2daRow = " + IntToString(n2daRow) + "\n"
|
||||
+ "bLevelDependent = " + DebugBool2String(bLevelDependent) + "\n"
|
||||
+ "nLevelToTieTo = " + IntToString(nLevelToTieTo) + "\n"
|
||||
+ "nSpells2daRow = " + IntToString(nSpells2daRow) + "\n"
|
||||
);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Increment Invocations known total
|
||||
SetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN,
|
||||
GetPersistantLocalInt(oCreature, sBase + _INVOCATION_LIST_TOTAL_KNOWN) + 1
|
||||
);
|
||||
|
||||
// Give the power's control feats
|
||||
object oSkin = GetPCSkin(oCreature);
|
||||
string sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID", n2daRow);
|
||||
itemproperty ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP));
|
||||
IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
||||
// Second power feat, if any
|
||||
sPowerFeatIP = Get2DACache(sPowerFile, "IPFeatID2", n2daRow);
|
||||
if(sPowerFeatIP != "")
|
||||
{
|
||||
ipFeat = PRCItemPropertyBonusFeat(StringToInt(sPowerFeatIP));
|
||||
IPSafeAddItemProperty(oSkin, ipFeat, 0.0f, X2_IP_ADDPROP_POLICY_KEEP_EXISTING, FALSE, FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void RemoveInvocationsKnownOnLevel(object oCreature, int nLevel)
|
||||
{
|
||||
if(DEBUG) DoDebug("inv_inc_invknown: RemoveInvocationKnownOnLevel():\n"
|
||||
+ "oCreature = " + DebugObject2Str(oCreature) + "\n"
|
||||
+ "nLevel = " + IntToString(nLevel) + "\n"
|
||||
);
|
||||
|
||||
string sPostFix = _INVOCATION_LIST_LEVEL_ARRAY + IntToString(nLevel);
|
||||
// For each Invocation list, determine if an array exists for this level.
|
||||
if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_DRAGONFIRE_ADEPT) + sPostFix))
|
||||
// If one does exist, clear it
|
||||
_RemoveInvocationArray(oCreature, INVOCATION_LIST_DRAGONFIRE_ADEPT, nLevel);
|
||||
|
||||
if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_WARLOCK) + sPostFix))
|
||||
_RemoveInvocationArray(oCreature, INVOCATION_LIST_WARLOCK, nLevel);
|
||||
|
||||
if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_DRAGON_SHAMAN) + sPostFix))
|
||||
_RemoveInvocationArray(oCreature, INVOCATION_LIST_DRAGON_SHAMAN, nLevel);
|
||||
|
||||
if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_EXTRA) + sPostFix))
|
||||
_RemoveInvocationArray(oCreature, INVOCATION_LIST_EXTRA, nLevel);
|
||||
|
||||
if(persistant_array_exists(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(INVOCATION_LIST_EXTRA_EPIC) + sPostFix))
|
||||
_RemoveInvocationArray(oCreature, INVOCATION_LIST_EXTRA_EPIC, nLevel);
|
||||
}
|
||||
|
||||
int GetKnownInvocationsModifier(object oCreature, int nList)
|
||||
{
|
||||
return GetPersistantLocalInt(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(nList) + _INVOCATION_LIST_MODIFIER);
|
||||
}
|
||||
|
||||
void SetKnownInvocationsModifier(object oCreature, int nList, int nNewValue)
|
||||
{
|
||||
SetPersistantLocalInt(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(nList) + _INVOCATION_LIST_MODIFIER, nNewValue);
|
||||
}
|
||||
|
||||
int GetInvocationCount(object oCreature, int nList)
|
||||
{
|
||||
return GetPersistantLocalInt(oCreature, _INVOCATION_LIST_NAME_BASE + IntToString(nList) + _INVOCATION_LIST_TOTAL_KNOWN);
|
||||
}
|
||||
|
||||
int GetMaxInvocationCount(object oCreature, int nList)
|
||||
{
|
||||
int nMaxInvocations = 0;
|
||||
|
||||
switch(nList)
|
||||
{
|
||||
case INVOCATION_LIST_DRAGONFIRE_ADEPT:{
|
||||
// Determine base Invocations known
|
||||
int nLevel = GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCreature);
|
||||
nLevel += GetPrimaryInvocationClass(oCreature) == CLASS_TYPE_DRAGONFIRE_ADEPT ? GetInvocationPRCLevels(oCreature) : 0;
|
||||
if(nLevel == 0)
|
||||
break;
|
||||
nMaxInvocations = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_DRAGONFIRE_ADEPT), "InvocationKnown", nLevel - 1));
|
||||
|
||||
// Calculate feats
|
||||
|
||||
// Add in the custom modifier
|
||||
nMaxInvocations += GetKnownInvocationsModifier(oCreature, nList);
|
||||
break;
|
||||
}
|
||||
|
||||
case INVOCATION_LIST_WARLOCK:{
|
||||
// Determine base Invocations known
|
||||
int nLevel = GetLevelByClass(CLASS_TYPE_WARLOCK, oCreature);
|
||||
nLevel += GetPrimaryInvocationClass(oCreature) == CLASS_TYPE_WARLOCK ? GetInvocationPRCLevels(oCreature) : 0;
|
||||
if(nLevel == 0)
|
||||
break;
|
||||
nMaxInvocations = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_WARLOCK), "InvocationKnown", nLevel - 1));
|
||||
|
||||
// Calculate feats
|
||||
|
||||
// Add in the custom modifier
|
||||
nMaxInvocations += GetKnownInvocationsModifier(oCreature, nList);
|
||||
break;
|
||||
}
|
||||
|
||||
case INVOCATION_LIST_DRAGON_SHAMAN:{
|
||||
// Determine base Invocations known
|
||||
int nLevel = GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oCreature);
|
||||
nLevel += GetPrimaryInvocationClass(oCreature) == CLASS_TYPE_DRAGON_SHAMAN ? GetInvocationPRCLevels(oCreature) : 0;
|
||||
if(nLevel == 0)
|
||||
break;
|
||||
nMaxInvocations = StringToInt(Get2DACache(GetAMSKnownFileName(CLASS_TYPE_DRAGON_SHAMAN), "InvocationKnown", nLevel - 1));
|
||||
|
||||
// Calculate feats
|
||||
|
||||
// Add in the custom modifier
|
||||
nMaxInvocations += GetKnownInvocationsModifier(oCreature, nList);
|
||||
break;
|
||||
}
|
||||
|
||||
case INVOCATION_LIST_EXTRA:
|
||||
nMaxInvocations = GetHasFeat(FEAT_EXTRA_INVOCATION_I, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_II, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_III, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_IV, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_V, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_VI, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_VII, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_VIII, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_IX, oCreature) +
|
||||
GetHasFeat(FEAT_EXTRA_INVOCATION_X, oCreature);
|
||||
break;
|
||||
|
||||
case INVOCATION_LIST_EXTRA_EPIC:
|
||||
nMaxInvocations = GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_I, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_II, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_III, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_IV, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_V, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_VI, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_VII, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_VIII, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_IX, oCreature) +
|
||||
GetHasFeat(FEAT_EPIC_EXTRA_INVOCATION_X, oCreature);
|
||||
break;
|
||||
|
||||
default:{
|
||||
string sErr = "GetMaxInvocationCount(): ERROR: Unknown power list value: " + IntToString(nList);
|
||||
if(DEBUG) DoDebug(sErr);
|
||||
else WriteTimestampedLogEntry(sErr);
|
||||
}
|
||||
}
|
||||
|
||||
return nMaxInvocations;
|
||||
}
|
||||
|
||||
int GetHasInvocation(int nInvocation, object oCreature = OBJECT_SELF)
|
||||
{
|
||||
if((GetLevelByClass(CLASS_TYPE_DRAGONFIRE_ADEPT, oCreature)
|
||||
&& GetHasFeat(GetClassFeatFromPower(nInvocation, CLASS_TYPE_DRAGONFIRE_ADEPT), oCreature)
|
||||
) ||
|
||||
(GetLevelByClass(CLASS_TYPE_WARLOCK, oCreature)
|
||||
&& GetHasFeat(GetClassFeatFromPower(nInvocation, CLASS_TYPE_WARLOCK), oCreature)
|
||||
) ||
|
||||
(GetLevelByClass(CLASS_TYPE_DRAGON_SHAMAN, oCreature)
|
||||
&& GetHasFeat(GetClassFeatFromPower(nInvocation, CLASS_TYPE_DRAGON_SHAMAN), oCreature)
|
||||
)
|
||||
// add new Invocation classes here
|
||||
)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
string DebugListKnownInvocations(object oCreature)
|
||||
{
|
||||
string sReturn = "Invocations known by " + DebugObject2Str(oCreature) + ":\n";
|
||||
int i, j, k, numPowerLists = 6;
|
||||
int nPowerList, nSize;
|
||||
string sTemp, sArray, sArrayBase, sPowerFile;
|
||||
// Loop over all power lists
|
||||
for(i = 1; i <= numPowerLists; i++)
|
||||
{
|
||||
// Some padding
|
||||
sReturn += " ";
|
||||
// Get the power list for this loop
|
||||
switch(i)
|
||||
{
|
||||
case 1: nPowerList = INVOCATION_LIST_DRAGONFIRE_ADEPT; sReturn += "Dragonfire Adept"; break;
|
||||
|
||||
case 2: nPowerList = INVOCATION_LIST_WARLOCK; sReturn += "Warlock"; break;
|
||||
case 3: nPowerList = INVOCATION_LIST_DRAGON_SHAMAN; sReturn += "Dragon Shaman"; break;
|
||||
|
||||
// This should always be last
|
||||
case 5: nPowerList = INVOCATION_LIST_EXTRA; sReturn += "Extra"; break;
|
||||
case 6: nPowerList = INVOCATION_LIST_EXTRA_EPIC; sReturn += "Epic Extra"; break;
|
||||
}
|
||||
sReturn += " Invocations known:\n";
|
||||
|
||||
// Determine if the character has any Invocations from this list
|
||||
sPowerFile = GetAMSDefinitionFileName(nPowerList);
|
||||
sArrayBase = _INVOCATION_LIST_NAME_BASE + IntToString(nPowerList);
|
||||
|
||||
// Loop over levels
|
||||
for(j = 1; j <= GetHitDice(oCreature); j++)
|
||||
{
|
||||
sArray = sArrayBase + _INVOCATION_LIST_LEVEL_ARRAY + IntToString(j);
|
||||
if(persistant_array_exists(oCreature, sArray))
|
||||
{
|
||||
sReturn += " Gained on level " + IntToString(j) + ":\n";
|
||||
nSize = persistant_array_get_size(oCreature, sArray);
|
||||
for(k = 0; k < nSize; k++)
|
||||
sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name",
|
||||
GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k))
|
||||
)
|
||||
)
|
||||
)
|
||||
+ "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Non-leveldependent Invocations
|
||||
sArray = sArrayBase + _INVOCATION_LIST_GENERAL_ARRAY;
|
||||
if(persistant_array_exists(oCreature, sArray))
|
||||
{
|
||||
sReturn += " Non-leveldependent:\n";
|
||||
nSize = persistant_array_get_size(oCreature, sArray);
|
||||
for(k = 0; k < nSize; k++)
|
||||
sReturn += " " + GetStringByStrRef(StringToInt(Get2DACache(sPowerFile, "Name",
|
||||
GetPowerfileIndexFromSpellID(persistant_array_get_int(oCreature, sArray, k))
|
||||
)
|
||||
)
|
||||
)
|
||||
+ "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return sReturn;
|
||||
}
|
||||
// Test main
|
||||
//void main(){}
|
||||
@@ -0,0 +1,582 @@
|
||||
//::///////////////////////////////////////////////
|
||||
//:: Invocation include: Casting
|
||||
//:: inv_inc_invoke
|
||||
//::///////////////////////////////////////////////
|
||||
/** @file
|
||||
Defines structures and functions for handling
|
||||
initiating a invocation
|
||||
|
||||
@author Fox
|
||||
@date Created - 2008.1.26
|
||||
@thanks to Ornedan for his work on Psionics upon which this is based.
|
||||
*/
|
||||
//:://////////////////////////////////////////////
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Constants */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
const string PRC_INVOKING_CLASS = "PRC_CurrentInvocation_InitiatingClass";
|
||||
const string PRC_INVOCATION_LEVEL = "PRC_CurrentInvocation_Level";
|
||||
const string INV_DEBUG_IGNORE_CONSTRAINTS = "INV_DEBUG_IGNORE_CONSTRAINTS";
|
||||
|
||||
/**
|
||||
* The variable in which the invocation token is stored. If no token exists,
|
||||
* the variable is set to point at the invoker itself. That way OBJECT_INVALID
|
||||
* means the variable is unitialised.
|
||||
*/
|
||||
//const string PRC_INVOCATION_TOKEN_VAR = "PRC_InvocationToken";
|
||||
//const string PRC_INVOCATION_TOKEN_NAME = "PRC_INVOKETOKEN";
|
||||
//const float PRC_INVOCATION_HB_DELAY = 0.5f;
|
||||
|
||||
|
||||
int GetInvokerLevel(object oInvoker = OBJECT_SELF, int nSpecificClass = CLASS_TYPE_INVALID, int bPracticedInvoker = TRUE);
|
||||
int GetInvocationLevel(object oInvoker);
|
||||
int GetInvokingClass(object oInvoker = OBJECT_SELF);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Structures */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* A structure that contains common data used during invocation.
|
||||
*/
|
||||
struct invocation{
|
||||
/* Generic stuff */
|
||||
/// The creature Truespeaking the Invocation
|
||||
object oInvoker;
|
||||
/// Whether the invocation is successful or not
|
||||
int bCanInvoke;
|
||||
/// The creature's invoker level in regards to this invocation
|
||||
int nInvokerLevel;
|
||||
/// The invocation's spell ID
|
||||
int nInvocationId;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function prototypes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Determines if the invocation that is currently being attempted to be TrueSpoken
|
||||
* can in fact be truespoken. Determines metainvocations used.
|
||||
*
|
||||
* @param oInvoker A creature attempting to truespeak a invocation at this moment.
|
||||
* @param oTarget The target of the invocation, if any. For pure Area of Effect.
|
||||
* invocations, this should be OBJECT_INVALID. Otherwise the main
|
||||
* target of the invocation as returned by PRCGetSpellTargetObject().
|
||||
*
|
||||
* @return A invocation structure that contains the data about whether
|
||||
* the invocation was successfully initiated and some other
|
||||
* commonly used data, like the PC's invoker level for this invocation.
|
||||
*/
|
||||
struct invocation EvaluateInvocation(object oInvoker, object oTarget);
|
||||
|
||||
/**
|
||||
* Causes OBJECT_SELF to use the given invocation.
|
||||
*
|
||||
* @param nInvocation The index of the invocation to use in spells.2da or an UTTER_*
|
||||
* @param nClass The index of the class to use the invocation as in classes.2da or a CLASS_TYPE_*
|
||||
* @param nLevelOverride An optional override to normal invoker level.
|
||||
* @param bInstant If true invocation will be used without casting animations (eldritch sculptor)
|
||||
* Default: 0, which means the parameter is ignored.
|
||||
*/
|
||||
void UseInvocation(int nInvocation, int nClass, int nLevelOverride = 0, int bInstant = FALSE);
|
||||
|
||||
/**
|
||||
* A debugging function. Takes a invocation structure and
|
||||
* makes a string describing the contents.
|
||||
*
|
||||
* @param move A set of invocation data
|
||||
* @return A string describing the contents of move
|
||||
*/
|
||||
string DebugInvocation2Str(struct invocation invoked);
|
||||
|
||||
/**
|
||||
* Stores a invocation structure as a set of local variables. If
|
||||
* a structure was already stored with the same name on the same object,
|
||||
* it is overwritten.
|
||||
*
|
||||
* @param oObject The object on which to store the structure
|
||||
* @param sName The name under which to store the structure
|
||||
* @param move The invocation structure to store
|
||||
*/
|
||||
void SetLocalInvocation(object oObject, string sName, struct invocation invoked);
|
||||
|
||||
/**
|
||||
* Retrieves a previously stored invocation structure. If no structure is stored
|
||||
* by the given name, the structure returned is empty.
|
||||
*
|
||||
* @param oObject The object from which to retrieve the structure
|
||||
* @param sName The name under which the structure is stored
|
||||
* @return The structure built from local variables stored on oObject under sName
|
||||
*/
|
||||
struct invocation GetLocalInvocation(object oObject, string sName);
|
||||
|
||||
/**
|
||||
* Deletes a stored invocation structure.
|
||||
*
|
||||
* @param oObject The object on which the structure is stored
|
||||
* @param sName The name under which the structure is stored
|
||||
*/
|
||||
void DeleteLocalInvocation(object oObject, string sName);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Includes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//#include "inv_inc_invfunc" //Access in parent
|
||||
#include "prc_spellf_inc"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Internal functions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/** Internal function.
|
||||
* Handles Spellfire absorption when a utterance is used on a friendly spellfire
|
||||
* user.
|
||||
*/
|
||||
struct invocation _DoInvocationSpellfireFriendlyAbsorption(struct invocation invoked, object oTarget)
|
||||
{
|
||||
if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") &&
|
||||
GetIsFriend(oTarget, invoked.oInvoker)
|
||||
)
|
||||
{
|
||||
if(CheckSpellfire(invoked.oInvoker, oTarget, TRUE))
|
||||
{
|
||||
PRCShowSpellResist(invoked.oInvoker, oTarget, SPELL_RESIST_MANTLE);
|
||||
invoked.bCanInvoke = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return invoked;
|
||||
}
|
||||
|
||||
/** Internal function.
|
||||
* Sets invocation-related local variables.
|
||||
*
|
||||
* @param oInvoker The creature currently casting invocation
|
||||
* @param nClass Invocation casting class constant
|
||||
* @param nLevel Invocation level
|
||||
*/
|
||||
void _SetInvocationVariables(object oInvoker, int nClass, int nLevel)
|
||||
{
|
||||
if (DEBUG) FloatingTextStringOnCreature(GetName(oInvoker)+" is a "+IntToString(nClass)+" at "+IntToString(nLevel)+" invocation level", oInvoker);
|
||||
SetLocalInt(oInvoker, PRC_INVOKING_CLASS, nClass + 1);
|
||||
SetLocalInt(oInvoker, PRC_INVOCATION_LEVEL, nLevel);
|
||||
}
|
||||
|
||||
|
||||
/** Internal function.
|
||||
* Deletes invocation-related local variables.
|
||||
*
|
||||
* @param oInvoker The creature currently initiating a invocation
|
||||
*/
|
||||
void _CleanInvocationVariables(object oInvoker)
|
||||
{
|
||||
DeleteLocalInt(oInvoker, PRC_INVOKING_CLASS);
|
||||
DeleteLocalInt(oInvoker, PRC_INVOCATION_LEVEL);
|
||||
}
|
||||
|
||||
/** Internal function.
|
||||
* Determines whether a invocation token exists. If one does, returns it.
|
||||
*
|
||||
* @param oInvoker A creature whose invocation token to get
|
||||
* @return The invocation token if it exists, OBJECT_INVALID otherwise.
|
||||
*/
|
||||
/*object _GetInvocationToken(object oInvoker)
|
||||
{
|
||||
object oInvokeToken = GetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR);
|
||||
|
||||
// If the token object is no longer valid, set the variable to point at invoker
|
||||
if(!GetIsObjectValid(oInvokeToken))
|
||||
{
|
||||
oInvokeToken = oInvoker;
|
||||
SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvokeToken);
|
||||
}
|
||||
|
||||
|
||||
// Check if there is no token
|
||||
if(oInvokeToken == oInvoker)
|
||||
oInvokeToken = OBJECT_INVALID;
|
||||
|
||||
return oInvokeToken;
|
||||
}*/
|
||||
|
||||
/** Internal function.
|
||||
* Destroys the given invocation token and sets the creature's invocation token variable
|
||||
* to point at itself.
|
||||
*
|
||||
* @param oInvoker The invoker whose token to destroy
|
||||
* @param oInvokeToken The token to destroy
|
||||
*/
|
||||
/*void _DestroyInvocationToken(object oInvoker, object oInvokeToken)
|
||||
{
|
||||
DestroyObject(oInvokeToken);
|
||||
SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvoker);
|
||||
}*/
|
||||
|
||||
/** Internal function.
|
||||
* Destroys the previous invocation token, if any, and creates a new one.
|
||||
*
|
||||
* @param oInvoker A creature for whom to create a invocation token
|
||||
* @return The newly created token
|
||||
*/
|
||||
/*object _CreateInvocationToken(object oInvoker)
|
||||
{
|
||||
object oInvokeToken = _GetInvocationToken(oInvoker);
|
||||
object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oInvoker);
|
||||
|
||||
// Delete any previous tokens
|
||||
if(GetIsObjectValid(oInvokeToken))
|
||||
_DestroyInvocationToken(oInvoker, oInvokeToken);
|
||||
|
||||
// Create new token and store a reference to it
|
||||
oInvokeToken = CreateItemOnObject(PRC_INVOCATION_TOKEN_NAME, oStore);
|
||||
SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvokeToken);
|
||||
|
||||
Assert(GetIsObjectValid(oInvokeToken), "GetIsObjectValid(oInvokeToken)", "ERROR: Unable to create invocation token! Store object: " + DebugObject2Str(oStore), "inv_inc_invoke", "_CreateInvocationToken()");
|
||||
|
||||
return oInvokeToken;
|
||||
}*/
|
||||
|
||||
/** Internal function.
|
||||
* Determines whether the given invoker is doing something that would
|
||||
* interrupt initiating a invocation or affected by an effect that would do
|
||||
* the same.
|
||||
*
|
||||
* @param oInvoker A creature on which _InvocationHB() is running
|
||||
* @return TRUE if the creature can continue initiating,
|
||||
* FALSE otherwise
|
||||
*/
|
||||
/*int _InvocationStateCheck(object oInvoker)
|
||||
{
|
||||
int nAction = GetCurrentAction(oInvoker);
|
||||
// If the current action is not among those that could either be used to truespeak the invocation or movement, the invocation fails
|
||||
if(!(nAction || ACTION_CASTSPELL || nAction == ACTION_INVALID ||
|
||||
nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT ||
|
||||
nAction || ACTION_USEOBJECT || nAction == ACTION_WAIT
|
||||
) )
|
||||
return FALSE;
|
||||
|
||||
// Affected by something that prevents one from initiating
|
||||
effect eTest = GetFirstEffect(oInvoker);
|
||||
int nEType;
|
||||
while(GetIsEffectValid(eTest))
|
||||
{
|
||||
nEType = GetEffectType(eTest);
|
||||
if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE ||
|
||||
nEType == EFFECT_TYPE_DAZED ||
|
||||
nEType == EFFECT_TYPE_PARALYZE ||
|
||||
nEType == EFFECT_TYPE_PETRIFY ||
|
||||
nEType == EFFECT_TYPE_SLEEP ||
|
||||
nEType == EFFECT_TYPE_STUNNED
|
||||
)
|
||||
return FALSE;
|
||||
|
||||
// Get next effect
|
||||
eTest = GetNextEffect(oInvoker);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}*/
|
||||
|
||||
/** Internal function.
|
||||
* Runs while the given creature is initiating. If they move, take other actions
|
||||
* that would cause them to interrupt initiating the invocation or are affected by an
|
||||
* effect that would cause such interruption, deletes the invocation token.
|
||||
* Stops if such condition occurs or something else destroys the token.
|
||||
*
|
||||
* @param oInvoker A creature initiating a invocation
|
||||
* @param lInvoker The location where the invoker was when starting the invocation
|
||||
* @param oInvokeToken The invocation token that controls the ongoing invocation
|
||||
*/
|
||||
/*void _InvocationHB(object oInvoker, location lInvoker, object oInvokeToken)
|
||||
{
|
||||
if(DEBUG) DoDebug("_InvocationHB() running:\n"
|
||||
+ "oInvoker = " + DebugObject2Str(oInvoker) + "\n"
|
||||
+ "lInvoker = " + DebugLocation2Str(lInvoker) + "\n"
|
||||
+ "oInvokeToken = " + DebugObject2Str(oInvokeToken) + "\n"
|
||||
+ "Distance between invocation start location and current location: " + FloatToString(GetDistanceBetweenLocations(lInvoker, GetLocation(oInvoker))) + "\n"
|
||||
);
|
||||
if(GetIsObjectValid(oInvokeToken))
|
||||
{
|
||||
// Continuance check
|
||||
if(GetDistanceBetweenLocations(lInvoker, GetLocation(oInvoker)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting
|
||||
!_InvocationStateCheck(oInvoker) // Action and effect check
|
||||
)
|
||||
{
|
||||
if(DEBUG) DoDebug("_InvocationHB(): invoker moved or lost concentration, destroying token");
|
||||
_DestroyInvocationToken(oInvoker, oInvokeToken);
|
||||
|
||||
// Inform invoker
|
||||
FloatingTextStrRefOnCreature(16832980, oInvoker, FALSE); // "You have lost concentration on the invocation you were attempting to cast!"
|
||||
}
|
||||
// Schedule next HB
|
||||
else
|
||||
DelayCommand(PRC_INVOCATION_HB_DELAY, _InvocationHB(oInvoker, lInvoker, oInvokeToken));
|
||||
}
|
||||
}*/
|
||||
|
||||
/** Internal function.
|
||||
* Checks if the invoker is in range to use the invocation they are trying to use.
|
||||
* If not, queues commands to make the invoker to run into range.
|
||||
*
|
||||
* @param oInvoker A creature initiating a invocation
|
||||
* @param nInvocation SpellID of the invocation being initiated
|
||||
* @param lTarget The target location or the location of the target object
|
||||
*/
|
||||
/*void _InvocationRangeCheck(object oInvoker, int nInvocation, location lTarget)
|
||||
{
|
||||
float fDistance = GetDistanceBetweenLocations(GetLocation(oInvoker), lTarget);
|
||||
float fRangeLimit;
|
||||
string sRange = Get2DACache("spells", "Range", nInvocation);
|
||||
|
||||
// Personal range invocations are always in range
|
||||
if(sRange == "P")
|
||||
return;
|
||||
// Ranges according to the CCG spells.2da page
|
||||
else if(sRange == "T")
|
||||
fRangeLimit = 2.25f;
|
||||
else if(sRange == "S")
|
||||
fRangeLimit = 8.0f;
|
||||
else if(sRange == "M")
|
||||
fRangeLimit = 20.0f;
|
||||
else if(sRange == "L")
|
||||
fRangeLimit = 40.0f;
|
||||
|
||||
// See if we are out of range
|
||||
if(fDistance > fRangeLimit)
|
||||
{
|
||||
// Create waypoint for the movement
|
||||
object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget);
|
||||
|
||||
// Move into range, with a bit of fudge-factor
|
||||
//ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f);
|
||||
|
||||
// CleanUp
|
||||
ActionDoCommand(DestroyObject(oWP));
|
||||
|
||||
// CleanUp, paranoia
|
||||
AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f)));
|
||||
}
|
||||
}*/
|
||||
|
||||
/** Internal function.
|
||||
* Assigns the fakecast command that is used to display the conjuration VFX when using an invocation.
|
||||
* Separated from UseInvocation() due to a bug with ActionFakeCastSpellAtObject(), which requires
|
||||
* use of ClearAllActions() to work around.
|
||||
* The problem is that if the target is an item on the ground, if the actor is out of spell
|
||||
* range when doing the fakecast, they will run on top of the item instead of to the edge of
|
||||
* the spell range. This only happens if there was a "real action" in the actor's action queue
|
||||
* immediately prior to the fakecast.
|
||||
*/
|
||||
/*void _AssignUseInvocationFakeCastCommands(object oInvoker, object oTarget, location lTarget, int nSpellID)
|
||||
{
|
||||
// Nuke actions to prevent the fakecast action from bugging
|
||||
ClearAllActions();
|
||||
|
||||
if(GetIsObjectValid(oTarget))
|
||||
ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT);
|
||||
else
|
||||
ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT);
|
||||
}*/
|
||||
|
||||
|
||||
/** Internal function.
|
||||
* Places the cheatcasting of the real invocation into the invoker's action queue.
|
||||
*/
|
||||
/*void _UseInvocationAux(object oInvoker, object oInvokeToken, int nSpellId,
|
||||
object oTarget, location lTarget,
|
||||
int nInvocation, int nClass, int nLevelOverride)
|
||||
{
|
||||
if(DEBUG) DoDebug("_UseInvocationAux() running:\n"
|
||||
+ "oInvoker = " + DebugObject2Str(oInvoker) + "\n"
|
||||
+ "oInvokeToken = " + DebugObject2Str(oInvokeToken) + "\n"
|
||||
+ "nSpellId = " + IntToString(nSpellId) + "\n"
|
||||
+ "oTarget = " + DebugObject2Str(oTarget) + "\n"
|
||||
+ "lTarget = " + DebugLocation2Str(lTarget) + "\n"
|
||||
+ "nInvocation = " + IntToString(nInvocation) + "\n"
|
||||
+ "nClass = " + IntToString(nClass) + "\n"
|
||||
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
|
||||
);
|
||||
|
||||
// Make sure nothing has interrupted this invocation
|
||||
if(GetIsObjectValid(oInvokeToken))
|
||||
{
|
||||
if(DEBUG) DoDebug("_UseInvocationAux(): Token was valid, queueing actual invocation");
|
||||
// Set the class to cast as
|
||||
SetLocalInt(oInvoker, PRC_INVOKING_CLASS, nClass + 1);
|
||||
|
||||
// Set the invocation's level
|
||||
SetLocalInt(oInvoker, PRC_INVOCATION_LEVEL, StringToInt(lookup_spell_innate(nSpellId)));
|
||||
|
||||
if(nLevelOverride != 0)
|
||||
AssignCommand(oInvoker, ActionDoCommand(SetLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride)));
|
||||
if(GetIsObjectValid(oTarget))
|
||||
AssignCommand(oInvoker, ActionCastSpellAtObject(nInvocation, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
|
||||
else
|
||||
AssignCommand(oInvoker, ActionCastSpellAtLocation(nInvocation, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
|
||||
if(nLevelOverride != 0)
|
||||
AssignCommand(oInvoker, ActionDoCommand(DeleteLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE)));
|
||||
|
||||
// Destroy the invocation token for this invocation
|
||||
_DestroyInvocationToken(oInvoker, oInvokeToken);
|
||||
}
|
||||
}*/
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function definitions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
struct invocation EvaluateInvocation(object oInvoker, object oTarget)
|
||||
{
|
||||
/* Get some data */
|
||||
int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS) : FALSE;
|
||||
// invoker-related stuff
|
||||
int nInvokerLevel = GetInvokerLevel(oInvoker);
|
||||
int nInvocationLevel = GetInvocationLevel(oInvoker);
|
||||
int nClass = GetInvokingClass(oInvoker);
|
||||
|
||||
/* Initialise the invocation structure */
|
||||
struct invocation invoked;
|
||||
invoked.oInvoker = oInvoker;
|
||||
invoked.bCanInvoke = TRUE; // Assume successfull invocation by default
|
||||
invoked.nInvokerLevel = nInvokerLevel;
|
||||
invoked.nInvocationId = PRCGetSpellId();
|
||||
|
||||
if (DEBUG) FloatingTextStringOnCreature(GetName(oInvoker)+" is a "+IntToString(nClass)+" casting invocation "+IntToString(invoked.nInvocationId)+", a "+IntToString(nInvocationLevel)+" level invocation, at "+IntToString(nInvokerLevel)+" invoker level", oInvoker);
|
||||
|
||||
// Skip doing anything if something has prevented a successful invocation already by this point
|
||||
//if(invoked.bCanInvoke)
|
||||
//{
|
||||
invoked = _DoInvocationSpellfireFriendlyAbsorption(invoked, oTarget);
|
||||
//}//end if
|
||||
|
||||
if(DEBUG) DoDebug("EvaluateInvocation(): Final result:\n" + DebugInvocation2Str(invoked));
|
||||
|
||||
// Initiate invocation-related variable CleanUp
|
||||
//DelayCommand(0.5f, _CleanInvocationVariables(oInvoker));
|
||||
|
||||
return invoked;
|
||||
}
|
||||
|
||||
void UseInvocation(int nInvocation, int nClass, int nLevelOverride = 0, int bInstant = FALSE)
|
||||
{
|
||||
if(nClass < 0)
|
||||
nClass = CLASS_TYPE_WARLOCK;
|
||||
object oInvoker = OBJECT_SELF;
|
||||
// object oSkin = GetPCSkin(oInvoker);
|
||||
// object oTarget = PRCGetSpellTargetObject();
|
||||
// object oInvokeToken;
|
||||
// location lTarget = PRCGetSpellTargetLocation();
|
||||
// int nSpellID = PRCGetSpellId();
|
||||
//int nInvocationDur = StringToInt(Get2DACache("spells", "ConjTime", nInvocation)) + StringToInt(Get2DACache("spells", "CastTime", nInvocation));
|
||||
// This is a test case to speed up the impact of the melee attacks, as PerformAttackRound takes the full 6 second.
|
||||
// int nInvocationDur = 0;
|
||||
|
||||
|
||||
// Setup invocation-related variables
|
||||
ActionDoCommand(_SetInvocationVariables(oInvoker, nClass, StringToInt(lookup_spell_innate(nInvocation))));
|
||||
|
||||
// Cast the actual invocation
|
||||
ActionCastSpell(nInvocation, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstant);
|
||||
|
||||
// Initiate invocation-related variable CleanUp
|
||||
ActionDoCommand(_CleanInvocationVariables(oInvoker));
|
||||
// Normally swift action invocations check
|
||||
/*if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nInvocation, nClass)) == "SWIFT_ACTION" && // The invocation is swift action to use
|
||||
TakeSwiftAction(oInvoker) // And the invoker can take a swift action now
|
||||
)
|
||||
{
|
||||
nInvocationDur = 0;
|
||||
}*/
|
||||
|
||||
/*if(DEBUG) DoDebug("UseInvocation(): invoker is " + DebugObject2Str(oInvoker) + "\n"
|
||||
+ "nInvocation = " + IntToString(nInvocation) + "\n"
|
||||
+ "nClass = " + IntToString(nClass) + "\n"
|
||||
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
|
||||
+ "invocation duration = " + IntToString(nInvocationDur) + "ms \n"
|
||||
//+ "Token exists = " + DebugBool2String(GetIsObjectValid(oInvokeToken))
|
||||
);*/
|
||||
|
||||
// Create the invocation token. Deletes any old tokens and cancels corresponding invocations as a side effect
|
||||
//oInvokeToken = _CreateInvocationToken(oInvoker);
|
||||
|
||||
/// @todo Hook to the invoker's OnDamaged event for the concentration checks to avoid losing the invocation
|
||||
|
||||
// Nuke action queue to prevent cheating with creative invocation stacking.
|
||||
// Probably not necessary anymore - Ornedan
|
||||
//if(DEBUG) SendMessageToPC(oInvoker, "Clearing all actions in preparation for second stage of the invocation.");
|
||||
//ClearAllActions();
|
||||
|
||||
// If out of range, move to range
|
||||
//_InvocationRangeCheck(oInvoker, nInvocation, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget);
|
||||
|
||||
// Start the invocation monitor HB
|
||||
//DelayCommand(IntToFloat(nInvocationDur), ActionDoCommand(_InvocationHB(oInvoker, GetLocation(oInvoker), oInvokeToken)));
|
||||
|
||||
// Assuming the spell isn't used as a swift action, fakecast for visuals
|
||||
/*if(nInvocationDur > 0)
|
||||
{
|
||||
// Hack. Workaround of a bug with the fakecast actions. See function comment for details
|
||||
ActionDoCommand(_AssignUseInvocationFakeCastCommands(oInvoker, oTarget, lTarget, nSpellID));
|
||||
}*/
|
||||
|
||||
// Action queue the function that will cheatcast the actual invocation
|
||||
//DelayCommand(IntToFloat(nInvocationDur), AssignCommand(oInvoker, ActionDoCommand(_UseInvocationAux(oInvoker, oInvokeToken, nSpellID, oTarget, lTarget, nInvocation, nClass, nLevelOverride))));
|
||||
}
|
||||
|
||||
string DebugInvocation2Str(struct invocation invoked)
|
||||
{
|
||||
string sRet;
|
||||
|
||||
sRet += "oInvoker = " + DebugObject2Str(invoked.oInvoker) + "\n";
|
||||
sRet += "bCanInvoke = " + DebugBool2String(invoked.bCanInvoke) + "\n";
|
||||
sRet += "nInvokerLevel = " + IntToString(invoked.nInvokerLevel);
|
||||
|
||||
return sRet;
|
||||
}
|
||||
|
||||
void SetLocalInvocation(object oObject, string sName, struct invocation invoked)
|
||||
{
|
||||
//SetLocal (oObject, sName + "_", );
|
||||
SetLocalObject(oObject, sName + "_oInvoker", invoked.oInvoker);
|
||||
|
||||
SetLocalInt(oObject, sName + "_bCanInvoke", invoked.bCanInvoke);
|
||||
SetLocalInt(oObject, sName + "_nInvokerLevel", invoked.nInvokerLevel);
|
||||
SetLocalInt(oObject, sName + "_nSpellID", invoked.nInvocationId);
|
||||
}
|
||||
|
||||
struct invocation GetLocalInvocation(object oObject, string sName)
|
||||
{
|
||||
struct invocation invoked;
|
||||
invoked.oInvoker = GetLocalObject(oObject, sName + "_oInvoker");
|
||||
|
||||
invoked.bCanInvoke = GetLocalInt(oObject, sName + "_bCanInvoke");
|
||||
invoked.nInvokerLevel = GetLocalInt(oObject, sName + "_nInvokerLevel");
|
||||
invoked.nInvocationId = GetLocalInt(oObject, sName + "_nSpellID");
|
||||
|
||||
return invoked;
|
||||
}
|
||||
|
||||
void DeleteLocalInvocation(object oObject, string sName)
|
||||
{
|
||||
DeleteLocalObject(oObject, sName + "_oInvoker");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_bCanInvoke");
|
||||
DeleteLocalInt(oObject, sName + "_nInvokerLevel");
|
||||
DeleteLocalInt(oObject, sName + "_nSpellID");
|
||||
}
|
||||
|
||||
void InvocationDebugIgnoreConstraints(object oInvoker)
|
||||
{
|
||||
SetLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS, TRUE);
|
||||
DelayCommand(0.0f, DeleteLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS));
|
||||
}
|
||||
|
||||
// Test main
|
||||
//void main(){}
|
||||
@@ -0,0 +1,446 @@
|
||||
//::///////////////////////////////////////////////
|
||||
//:: Spells include: Spell Penetration
|
||||
//:: prc_add_spl_pen
|
||||
//::///////////////////////////////////////////////
|
||||
/** @file
|
||||
Defines functions that may have something to do
|
||||
with modifying a spell's caster level in regards
|
||||
to Spell Resistance penetration.
|
||||
*/
|
||||
//:://////////////////////////////////////////////
|
||||
//:://////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Forward Declarations */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE);
|
||||
int GetIsElementalSpell(int nSpellID, int nDescriptor = -1);
|
||||
int ShadowWeave(object oCaster, int iSpellID, int nSpellSchool = -1);
|
||||
int HexToInt(string sHex);
|
||||
int GetIsDeniedDexBonusToAC(object oDefender, object oAttacker, int nIgnoreUD = FALSE);
|
||||
object PRCGetSpellTargetObject(object oCaster = OBJECT_SELF);
|
||||
int PRCGetSpellId(object oCaster = OBJECT_SELF);
|
||||
int GetSpellSchool(int iSpellId);
|
||||
int GetEssentiaInvested(object oMeldshaper, int nMeld = -1);
|
||||
int PRCGetCasterLevel(object oCaster = OBJECT_SELF);
|
||||
int GetEssentiaInvestedFeat(object oMeldshaper, int nFeat);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function prototypes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
int GetHeartWarderPene(int spell_id, object oCaster = OBJECT_SELF);
|
||||
|
||||
int ElementalSavantSP(int spell_id, object oCaster = OBJECT_SELF);
|
||||
|
||||
int RedWizardSP(int spell_id, int nSchool, object oCaster = OBJECT_SELF);
|
||||
|
||||
int GetSpellPenetreFocusSchool(int nSchool, object oCaster = OBJECT_SELF);
|
||||
|
||||
int GetSpellPowerBonus(object oCaster = OBJECT_SELF);
|
||||
|
||||
int ShadowWeavePen(int spell_id, int nSchool, object oCaster = OBJECT_SELF);
|
||||
|
||||
int KOTCSpellPenVsDemons(object oCaster, object oTarget);
|
||||
|
||||
int RunecasterRunePowerSP(object oCaster);
|
||||
|
||||
int MarshalDeterminedCaster(object oCaster);
|
||||
|
||||
int DuskbladeSpellPower(object oCaster, object oTarget);
|
||||
|
||||
int DraconicMagicPower(object oCaster);
|
||||
|
||||
int TrueCastingSpell(object oCaster);
|
||||
|
||||
string ChangedElementalType(int spell_id, object oCaster = OBJECT_SELF);
|
||||
|
||||
// Use this function to get the adjustments to a spell or SLAs spell penetration
|
||||
// from the various class effects
|
||||
// Update this function if any new classes change spell pentration
|
||||
int add_spl_pen(object oCaster = OBJECT_SELF);
|
||||
|
||||
int SPGetPenetr(object oCaster = OBJECT_SELF);
|
||||
|
||||
int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Includes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//#include "prc_inc_spells"
|
||||
//#include "prc_alterations"
|
||||
//#include "prcsp_archmaginc"
|
||||
//#include "prc_inc_racial"
|
||||
#include "inc_2dacache"
|
||||
#include "prc_feat_const"
|
||||
#include "prc_class_const"
|
||||
#include "prc_inc_descrptr"
|
||||
#include "prc_inc_racial"
|
||||
#include "prc_spell_const"
|
||||
#include "inv_invoc_const"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function definitions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Determine if a spell type is elemental
|
||||
//
|
||||
int IsSpellTypeElemental(string type)
|
||||
{
|
||||
return type == "Acid"
|
||||
|| type == "Cold"
|
||||
|| type == "Electricity"
|
||||
|| type == "Fire"
|
||||
|| type == "Sonic";
|
||||
}
|
||||
|
||||
int GetHeartWarderPene(int spell_id, object oCaster = OBJECT_SELF)
|
||||
{
|
||||
// Guard Expensive Calculations
|
||||
if(!GetHasFeat(FEAT_VOICE_SIREN, oCaster))
|
||||
return 0;
|
||||
|
||||
// Bonus Requires Verbal Spells
|
||||
string VS = GetStringLowerCase(Get2DACache("spells", "VS", spell_id));
|
||||
if(FindSubString(VS, "v") == -1)
|
||||
return 0;
|
||||
|
||||
// These feats provide greater bonuses or remove the Verbal requirement
|
||||
if(PRCGetMetaMagicFeat(oCaster, FALSE) & METAMAGIC_SILENT
|
||||
|| GetHasFeat(FEAT_SPELL_PENETRATION, oCaster)
|
||||
|| GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oCaster)
|
||||
|| GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oCaster))
|
||||
return 0;
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
//
|
||||
// Calculate Elemental Savant Contributions
|
||||
//
|
||||
int ElementalSavantSP(int spell_id, object oCaster = OBJECT_SELF)
|
||||
{
|
||||
// get spell elemental type
|
||||
int element = GetIsElementalSpell(spell_id);
|
||||
|
||||
//not an elemental spell
|
||||
if(!element)
|
||||
return 0;
|
||||
|
||||
int nSP = 0;
|
||||
|
||||
// All Elemental Savants will have this feat
|
||||
// when they first gain a penetration bonus.
|
||||
// Otherwise this would require checking ~4 items (class or specific feats)
|
||||
if(GetHasFeat(FEAT_ES_PEN_1, oCaster))
|
||||
{
|
||||
int feat, nES;
|
||||
nES = GetLevelByClass(CLASS_TYPE_ELEMENTAL_SAVANT, oCaster);
|
||||
|
||||
// Specify the elemental type rather than lookup by class?
|
||||
if(element & DESCRIPTOR_FIRE)
|
||||
{
|
||||
feat = FEAT_ES_FIRE;
|
||||
}
|
||||
else if(element & DESCRIPTOR_COLD)
|
||||
{
|
||||
feat = FEAT_ES_COLD;
|
||||
}
|
||||
else if(element & DESCRIPTOR_ELECTRICITY)
|
||||
{
|
||||
feat = FEAT_ES_ELEC;
|
||||
}
|
||||
else if(element & DESCRIPTOR_ACID)
|
||||
{
|
||||
feat = FEAT_ES_ACID;
|
||||
}
|
||||
|
||||
// Now determine the bonus
|
||||
if(feat && GetHasFeat(feat, oCaster))
|
||||
nSP = nES / 3;
|
||||
}
|
||||
// SendMessageToPC(GetFirstPC(), "Your Elemental Penetration modifier is " + IntToString(nSP));
|
||||
return nSP;
|
||||
}
|
||||
|
||||
//Red Wizard SP boost based on spell school specialization
|
||||
int RedWizardSP(int spell_id, int nSchool, object oCaster = OBJECT_SELF)
|
||||
{
|
||||
int iRedWizard = GetLevelByClass(CLASS_TYPE_RED_WIZARD, oCaster);
|
||||
int nSP;
|
||||
|
||||
if(iRedWizard)
|
||||
{
|
||||
int iRWSpec;
|
||||
switch(nSchool)
|
||||
{
|
||||
case SPELL_SCHOOL_ABJURATION: iRWSpec = FEAT_RW_TF_ABJ; break;
|
||||
case SPELL_SCHOOL_CONJURATION: iRWSpec = FEAT_RW_TF_CON; break;
|
||||
case SPELL_SCHOOL_DIVINATION: iRWSpec = FEAT_RW_TF_DIV; break;
|
||||
case SPELL_SCHOOL_ENCHANTMENT: iRWSpec = FEAT_RW_TF_ENC; break;
|
||||
case SPELL_SCHOOL_EVOCATION: iRWSpec = FEAT_RW_TF_EVO; break;
|
||||
case SPELL_SCHOOL_ILLUSION: iRWSpec = FEAT_RW_TF_ILL; break;
|
||||
case SPELL_SCHOOL_NECROMANCY: iRWSpec = FEAT_RW_TF_NEC; break;
|
||||
case SPELL_SCHOOL_TRANSMUTATION: iRWSpec = FEAT_RW_TF_TRS; break;
|
||||
}
|
||||
|
||||
if(iRWSpec && GetHasFeat(iRWSpec, oCaster))
|
||||
nSP = (iRedWizard / 2) + 1;
|
||||
}
|
||||
// SendMessageToPC(GetFirstPC(), "Your Spell Power modifier is " + IntToString(nSP));
|
||||
return nSP;
|
||||
}
|
||||
|
||||
int GetSpellPenetreFocusSchool(int nSchool, object oCaster = OBJECT_SELF)
|
||||
{
|
||||
if(nSchool)
|
||||
{
|
||||
if(GetHasFeat(FEAT_FOCUSED_SPELL_PENETRATION_ABJURATION+nSchool-1, oCaster))
|
||||
return 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetSpellPowerBonus(object oCaster = OBJECT_SELF)
|
||||
{
|
||||
if(GetHasFeat(FEAT_SPELLPOWER_10, oCaster))
|
||||
return 10;
|
||||
else if(GetHasFeat(FEAT_SPELLPOWER_8, oCaster))
|
||||
return 8;
|
||||
else if(GetHasFeat(FEAT_SPELLPOWER_6, oCaster))
|
||||
return 6;
|
||||
else if(GetHasFeat(FEAT_SPELLPOWER_4, oCaster))
|
||||
return 4;
|
||||
else if(GetHasFeat(FEAT_SPELLPOWER_2, oCaster))
|
||||
return 2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Shadow Weave Feat
|
||||
// +1 caster level vs SR (school Ench,Illu,Necro)
|
||||
int ShadowWeavePen(int spell_id, int nSchool, object oCaster = OBJECT_SELF)
|
||||
{
|
||||
int iShadow = GetLevelByClass(CLASS_TYPE_SHADOW_ADEPT, oCaster);
|
||||
int nSP;
|
||||
|
||||
// Apply changes if the caster has level in Shadow Adept class
|
||||
// and this spell is eligible for the spell penetration check increase
|
||||
if (iShadow > 0 && ShadowWeave(oCaster, spell_id, nSchool) == 1)
|
||||
// Shadow Spell Power
|
||||
nSP = iShadow / 3;
|
||||
|
||||
return nSP;
|
||||
}
|
||||
|
||||
int KOTCSpellPenVsDemons(object oCaster, object oTarget)
|
||||
{
|
||||
if(GetLevelByClass(CLASS_TYPE_KNIGHT_CHALICE, oCaster) >= 1)
|
||||
{
|
||||
if(MyPRCGetRacialType(oTarget) == RACIAL_TYPE_OUTSIDER)
|
||||
{
|
||||
if(GetAlignmentGoodEvil(oTarget) == ALIGNMENT_EVIL)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunecasterRunePowerSP(object oCaster)
|
||||
{
|
||||
int nSP = 0;
|
||||
|
||||
// casting from a rune
|
||||
if(GetResRef(GetSpellCastItem()) == "prc_rune_1")
|
||||
{
|
||||
nSP = StringToInt(GetTag(GetSpellCastItem()));
|
||||
}
|
||||
// caster is runechanting
|
||||
else if(GetHasSpellEffect(SPELL_RUNE_CHANT))
|
||||
{
|
||||
int nClass = GetLevelByClass(CLASS_TYPE_RUNECASTER, oCaster);
|
||||
|
||||
if (nClass >= 30) nSP = 10;
|
||||
else if (nClass >= 27) nSP = 9;
|
||||
else if (nClass >= 24) nSP = 8;
|
||||
else if (nClass >= 21) nSP = 7;
|
||||
else if (nClass >= 18) nSP = 6;
|
||||
else if (nClass >= 15) nSP = 5;
|
||||
else if (nClass >= 12) nSP = 4;
|
||||
else if (nClass >= 9) nSP = 3;
|
||||
else if (nClass >= 5) nSP = 2;
|
||||
else if (nClass >= 2) nSP = 1;
|
||||
}
|
||||
|
||||
return nSP;
|
||||
}
|
||||
|
||||
int MarshalDeterminedCaster(object oCaster)
|
||||
{
|
||||
return GetLocalInt(oCaster,"Marshal_DetCast");
|
||||
}
|
||||
|
||||
int DuskbladeSpellPower(object oCaster, object oTarget)
|
||||
{
|
||||
int nSP = 0;
|
||||
if(GetLocalInt(oTarget, "DuskbladeSpellPower"))
|
||||
{
|
||||
int nClass = GetLevelByClass(CLASS_TYPE_DUSKBLADE, oCaster);
|
||||
|
||||
if(nClass >= 38) nSP = 10;
|
||||
else if(nClass >= 36) nSP = 9;
|
||||
else if(nClass >= 31) nSP = 8;
|
||||
else if(nClass >= 26) nSP = 7;
|
||||
else if(nClass >= 21) nSP = 6;
|
||||
else if(nClass >= 18) nSP = 5;
|
||||
else if(nClass >= 16) nSP = 4;
|
||||
else if(nClass >= 11) nSP = 3;
|
||||
else if(nClass >= 6) nSP = 2;
|
||||
}
|
||||
|
||||
return nSP;
|
||||
}
|
||||
|
||||
int DraconicMagicPower(object oCaster)
|
||||
{
|
||||
return GetLocalInt(oCaster,"MagicPowerAura");
|
||||
}
|
||||
|
||||
int TrueCastingSpell(object oCaster)
|
||||
{
|
||||
if(GetHasSpellEffect(SPELL_TRUE_CASTING, oCaster))
|
||||
return 10;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Beguilers of level 8+ gain +2 bonus to SR agianst enemis that are denided DEX bonus to AC
|
||||
int CloakedCastingSR(object oCaster, object oTarget)
|
||||
{
|
||||
if(GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster) >= 8)
|
||||
{
|
||||
if(GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE))
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PenetratingBlast(object oCaster, object oTarget)
|
||||
{
|
||||
if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET"))
|
||||
{
|
||||
if(GetLocalInt(oCaster, "BlastEssence") == INVOKE_PENETRATING_BLAST)
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add_spl_pen(object oCaster = OBJECT_SELF)
|
||||
{
|
||||
object oTarget = PRCGetSpellTargetObject();
|
||||
int spell_id = PRCGetSpellId();
|
||||
int nSchool = GetSpellSchool(spell_id);
|
||||
|
||||
int nSP = ElementalSavantSP(spell_id, oCaster);
|
||||
nSP += GetHeartWarderPene(spell_id, oCaster);
|
||||
nSP += RedWizardSP(spell_id, nSchool, oCaster);
|
||||
nSP += GetSpellPowerBonus(oCaster);
|
||||
nSP += GetSpellPenetreFocusSchool(nSchool, oCaster);
|
||||
nSP += ShadowWeavePen(spell_id, nSchool, oCaster);
|
||||
nSP += RunecasterRunePowerSP(oCaster);
|
||||
nSP += MarshalDeterminedCaster(oCaster);
|
||||
nSP += DraconicMagicPower(oCaster);
|
||||
nSP += TrueCastingSpell(oCaster);
|
||||
nSP += GetEssentiaInvestedFeat(oCaster, FEAT_SOULTOUCHED_SPELLCASTING);
|
||||
if(GetIsObjectValid(oTarget))
|
||||
{
|
||||
nSP += CloakedCastingSR(oCaster, oTarget);
|
||||
nSP += PenetratingBlast(oCaster, oTarget);
|
||||
nSP += KOTCSpellPenVsDemons(oCaster, oTarget);
|
||||
nSP += DuskbladeSpellPower(oCaster, oTarget);
|
||||
}
|
||||
|
||||
return nSP;
|
||||
}
|
||||
|
||||
//
|
||||
// This function converts elemental types as needed
|
||||
//
|
||||
string ChangedElementalType(int spell_id, object oCaster = OBJECT_SELF)
|
||||
{
|
||||
// Lookup the spell type
|
||||
string spellType = Get2DACache("spells", "ImmunityType", spell_id);//lookup_spell_type(spell_id);
|
||||
|
||||
// Check if an override is set
|
||||
string sType = GetLocalString(oCaster, "archmage_mastery_elements_name");
|
||||
|
||||
// If so, check if the spell qualifies for a change
|
||||
if (sType == "" || !IsSpellTypeElemental(spellType))
|
||||
sType = spellType;
|
||||
|
||||
return sType;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function definitions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// Get the Spell Penetration Bonuses
|
||||
//
|
||||
int SPGetPenetr(object oCaster = OBJECT_SELF)
|
||||
{
|
||||
int nPenetr = 0;
|
||||
|
||||
// This is a deliberate optimization attempt.
|
||||
// The first feat determines if the others even need
|
||||
// to be referenced.
|
||||
if(GetHasFeat(FEAT_SPELL_PENETRATION, oCaster))
|
||||
{
|
||||
nPenetr += 2;
|
||||
if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION, oCaster))
|
||||
nPenetr += 4;
|
||||
else if (GetHasFeat(FEAT_GREATER_SPELL_PENETRATION, oCaster))
|
||||
nPenetr += 2;
|
||||
}
|
||||
|
||||
// Check for additional improvements
|
||||
nPenetr += add_spl_pen(oCaster);
|
||||
|
||||
return nPenetr;
|
||||
}
|
||||
|
||||
//
|
||||
// Interface for specific AOE requirements
|
||||
// TODO: Determine who or what removes the cached local var (bug?)
|
||||
// TODO: Try and remove this function completely? It does 2 things the
|
||||
// above function doesnt: Effective Caster Level and Cache
|
||||
//
|
||||
int SPGetPenetrAOE(object oCaster = OBJECT_SELF, int nCasterLvl = 0)
|
||||
{
|
||||
// Check the cache
|
||||
int nPenetr = GetLocalInt(OBJECT_SELF, "nPenetre");
|
||||
|
||||
// Compute the result
|
||||
if (!nPenetr) {
|
||||
nPenetr = (nCasterLvl) ? nCasterLvl : PRCGetCasterLevel(oCaster);
|
||||
|
||||
// Factor in Penetration Bonuses
|
||||
nPenetr += SPGetPenetr(oCaster);
|
||||
|
||||
// Who removed this?
|
||||
SetLocalInt(OBJECT_SELF,"nPenetre",nPenetr);
|
||||
}
|
||||
|
||||
return nPenetr;
|
||||
}
|
||||
|
||||
// Test main
|
||||
//void main(){}
|
||||
@@ -0,0 +1,766 @@
|
||||
/* Core functions taken from high up the branch
|
||||
which are needed lower. */
|
||||
|
||||
//:: Updated for .35 by Jaysyn 2023/03/10
|
||||
//////////////////////////////////////////////////
|
||||
/* Forward Declarations */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
int GetSpellLevel(int nSpell, int nClass);
|
||||
string GetFileForClass(int nClass);
|
||||
int GetSpellbookTypeForClass(int nClass);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function Prototypes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//:: Returns true if oCaster's race can naturally cast sorcerer spells.
|
||||
int GetIsRHDSorcerer(object oCaster = OBJECT_SELF);
|
||||
|
||||
//:: Returns true if oCaster's race can naturally cast bard spells.
|
||||
int GetIsRHDBard(object oCaster = OBJECT_SELF);
|
||||
|
||||
// wrapper for getspelltargetlocation
|
||||
location PRCGetSpellTargetLocation(object oCaster = OBJECT_SELF);
|
||||
|
||||
// Avoids adding passive spellcasting to the character's action queue by
|
||||
// creating an object specifically to cast the spell on the character.
|
||||
//
|
||||
// NOTE: The spell script must refer to the PC as PRCGetSpellTargetObject()
|
||||
// otherwise this function WILL NOT WORK. Do not make any assumptions
|
||||
// about the PC being OBJECT_SELF.
|
||||
void ActionCastSpellOnSelf(int iSpell, int nMetaMagic = METAMAGIC_NONE, object oTarget = OBJECT_SELF);
|
||||
|
||||
// This is a wrapper function that causes OBJECT_SELF to fire the defined spell
|
||||
// at the defined level. The target is automatically the object or location
|
||||
// that the user selects. Useful for SLA's to perform the casting of a true
|
||||
// spell. This is useful because:
|
||||
//
|
||||
// 1) If the original's spell script is updated, so is this one.
|
||||
// 2) The spells are identified as the true spell. That is, they ARE the true spell.
|
||||
// 3) Spellhooks (such as item crafting) that can only identify true spells
|
||||
// will easily work.
|
||||
//
|
||||
// This function should only be used when SLA's are meant to simulate true
|
||||
// spellcasting abilities, such as those seen when using feats with subradials
|
||||
// to simulate spellbooks.
|
||||
void ActionCastSpell(int iSpell, int iCasterLev = 0, int iBaseDC = 0, int iTotalDC = 0,
|
||||
int nMetaMagic = METAMAGIC_NONE, int nClass = CLASS_TYPE_INVALID,
|
||||
int bUseOverrideTargetLocation=FALSE, int bUseOverrideTargetObject=FALSE,
|
||||
object oOverrideTarget=OBJECT_INVALID, int bInstantCast=TRUE, int bUseOverrideMetaMagic=FALSE);
|
||||
|
||||
/**
|
||||
* Checks whether the given creature is committing an action, or
|
||||
* under such effects that cause a breach of concentration.
|
||||
*
|
||||
* @param oConcentrator The creature to test
|
||||
* @return TRUE if concentration is broken, FALSE otherwise
|
||||
*/
|
||||
int GetBreakConcentrationCheck(object oConcentrator);
|
||||
|
||||
/**
|
||||
* Checks for breaks in concentration for an ongoing effect, and removes
|
||||
* the effect if concentration is broken.
|
||||
*
|
||||
* @param oCaster The creature who cast the effect
|
||||
* @param SpellID The id of the spell the effect belongs to
|
||||
* @param oTarget The creature or object that is the target of the effect
|
||||
* @param nDuration The duration the effect lasts in seconds.
|
||||
*/
|
||||
void CheckConcentrationOnEffect(object oCaster, int SpellID, object oTarget, int nDuration);
|
||||
|
||||
// gets the spell level adjustment to the nMetaMagic, including boni from the Improved Metamagic (epic) feat
|
||||
int GetMetaMagicSpellLevelAdjustment(int nMetaMagic);
|
||||
|
||||
// Returns true if a spellcaster
|
||||
int GetIsBioSpellCastClass(int nClass);
|
||||
|
||||
// Returns true for spell casters with spellbooks
|
||||
int GetIsNSBClass(int nClass);
|
||||
|
||||
// returns the spelllevel of nSpell as it can be cast by oCreature
|
||||
int PRCGetSpellLevel(object oCreature, int nSpell);
|
||||
|
||||
// returns if a character should be using the newspellbook when casting
|
||||
int UseNewSpellBook(object oCreature);
|
||||
|
||||
// wrapper for GetHasSpell, works for newspellbook 'fake' spells too
|
||||
// should return 0 if called with a normal spell when a character should be using the newspellbook
|
||||
int PRCGetHasSpell(int nRealSpellID, object oCreature = OBJECT_SELF);
|
||||
|
||||
// checks if oPC knows the specified spell
|
||||
// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters
|
||||
int PRCGetIsRealSpellKnown(int nRealSpellID, object oPC = OBJECT_SELF);
|
||||
|
||||
// checks if oPC knows the specified spell
|
||||
// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters
|
||||
// this will only check the spellbook of the class specified
|
||||
int PRCGetIsRealSpellKnownByClass(int nRealSpellID, int nClass, object oPC = OBJECT_SELF);
|
||||
|
||||
//routes to action cast spell, but puts a wrapper around to tell other functions its a
|
||||
//SLA, so dont craft etc
|
||||
//also defaults the totalDC to 10+spellevel+chamod
|
||||
// moved from prc_inc_racial
|
||||
void DoRacialSLA(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0, int bInstantCast = FALSE);
|
||||
|
||||
/**
|
||||
* Deletes a stored manifestation structure.
|
||||
*
|
||||
* @param oObject The object on which the structure is stored
|
||||
* @param sName The name under which the structure is stored
|
||||
*/
|
||||
void DeleteLocalManifestation(object oObject, string sName);
|
||||
|
||||
/**
|
||||
* Deletes a stored mystery structure.
|
||||
*
|
||||
* @param oObject The object on which the structure is stored
|
||||
* @param sName The name under which the structure is stored
|
||||
*/
|
||||
void DeleteLocalMystery(object oObject, string sName);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Constants */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// metamagic spell level adjustments for Bioware provided metamagic feats
|
||||
const int METAMAGIC_EXTEND_LEVEL = 1;
|
||||
const int METAMAGIC_SILENT_LEVEL = 1;
|
||||
const int METAMAGIC_STILL_LEVEL = 1;
|
||||
const int METAMAGIC_EMPOWER_LEVEL = 2;
|
||||
const int METAMAGIC_MAXIMIZE_LEVEL = 3;
|
||||
const int METAMAGIC_QUICKEN_LEVEL = 4;
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Includes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
#include "lookup_2da_spell"
|
||||
#include "inc_lookups"
|
||||
#include "prc_inc_damage"
|
||||
#include "prc_inc_sb_const" // Spell Book Constants
|
||||
#include "x0_i0_position"
|
||||
|
||||
/*
|
||||
access to prc_inc_nwscript via prc_inc_damage
|
||||
access to PRCGetSpell* via prc_inc_damage
|
||||
*/
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function Definitions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//:: Returns true if oCaster's race can naturally cast sorcerer spells.
|
||||
int GetIsRHDSorcerer(object oCaster = OBJECT_SELF)
|
||||
{
|
||||
int nRace = GetRacialType(oCaster);
|
||||
|
||||
return (nRace == RACIAL_TYPE_ARANEA ||
|
||||
nRace == RACIAL_TYPE_ARKAMOI ||
|
||||
nRace == RACIAL_TYPE_DRIDER ||
|
||||
nRace == RACIAL_TYPE_HOBGOBLIN_WARSOUL ||
|
||||
nRace == RACIAL_TYPE_MARRUTACT ||
|
||||
nRace == RACIAL_TYPE_RAKSHASA ||
|
||||
nRace == RACIAL_TYPE_REDSPAWN_ARCANISS);
|
||||
}
|
||||
|
||||
//:: Returns true if oCaster's race can naturally cast bard spells.
|
||||
int GetIsRHDBard(object oCaster = OBJECT_SELF)
|
||||
{
|
||||
int nRace = GetRacialType(oCaster);
|
||||
|
||||
return (nRace == RACIAL_TYPE_GLOURA);
|
||||
}
|
||||
|
||||
//wrapper for GetSpellTargetLocation()
|
||||
location PRCGetSpellTargetLocation(object oCaster = OBJECT_SELF)
|
||||
{
|
||||
// check if there is an override location on the module, and return that
|
||||
// bioware did not define a LOCATION_INVALID const, so we must signal a valid override location by setting a local int on the module
|
||||
if(GetLocalInt(GetModule(), PRC_SPELL_TARGET_LOCATION_OVERRIDE))
|
||||
{
|
||||
if (DEBUG) DoDebug("PRCGetSpellTargetLocation: found override target location on module");
|
||||
return GetLocalLocation(GetModule(), PRC_SPELL_TARGET_LOCATION_OVERRIDE);
|
||||
}
|
||||
|
||||
|
||||
// check if there is an override location on the caster, and return that
|
||||
// bioware did not define a LOCATION_INVALID const, so we signal a valid override location by setting a local int on oCaster
|
||||
if (GetLocalInt(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE))
|
||||
{
|
||||
if (DEBUG) DoDebug("PRCGetSpellTargetLocation: found override target location on caster "+GetName(oCaster));
|
||||
return GetLocalLocation(oCaster, PRC_SPELL_TARGET_LOCATION_OVERRIDE);
|
||||
}
|
||||
|
||||
|
||||
// The rune/gem/skull always targets the one who activates it.
|
||||
object oItem = PRCGetSpellCastItem(oCaster);
|
||||
if(GetIsObjectValid(oItem) && (GetResRef(oItem) == "prc_rune_1" ||
|
||||
GetResRef(oItem) == "prc_skulltalis" || GetTag(oItem) == "prc_attunegem"))
|
||||
return GetLocation(GetItemPossessor(oItem));
|
||||
|
||||
if (GetLocalInt(oCaster, "BlackLabyrinth") && d10() < 3)
|
||||
return GenerateNewLocationFromLocation(GetSpellTargetLocation(), FeetToMeters(5.0*d4()), IntToFloat(Random(360)), IntToFloat(Random(360)));
|
||||
|
||||
// if we made it here, we must use Bioware's function
|
||||
return GetSpellTargetLocation();
|
||||
}
|
||||
|
||||
void ActionCastSpellOnSelf(int iSpell, int nMetaMagic = METAMAGIC_NONE, object oTarget = OBJECT_SELF)
|
||||
{
|
||||
if(!GetIsObjectValid(oTarget)) oTarget = OBJECT_SELF;
|
||||
object oCastingObject = CreateObject(OBJECT_TYPE_PLACEABLE, "x0_rodwonder", GetLocation(oTarget));
|
||||
|
||||
AssignCommand(oCastingObject, ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
|
||||
if (DEBUG) DoDebug("ActionCastSpellOnSelf: Casting Spell "+IntToString(iSpell)+" on "+GetName(oTarget));
|
||||
|
||||
DestroyObject(oCastingObject, 6.0);
|
||||
}
|
||||
|
||||
void ActionCastSpell(int iSpell, int iCasterLev = 0, int iBaseDC = 0, int iTotalDC = 0,
|
||||
int nMetaMagic = METAMAGIC_NONE, int nClass = CLASS_TYPE_INVALID,
|
||||
int bUseOverrideTargetLocation=FALSE, int bUseOverrideTargetObject=FALSE,
|
||||
object oOverrideTarget=OBJECT_INVALID, int bInstantCast=TRUE, int bUseOverrideMetaMagic=FALSE)
|
||||
{
|
||||
|
||||
//if its a hostile spell, clear the action queue
|
||||
//this stops people stacking hostile spells to be instacast
|
||||
//at the end, for example when coming out of invisibility
|
||||
// X - hope this is not needed if spells are cast normally
|
||||
//if(Get2DACache("spells", "HostileSetting", iSpell) == "1" && bInstantCast)
|
||||
// ClearAllActions();
|
||||
|
||||
object oTarget = PRCGetSpellTargetObject();
|
||||
location lLoc = PRCGetSpellTargetLocation();
|
||||
|
||||
//set the overriding values
|
||||
if (iCasterLev != 0)
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE, iCasterLev));
|
||||
if (iTotalDC != 0)
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE, iTotalDC));
|
||||
if (iBaseDC != 0)
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE, iBaseDC));
|
||||
if (nClass != CLASS_TYPE_INVALID)
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE, nClass));
|
||||
if (bUseOverrideMetaMagic)
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE, nMetaMagic));
|
||||
else if (nMetaMagic != METAMAGIC_NONE)
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_METAMAGIC_ADJUSTMENT, nMetaMagic));
|
||||
if (bUseOverrideTargetLocation)
|
||||
{
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE, TRUE));
|
||||
//location must be set outside of this function at the moment
|
||||
//cant pass a location into a function as an optional parameter
|
||||
//go bioware for not defining an invalid location constant
|
||||
}
|
||||
if (bUseOverrideTargetObject)
|
||||
{
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE, TRUE));
|
||||
ActionDoCommand(SetLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE, oOverrideTarget));
|
||||
}
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, "UsingActionCastSpell", TRUE));
|
||||
|
||||
if(DEBUG) DoDebug("ActionCastSpell SpellId: " + IntToString(iSpell));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Caster Level: " + IntToString(iCasterLev));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Base DC: " + IntToString(iBaseDC));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Total DC: " + IntToString(iTotalDC));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Metamagic: " + IntToString(nMetaMagic));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Caster Class: " + IntToString(nClass));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Target: " + GetName(oTarget));
|
||||
if(DEBUG) DoDebug("ActionCastSpell Override Target: " + GetName(oOverrideTarget));
|
||||
|
||||
//cast the spell
|
||||
if (GetIsObjectValid(oOverrideTarget))
|
||||
ActionCastSpellAtObject(iSpell, oOverrideTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast);
|
||||
else if (GetIsObjectValid(oTarget))
|
||||
ActionCastSpellAtObject(iSpell, oTarget, nMetaMagic, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast);
|
||||
else
|
||||
ActionCastSpellAtLocation(iSpell, lLoc, nMetaMagic, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, bInstantCast);
|
||||
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "UsingActionCastSpell"));
|
||||
|
||||
//clean up afterwards
|
||||
if(bInstantCast)//give scripts time to read the variables
|
||||
{
|
||||
if (iCasterLev != 0)
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE)));
|
||||
if (iTotalDC != 0)
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE)));
|
||||
if (iBaseDC != 0)
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE)));
|
||||
if (nClass != CLASS_TYPE_INVALID)
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE)));
|
||||
if (nMetaMagic != METAMAGIC_NONE)
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE)));
|
||||
if (bUseOverrideTargetLocation)
|
||||
{
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE)));
|
||||
//location must be set outside of this function at the moment
|
||||
//cant pass a location into a function as an optional parameter
|
||||
//go bioware for not defining an invalid location constant
|
||||
}
|
||||
if (bUseOverrideTargetObject)
|
||||
{
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE)));
|
||||
ActionDoCommand(DelayCommand(1.0, DeleteLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iCasterLev != 0)
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_CASTERLEVEL_OVERRIDE));
|
||||
if (iTotalDC != 0)
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_DC_TOTAL_OVERRIDE));
|
||||
if (iBaseDC != 0)
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_DC_BASE_OVERRIDE));
|
||||
if (nClass != CLASS_TYPE_INVALID)
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_CASTERCLASS_OVERRIDE));
|
||||
if (bUseOverrideMetaMagic)
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_OVERRIDE));
|
||||
else if (nMetaMagic != METAMAGIC_NONE)
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_METAMAGIC_ADJUSTMENT));
|
||||
if (bUseOverrideTargetLocation)
|
||||
{
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_LOCATION_OVERRIDE));
|
||||
//location must be set outside of this function at the moment
|
||||
//cant pass a location into a function as an optional parameter
|
||||
//go bioware for not defining an invalid location constant
|
||||
}
|
||||
if (bUseOverrideTargetObject)
|
||||
{
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE));
|
||||
ActionDoCommand(DeleteLocalObject(OBJECT_SELF, PRC_SPELL_TARGET_OBJECT_OVERRIDE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
//The problem with this approace is that the effects are then applies by the original spell, which could go wrong. What to do?
|
||||
SetLocalInt(OBJECT_SELF, PRC_SPELLID_OVERRIDE, GetSpellId());
|
||||
DelayCommand(1.0, DeleteLocalInt(OBJECT_SELF, PRC_SPELLID_OVERRIDE));
|
||||
string sScript = Get2DACache("spells", "ImpactScript", iSpell);
|
||||
ExecuteScript(sScript, OBJECT_SELF);
|
||||
*/
|
||||
}
|
||||
|
||||
int GetBreakConcentrationCheck(object oConcentrator)
|
||||
{
|
||||
if (GetHasSpellEffect(VESTIGE_DAHLVERNAR, oConcentrator) && !GetLocalInt(oConcentrator, "PactQuality"+IntToString(VESTIGE_DAHLVERNAR))) return TRUE;
|
||||
|
||||
int nAction = GetCurrentAction(oConcentrator);
|
||||
// creature doing anything that requires attention and breaks concentration
|
||||
if (nAction == ACTION_DISABLETRAP || nAction == ACTION_TAUNT ||
|
||||
nAction == ACTION_PICKPOCKET || nAction == ACTION_ATTACKOBJECT ||
|
||||
nAction == ACTION_COUNTERSPELL || nAction == ACTION_FLAGTRAP ||
|
||||
nAction == ACTION_CASTSPELL || nAction == ACTION_ITEMCASTSPELL)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
//suffering a mental effect
|
||||
effect e1 = GetFirstEffect(oConcentrator);
|
||||
int nType;
|
||||
while (GetIsEffectValid(e1))
|
||||
{
|
||||
nType = GetEffectType(e1);
|
||||
if (nType == EFFECT_TYPE_STUNNED || nType == EFFECT_TYPE_PARALYZE ||
|
||||
nType == EFFECT_TYPE_SLEEP || nType == EFFECT_TYPE_FRIGHTENED ||
|
||||
nType == EFFECT_TYPE_PETRIFY || nType == EFFECT_TYPE_CONFUSED ||
|
||||
nType == EFFECT_TYPE_DOMINATED || nType == EFFECT_TYPE_POLYMORPH)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
e1 = GetNextEffect(oConcentrator);
|
||||
}
|
||||
// add to on damage event
|
||||
AddEventScript(oConcentrator, EVENT_VIRTUAL_ONDAMAGED, "prc_od_conc", FALSE, FALSE);
|
||||
if(GetLocalInt(oConcentrator, "CONC_BROKEN")) // won't be set first time around regardless
|
||||
{
|
||||
DeleteLocalInt(oConcentrator, "CONC_BROKEN"); // reset for next spell
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CheckConcentrationOnEffect(object oCaster, int SpellID, object oTarget, int nDuration)
|
||||
{
|
||||
int nDur = GetLocalInt(oCaster, "Conc" + IntToString(SpellID));
|
||||
if(GetBreakConcentrationCheck(oCaster) == TRUE && nDur < nDuration)
|
||||
{
|
||||
FloatingTextStringOnCreature("*Concentration Broken*", oCaster);
|
||||
DeleteLocalInt(oCaster, "Conc" + IntToString(SpellID));
|
||||
PRCRemoveSpellEffects(SpellID, oCaster, oTarget);
|
||||
}
|
||||
else if(nDur < nDuration)
|
||||
{
|
||||
SetLocalInt(oCaster, "Conc" + IntToString(SpellID), nDur + 3);
|
||||
DelayCommand(3.0, CheckConcentrationOnEffect(oCaster, SpellID, oTarget, nDuration));
|
||||
}
|
||||
else
|
||||
{
|
||||
DeleteLocalInt(oCaster, "Conc" + IntToString(SpellID));
|
||||
}
|
||||
}
|
||||
|
||||
int PRCGetSpellLevelForClass(int nSpell, int nClass)
|
||||
{
|
||||
string sSpellLevel = "";
|
||||
if (nClass == CLASS_TYPE_WIZARD || nClass == CLASS_TYPE_SORCERER)
|
||||
sSpellLevel = Get2DACache("spells", "Wiz_Sorc", nSpell);
|
||||
else if (nClass == CLASS_TYPE_RANGER)
|
||||
sSpellLevel = Get2DACache("spells", "Ranger", nSpell);
|
||||
else if (nClass == CLASS_TYPE_PALADIN)
|
||||
sSpellLevel = Get2DACache("spells", "Paladin", nSpell);
|
||||
else if (nClass == CLASS_TYPE_DRUID)
|
||||
sSpellLevel = Get2DACache("spells", "Druid", nSpell);
|
||||
else if (nClass == CLASS_TYPE_CLERIC || nClass == CLASS_TYPE_UR_PRIEST || nClass == CLASS_TYPE_OCULAR)
|
||||
sSpellLevel = Get2DACache("spells", "Cleric", nSpell);
|
||||
else if (nClass == CLASS_TYPE_BARD)
|
||||
sSpellLevel = Get2DACache("spells", "Bard", nSpell);
|
||||
else if (nClass == CLASS_TYPE_ASSASSIN)
|
||||
sSpellLevel = Get2DACache("spells", "Assassin", nSpell);
|
||||
else if (nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK)
|
||||
sSpellLevel = Get2DACache("spells", "Cultist", nSpell);
|
||||
else if (nClass == CLASS_TYPE_NENTYAR_HUNTER)
|
||||
sSpellLevel = Get2DACache("spells", "Nentyar", nSpell);
|
||||
else if (nClass == CLASS_TYPE_SHADOWLORD)
|
||||
sSpellLevel = Get2DACache("spells", "Telflammar", nSpell);
|
||||
else if (nClass == CLASS_TYPE_SLAYER_OF_DOMIEL)
|
||||
sSpellLevel = Get2DACache("spells", "Domiel", nSpell);
|
||||
else if (nClass == CLASS_TYPE_SOHEI)
|
||||
sSpellLevel = Get2DACache("spells", "Sohei", nSpell);
|
||||
else if (nClass == CLASS_TYPE_VASSAL)
|
||||
sSpellLevel = Get2DACache("spells", "Bahamut", nSpell);
|
||||
else if (nClass == CLASS_TYPE_BLACKGUARD)
|
||||
sSpellLevel = Get2DACache("spells", "Blackguard", nSpell);
|
||||
else if (nClass == CLASS_TYPE_KNIGHT_CHALICE)
|
||||
sSpellLevel = Get2DACache("spells", "Chalice", nSpell);
|
||||
else if (nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE)
|
||||
sSpellLevel = Get2DACache("spells", "MiddleCircle", nSpell);
|
||||
else if (nClass == CLASS_TYPE_SOLDIER_OF_LIGHT)
|
||||
sSpellLevel = Get2DACache("spells", "SoLight", nSpell);
|
||||
else if (nClass == CLASS_TYPE_BLIGHTER)
|
||||
sSpellLevel = Get2DACache("spells", "Blighter", nSpell);
|
||||
else if (nClass == CLASS_TYPE_HEALER)
|
||||
sSpellLevel = Get2DACache("spells", "Healer", nSpell);
|
||||
else if (nClass == CLASS_TYPE_SHAMAN)
|
||||
sSpellLevel = Get2DACache("spells", "Shaman", nSpell);
|
||||
else if (nClass == CLASS_TYPE_INVALID)
|
||||
sSpellLevel = Get2DACache("spells", "Innate", nSpell);
|
||||
|
||||
if (sSpellLevel != "")
|
||||
return StringToInt(sSpellLevel);
|
||||
|
||||
// 2009-9-21: Support real spell ID's. -N-S
|
||||
// PRCGetSpellLevel() is called several times in the Bioware spellhooking script.
|
||||
// That means it will always pass a "real" spell ID to this function, but new-spellbook users won't have the real spell!
|
||||
// GetSpellLevel() takes the fake spell ID, so this function was always failing.
|
||||
//int nSpellLevel = GetSpellLevel(nSpell, nClass);
|
||||
int nSpellLevel = -1;
|
||||
int nSpellbookID = RealSpellToSpellbookID(nClass, nSpell);
|
||||
if (nSpellbookID == -1)
|
||||
nSpellLevel = GetSpellLevel(nSpell, nClass);
|
||||
else
|
||||
{
|
||||
string sFile = GetFileForClass(nClass);
|
||||
string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookID);
|
||||
if (sSpellLevel != "")
|
||||
nSpellLevel = StringToInt(sSpellLevel);
|
||||
}
|
||||
|
||||
return nSpellLevel;
|
||||
}
|
||||
|
||||
// returns the spell circle level of nSpell as it can be cast by oCreature
|
||||
int PRCGetSpellLevel(object oCreature, int nSpell)
|
||||
{
|
||||
/*if (!PRCGetHasSpell(nSpell, oCreature))
|
||||
return -1;*/
|
||||
|
||||
int nClass = PRCGetLastSpellCastClass();
|
||||
int nSpellLevel = PRCGetSpellLevelForClass(nSpell, nClass);
|
||||
if (nSpellLevel != -1)
|
||||
return nSpellLevel;
|
||||
|
||||
int i;
|
||||
for (i=1;i<=8;i++)
|
||||
{
|
||||
nClass = GetClassByPosition(i, oCreature);
|
||||
int nCharLevel = GetLevelByClass(nClass, oCreature);
|
||||
if (nCharLevel)
|
||||
{
|
||||
nSpellLevel = PRCGetSpellLevelForClass(nSpell, nClass);
|
||||
if (nSpellLevel != -1)
|
||||
return nSpellLevel;
|
||||
}
|
||||
}
|
||||
|
||||
//return innate level
|
||||
return StringToInt(Get2DACache("spells", "Innate", nSpell));
|
||||
}
|
||||
|
||||
// gets the spell level adjustment to the nMetaMagic, including boni from the Improved Metamagic (epic) feat
|
||||
int GetMetaMagicSpellLevelAdjustment(int nMetaMagic)
|
||||
{
|
||||
int nAdj;
|
||||
if (nMetaMagic == 0) return nAdj;
|
||||
|
||||
if (nMetaMagic & METAMAGIC_EXTEND) nAdj += METAMAGIC_EXTEND_LEVEL;
|
||||
if (nMetaMagic & METAMAGIC_SILENT) nAdj += METAMAGIC_SILENT_LEVEL;
|
||||
if (nMetaMagic & METAMAGIC_STILL) nAdj += METAMAGIC_STILL_LEVEL;
|
||||
if (nMetaMagic & METAMAGIC_EMPOWER) nAdj += METAMAGIC_EMPOWER_LEVEL;
|
||||
if (nMetaMagic & METAMAGIC_MAXIMIZE) nAdj += METAMAGIC_MAXIMIZE_LEVEL;
|
||||
if (nMetaMagic & METAMAGIC_QUICKEN) nAdj += METAMAGIC_QUICKEN_LEVEL;
|
||||
|
||||
return nAdj;
|
||||
}
|
||||
|
||||
int GetIsBioSpellCastClass(int nClass)
|
||||
{
|
||||
return nClass == CLASS_TYPE_WIZARD
|
||||
|| nClass == CLASS_TYPE_SORCERER && !GetIsRHDSorcerer()
|
||||
|| nClass == CLASS_TYPE_BARD && !GetIsRHDBard()
|
||||
|| nClass == CLASS_TYPE_CLERIC
|
||||
|| nClass == CLASS_TYPE_HEALER
|
||||
|| nClass == CLASS_TYPE_BLIGHTER
|
||||
|| nClass == CLASS_TYPE_BLACKGUARD
|
||||
|| nClass == CLASS_TYPE_UR_PRIEST
|
||||
|| nClass == CLASS_TYPE_OCULAR
|
||||
|| nClass == CLASS_TYPE_SLAYER_OF_DOMIEL
|
||||
|| nClass == CLASS_TYPE_CULTIST_SHATTERED_PEAK
|
||||
|| nClass == CLASS_TYPE_NENTYAR_HUNTER
|
||||
|| nClass == CLASS_TYPE_SHADOWLORD
|
||||
|| nClass == CLASS_TYPE_SOHEI
|
||||
|| nClass == CLASS_TYPE_SOLDIER_OF_LIGHT
|
||||
|| nClass == CLASS_TYPE_VASSAL
|
||||
|| nClass == CLASS_TYPE_KNIGHT_MIDDLECIRCLE
|
||||
|| nClass == CLASS_TYPE_KNIGHT_CHALICE
|
||||
|| nClass == CLASS_TYPE_SHAMAN
|
||||
|| nClass == CLASS_TYPE_DRUID
|
||||
|| nClass == CLASS_TYPE_PALADIN
|
||||
|| nClass == CLASS_TYPE_RANGER;
|
||||
}
|
||||
|
||||
int GetIsNSBClass(int nClass)
|
||||
{
|
||||
return !GetIsBioSpellCastClass(nClass)
|
||||
&& GetSpellbookTypeForClass(nClass) != SPELLBOOK_TYPE_INVALID;
|
||||
}
|
||||
|
||||
// returns if a character should be using the newspellbook when casting
|
||||
int UseNewSpellBook(object oCreature)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i <= 8; i++)
|
||||
{
|
||||
int nClass = GetClassByPosition(i, oCreature);
|
||||
if(GetIsNSBClass(nClass))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Special case
|
||||
if(GetLevelByClass(CLASS_TYPE_ULTIMATE_MAGUS, oCreature))
|
||||
return TRUE;
|
||||
|
||||
int nPrimaryArcane = GetPrimaryArcaneClass(oCreature);
|
||||
|
||||
//check they have bard/sorc in first arcane slot
|
||||
if(nPrimaryArcane != CLASS_TYPE_BARD && nPrimaryArcane != CLASS_TYPE_SORCERER)
|
||||
return FALSE;
|
||||
//check they have arcane PrC or Draconic Breath/Arcane Grace
|
||||
if(!GetArcanePRCLevels(oCreature, nPrimaryArcane)
|
||||
&& !(GetHasFeat(FEAT_DRACONIC_GRACE, oCreature) || GetHasFeat(FEAT_DRACONIC_BREATH, oCreature)))
|
||||
return FALSE;
|
||||
//check if the newspellbooks are disabled
|
||||
if((GetPRCSwitch(PRC_SORC_DISALLOW_NEWSPELLBOOK) && nPrimaryArcane == CLASS_TYPE_SORCERER) ||
|
||||
(GetPRCSwitch(PRC_BARD_DISALLOW_NEWSPELLBOOK) && nPrimaryArcane == CLASS_TYPE_BARD))
|
||||
return FALSE;
|
||||
//check they have bard/sorc levels
|
||||
if(!GetLevelByClass(CLASS_TYPE_BARD) && !GetLevelByClass(CLASS_TYPE_SORCERER))
|
||||
return FALSE;
|
||||
|
||||
//at this point, they should be using the new spellbook
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// wrapper for GetHasSpell, works for newspellbook 'fake' spells too (and metamagic)
|
||||
// should return 0 if called with a normal spell when a character should be using the newspellbook
|
||||
int PRCGetHasSpell(int nRealSpellID, object oCreature = OBJECT_SELF)
|
||||
{
|
||||
if(!PRCGetIsRealSpellKnown(nRealSpellID, oCreature))
|
||||
return 0;
|
||||
int nUses = GetHasSpell(nRealSpellID, oCreature);
|
||||
|
||||
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, nRealSpellID);
|
||||
nMeta = RealSpellToSpellbookIDCount(nClass, nRealSpellID);
|
||||
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;
|
||||
}
|
||||
if(nSpellbookType == SPELLBOOK_TYPE_PREPARED)
|
||||
{
|
||||
nCount = persistant_array_get_int(oCreature, "NewSpellbookMem_" + IntToString(nClass), j);
|
||||
if(DEBUG) DoDebug("prc_inc_core >> PRCGetHasSpell: 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("prc_inc_core >> PRCGetHasSpell: NewSpellbookMem_" + IntToString(nClass) + "[" + IntToString(j) + "] = " + IntToString(nCount));
|
||||
if(nCount > 0)
|
||||
{
|
||||
nUses += nCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(DEBUG) DoDebug("PRCGetHasSpell: RealSpellID = " + IntToString(nRealSpellID) + ", Uses = " + IntToString(nUses));
|
||||
return nUses;
|
||||
}
|
||||
|
||||
// checks if oPC knows the specified spell
|
||||
// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters
|
||||
int PRCGetIsRealSpellKnown(int nRealSpellID, object oPC = OBJECT_SELF)
|
||||
{
|
||||
if(GetHasSpell(nRealSpellID, oPC)) //FUGLY HACK: bioware class having uses of the spell
|
||||
return TRUE; // means they know the spell (close enough)
|
||||
int nClass;
|
||||
int nClassSlot = 1;
|
||||
while(nClassSlot <= 8)
|
||||
{
|
||||
nClass = GetClassByPosition(nClassSlot, oPC);
|
||||
if(GetIsDivineClass(nClass) || GetIsArcaneClass(nClass))
|
||||
if(PRCGetIsRealSpellKnownByClass(nRealSpellID, nClass, oPC))
|
||||
return TRUE;
|
||||
nClassSlot++;
|
||||
}
|
||||
// got here means no match
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// checks if oPC knows the specified spell
|
||||
// only works for classes that use the PRC spellbook, there is currently no way to do this for Bioware spellcasters
|
||||
// this will only check the spellbook of the class specified
|
||||
int PRCGetIsRealSpellKnownByClass(int nRealSpellID, int nClass, object oPC = OBJECT_SELF)
|
||||
{
|
||||
// check for whether bard and sorc are using the prc spellbooks
|
||||
if (nClass == CLASS_TYPE_BARD || nClass == CLASS_TYPE_SORCERER)
|
||||
{
|
||||
if(!UseNewSpellBook(oPC))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// get the cls_spell_***.2da index for the real spell
|
||||
int nSpellbookSpell = RealSpellToSpellbookID(nClass, nRealSpellID);
|
||||
// if the spell does not exist in the spellbook, return FALSE
|
||||
if (nSpellbookSpell == -1)
|
||||
return FALSE;
|
||||
// next check if the PC is high enough level to know the spell
|
||||
string sFile = GetFileForClass(nClass);
|
||||
int nSpellLevel = -1;
|
||||
string sSpellLevel = Get2DACache(sFile, "Level", nSpellbookSpell);
|
||||
if (sSpellLevel != "")
|
||||
nSpellLevel = StringToInt(sSpellLevel);
|
||||
if ((GetLevelByClass(nClass) < nSpellLevel) || nSpellLevel == -1)
|
||||
return FALSE; // not high enough level
|
||||
// at this stage, prepared casters know the spell and only spontaneous classes need checking
|
||||
// there are exceptions and these need hardcoding:
|
||||
|
||||
if((GetSpellbookTypeForClass(nClass) == SPELLBOOK_TYPE_PREPARED) && nClass != CLASS_TYPE_ARCHIVIST)
|
||||
return TRUE;
|
||||
|
||||
// spontaneous casters have all their known spells as hide feats
|
||||
// get the featID of the spell
|
||||
int nFeatID = StringToInt(Get2DACache(sFile, "FeatID", nSpellbookSpell));
|
||||
if (GetHasFeat(nFeatID, oPC))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//routes to action cast spell, but puts a wrapper around to tell other functions its a
|
||||
//SLA, so dont craft etc
|
||||
//also defaults th totalDC to 10+spellevel+chamod
|
||||
//this is Base DC, not total DC. SLAs are still spells, so spell focus should still apply.
|
||||
void DoRacialSLA(int nSpellID, int nCasterlevel = 0, int nTotalDC = 0, int bInstantCast = FALSE)
|
||||
{
|
||||
if(DEBUG) DoDebug("Spell DC passed to DoRacialSLA: " + IntToString(nTotalDC));
|
||||
if(nTotalDC == 0)
|
||||
nTotalDC = 10
|
||||
+StringToInt(Get2DACache("spells", "Innate", nSpellID))
|
||||
+GetAbilityModifier(ABILITY_CHARISMA);
|
||||
|
||||
ActionDoCommand(SetLocalInt(OBJECT_SELF, "SpellIsSLA", TRUE));
|
||||
if(DEBUG) DoDebug("Spell DC entered in ActionCastSpell: " + IntToString(nTotalDC));
|
||||
ActionCastSpell(nSpellID, nCasterlevel, 0, nTotalDC, METAMAGIC_NONE, CLASS_TYPE_INVALID, FALSE, FALSE, OBJECT_INVALID, bInstantCast);
|
||||
//ActionCastSpell(nSpellID, nCasterlevel, 0, nTotalDC);
|
||||
ActionDoCommand(DeleteLocalInt(OBJECT_SELF, "SpellIsSLA"));
|
||||
}
|
||||
|
||||
void DeleteLocalManifestation(object oObject, string sName)
|
||||
{
|
||||
DeleteLocalObject(oObject, sName + "_oManifester");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_bCanManifest");
|
||||
DeleteLocalInt(oObject, sName + "_nPPCost");
|
||||
DeleteLocalInt(oObject, sName + "_nPsiFocUsesRemain");
|
||||
DeleteLocalInt(oObject, sName + "_nManifesterLevel");
|
||||
DeleteLocalInt(oObject, sName + "_nSpellID");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_1");
|
||||
DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_2");
|
||||
DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_3");
|
||||
DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_4");
|
||||
DeleteLocalInt(oObject, sName + "_nTimesAugOptUsed_5");
|
||||
DeleteLocalInt(oObject, sName + "_nTimesGenericAugUsed");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_bChain");
|
||||
DeleteLocalInt(oObject, sName + "_bEmpower");
|
||||
DeleteLocalInt(oObject, sName + "_bExtend");
|
||||
DeleteLocalInt(oObject, sName + "_bMaximize");
|
||||
DeleteLocalInt(oObject, sName + "_bSplit");
|
||||
DeleteLocalInt(oObject, sName + "_bTwin");
|
||||
DeleteLocalInt(oObject, sName + "_bWiden");
|
||||
DeleteLocalInt(oObject, sName + "_bQuicken");
|
||||
}
|
||||
|
||||
void DeleteLocalMystery(object oObject, string sName)
|
||||
{
|
||||
DeleteLocalObject(oObject, sName + "_oShadow");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_bCanMyst");
|
||||
DeleteLocalInt(oObject, sName + "_nShadowcasterLevel");
|
||||
DeleteLocalInt(oObject, sName + "_nMystId");
|
||||
DeleteLocalInt(oObject, sName + "_nPen");
|
||||
DeleteLocalInt(oObject, sName + "_bIgnoreSR");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_bEmpower");
|
||||
DeleteLocalInt(oObject, sName + "_bExtend");
|
||||
DeleteLocalInt(oObject, sName + "_bMaximize");
|
||||
DeleteLocalInt(oObject, sName + "_bQuicken");
|
||||
|
||||
DeleteLocalInt(oObject, sName + "_nSaveDC");
|
||||
DeleteLocalFloat(oObject, sName + "_fDur");
|
||||
}
|
||||
|
||||
//::void main (){}
|
||||
@@ -0,0 +1,203 @@
|
||||
//
|
||||
// Wrapper Functions for the Archmage Class and Feats
|
||||
//
|
||||
|
||||
//
|
||||
// Notes: Normal use is to include prc_alterations.
|
||||
// If this file if to be included elsewhere add the following lines
|
||||
// to the target file:
|
||||
// #include "prcsp_reputation"
|
||||
// #include "prcsp_archmaginc"
|
||||
//
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Constants */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/// @todo Change these to TLK reads
|
||||
|
||||
const string MASTERY_OF_ELEMENTS_TAG = "archmage_mastery_elements";
|
||||
const string MASTERY_OF_ELEMENTS_NAME_TAG = "archmage_mastery_elements_name";
|
||||
const string MASTERY_OF_SHAPE_TAG = "archmage_mastery_shaping";
|
||||
const string MASTERY_OF_SHAPE_ON = "Shaping spells to protect allies.";
|
||||
const string MASTERY_OF_SHAPE_OFF = "Spell shaping is disabled, allies may be effected.";
|
||||
const string MASTERY_OF_ELEMENTS_ACID = "Elemental spell damage set to acid.";
|
||||
const string MASTERY_OF_ELEMENTS_COLD = "Elemental spell damage set to cold.";
|
||||
const string MASTERY_OF_ELEMENTS_ELECTRICAL = "Elemental spell damage set to electrical.";
|
||||
const string MASTERY_OF_ELEMENTS_FIRE = "Elemental spell damage set to fire.";
|
||||
const string MASTERY_OF_ELEMENTS_SONIC = "Elemental spell damage set to sonic.";
|
||||
const string MASTERY_OF_ELEMENTS_OFF = "Elemental spell damage returned to normal.";
|
||||
|
||||
const int FEAT_INACTIVE = 0;
|
||||
const int FEAT_ACTIVE = 1;
|
||||
|
||||
const int MASTERY_OF_SHAPE_EFFECT = 460;
|
||||
const int MASTERY_OF_ELEMENTS_EFFECT_ACID = 448;
|
||||
const int MASTERY_OF_ELEMENTS_EFFECT_ELECTRICAL = 463;
|
||||
const int MASTERY_OF_ELEMENTS_EFFECT_OFF = 460;
|
||||
|
||||
const int SPELL_MASTERY_ELEMENTS_NORMAL = 2000;
|
||||
const int SPELL_MASTERY_ELEMENTS_ACID = 2003;
|
||||
const int SPELL_MASTERY_ELEMENTS_COLD = 2002;
|
||||
const int SPELL_MASTERY_ELEMENTS_ELECTRICITY = 2004;
|
||||
const int SPELL_MASTERY_ELEMENTS_FIRE = 2001;
|
||||
const int SPELL_MASTERY_ELEMENTS_SONIC = 2005;
|
||||
|
||||
const int TIME_1_ROUND = 1;
|
||||
|
||||
int PRCGetSpellLevel(object oCreature, int nSpell);
|
||||
int PRCGetSpellId(object oCaster = OBJECT_SELF);
|
||||
object PRCGetSpellTargetObject(object oCaster = OBJECT_SELF);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function prototypes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Determines if Master of Shapes is active and applies in regards to the
|
||||
* given target.
|
||||
*
|
||||
* @param oCaster A creature casting an area-affecting spell
|
||||
* @param oTarget A creature that is in the affected area
|
||||
* @return TRUE if the creature should be exempt from the spell due to
|
||||
* Mastery of Shapes. FALSE otherwise
|
||||
*/
|
||||
int CheckMasteryOfShapes(object oCaster, object oTarget);
|
||||
|
||||
void SetFeatVisualEffects(object oCaster, int nEffect, string sMessage);
|
||||
|
||||
void ToggleMasteryOfShapes(object oCaster);
|
||||
|
||||
void SetMasteryOfElements();
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Includes */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
//#include "lookup_2da_spell"
|
||||
#include "prcsp_reputation"
|
||||
//#include "prc_inc_spells"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Function definitions */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
int CheckMasteryOfShapes(object oCaster, object oTarget)
|
||||
{
|
||||
int bRetVal = FALSE;
|
||||
|
||||
// This variable should not be set without the feat being available.
|
||||
// If someone wants to cheat, let them.
|
||||
if (GetLocalInt(oCaster, MASTERY_OF_SHAPE_TAG) == FEAT_ACTIVE && !GetIsReactionTypeHostile(oTarget, oCaster))
|
||||
{
|
||||
bRetVal = TRUE;
|
||||
}
|
||||
|
||||
return bRetVal;
|
||||
}
|
||||
|
||||
int ExtraordinarySpellAim(object oCaster, object oTarget)
|
||||
{
|
||||
int bRetVal = FALSE;
|
||||
|
||||
// This variable should not be set without the feat being available.
|
||||
// If someone wants to cheat, let them.
|
||||
if(GetHasFeat(FEAT_EXTRAORDINARY_SPELL_AIM, oCaster)
|
||||
&& !GetLocalInt(oCaster, "ExtraordinarySpellAim")
|
||||
&& GetIsFriend(oTarget, oCaster))
|
||||
{
|
||||
// Only once per spell
|
||||
SetLocalInt(oCaster, "ExtraordinarySpellAim", TRUE);
|
||||
DelayCommand(1.0, DeleteLocalInt(oCaster, "ExtraordinarySpellAim"));
|
||||
if(GetIsSkillSuccessful(oCaster, SKILL_SPELLCRAFT, 25 + PRCGetSpellLevel(oCaster, PRCGetSpellId())))
|
||||
bRetVal = TRUE;
|
||||
}
|
||||
|
||||
return bRetVal;
|
||||
}
|
||||
|
||||
//
|
||||
// Help with Visual Effects when setting feats
|
||||
//
|
||||
void SetFeatVisualEffects(object oCaster, int nEffect, string sMessage)
|
||||
{
|
||||
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectVisualEffect(nEffect),
|
||||
oCaster, RoundsToSeconds(TIME_1_ROUND));
|
||||
|
||||
FloatingTextStringOnCreature(sMessage, OBJECT_SELF, FALSE);
|
||||
}
|
||||
|
||||
//
|
||||
// Enable/Disable Mastery of Shapes
|
||||
//
|
||||
void ToggleMasteryOfShapes(object oCaster)
|
||||
{
|
||||
if (GetLocalInt(OBJECT_SELF, MASTERY_OF_SHAPE_TAG) == FEAT_INACTIVE) {
|
||||
SetLocalInt(OBJECT_SELF, MASTERY_OF_SHAPE_TAG, FEAT_ACTIVE);
|
||||
SetFeatVisualEffects(oCaster, MASTERY_OF_SHAPE_EFFECT, MASTERY_OF_SHAPE_ON);
|
||||
}
|
||||
else {
|
||||
SetLocalInt(OBJECT_SELF, MASTERY_OF_SHAPE_TAG, FEAT_INACTIVE);
|
||||
SetFeatVisualEffects(oCaster, MASTERY_OF_SHAPE_EFFECT, MASTERY_OF_SHAPE_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This function sets the Mastery of Elements feat to a specific element
|
||||
//
|
||||
void SetMasteryOfElements()
|
||||
{
|
||||
string msg = MASTERY_OF_ELEMENTS_OFF;
|
||||
string sElem = "";
|
||||
int nEffect = MASTERY_OF_ELEMENTS_EFFECT_OFF;
|
||||
int dmgType = FEAT_INACTIVE;
|
||||
|
||||
switch (PRCGetSpellId()) {
|
||||
case SPELL_MASTERY_ELEMENTS_ACID:
|
||||
nEffect = MASTERY_OF_ELEMENTS_EFFECT_ACID;
|
||||
dmgType = DAMAGE_TYPE_ACID;
|
||||
msg = MASTERY_OF_ELEMENTS_ACID;
|
||||
sElem = "Acid";
|
||||
break;
|
||||
|
||||
case SPELL_MASTERY_ELEMENTS_COLD:
|
||||
nEffect = VFX_IMP_AC_BONUS;
|
||||
dmgType = DAMAGE_TYPE_COLD;
|
||||
msg = MASTERY_OF_ELEMENTS_COLD;
|
||||
sElem = "Cold";
|
||||
break;
|
||||
|
||||
case SPELL_MASTERY_ELEMENTS_ELECTRICITY:
|
||||
nEffect = MASTERY_OF_ELEMENTS_EFFECT_ELECTRICAL;
|
||||
dmgType = DAMAGE_TYPE_ELECTRICAL;
|
||||
msg = MASTERY_OF_ELEMENTS_ELECTRICAL;
|
||||
sElem = "Electricity";
|
||||
break;
|
||||
|
||||
case SPELL_MASTERY_ELEMENTS_FIRE:
|
||||
nEffect = VFX_IMP_ELEMENTAL_PROTECTION;
|
||||
dmgType = DAMAGE_TYPE_FIRE;
|
||||
msg = MASTERY_OF_ELEMENTS_FIRE;
|
||||
sElem = "Fire";
|
||||
break;
|
||||
|
||||
case SPELL_MASTERY_ELEMENTS_SONIC:
|
||||
nEffect = VFX_FNF_SOUND_BURST;
|
||||
dmgType = DAMAGE_TYPE_SONIC;
|
||||
msg = MASTERY_OF_ELEMENTS_SONIC;
|
||||
sElem = "Sonic";
|
||||
break;
|
||||
|
||||
default:
|
||||
// Use the default initialized variables
|
||||
break;
|
||||
}
|
||||
|
||||
SetLocalInt(OBJECT_SELF, MASTERY_OF_ELEMENTS_TAG, dmgType);
|
||||
SetLocalString(OBJECT_SELF, MASTERY_OF_ELEMENTS_NAME_TAG, sElem);
|
||||
SetFeatVisualEffects(PRCGetSpellTargetObject(), nEffect, msg);
|
||||
}
|
||||
|
||||
// Test main
|
||||
//void main(){}
|
||||
@@ -0,0 +1,419 @@
|
||||
|
||||
// Module Constants
|
||||
const float CACHE_TIMEOUT_CAST = 2.0;
|
||||
const string CASTER_LEVEL_TAG = "PRCEffectiveCasterLevel";
|
||||
|
||||
// Constants that dictate ResistSpell results
|
||||
const int SPELL_RESIST_FAIL = 0;
|
||||
const int SPELL_RESIST_PASS = 1;
|
||||
const int SPELL_RESIST_GLOBE = 2;
|
||||
const int SPELL_RESIST_MANTLE = 3;
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Forward Declarations */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
int PRCDoResistSpell(object oCaster, object oTarget, int nEffCasterLvl=0, float fDelay = 0.0);
|
||||
int CheckSpellfire(object oCaster, object oTarget, int bFriendly = FALSE);
|
||||
int PRCGetLastSpellCastClass(object oCaster = OBJECT_SELF);
|
||||
int GetIsArcaneClass(int nClass, object oCaster = OBJECT_SELF);
|
||||
int GetIsDivineClass(int nClass, object oCaster = OBJECT_SELF);
|
||||
int GetIsIncarnumUser(object oMeldshaper);
|
||||
int GetIsMeldBound(object oMeldshaper, int nMeld = -1);
|
||||
int GetEssentiaInvestedFeat(object oMeldshaper, int nFeat);
|
||||
void SetPersistantLocalInt(object oPC, string sName, int nValue);
|
||||
int GetPersistantLocalInt(object oPC, string sName);
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
/* Included Files */
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
|
||||
#include "prc_inc_racial"
|
||||
//#include "prc_feat_const"
|
||||
//#include "prc_class_const"
|
||||
//#include "prcsp_reputation"
|
||||
#include "prcsp_archmaginc"
|
||||
//#include "prc_add_spell_dc"
|
||||
#include "prc_add_spl_pen"
|
||||
#include "moi_meld_const"
|
||||
|
||||
//
|
||||
// This function is a wrapper should someone wish to rewrite the Bioware
|
||||
// version. This is where it should be done.
|
||||
//
|
||||
int PRCResistSpell(object oCaster, object oTarget)
|
||||
{
|
||||
return ResistSpell(oCaster, oTarget);
|
||||
}
|
||||
|
||||
//
|
||||
// This function is a wrapper should someone wish to rewrite the Bioware
|
||||
// version. This is where it should be done.
|
||||
//
|
||||
int PRCGetSpellResistance(object oTarget, object oCaster)
|
||||
{
|
||||
int iSpellRes = GetSpellResistance(oTarget);
|
||||
int nHD = GetHitDice(oTarget);
|
||||
|
||||
//racial pack SR
|
||||
int iRacialSpellRes = 0;
|
||||
if(GetHasFeat(FEAT_SPELL27, oTarget))
|
||||
iRacialSpellRes = 27 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL25, oTarget))
|
||||
iRacialSpellRes = 25 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL23, oTarget))
|
||||
iRacialSpellRes = 23 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL22, oTarget))
|
||||
iRacialSpellRes = 22 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL21, oTarget))
|
||||
iRacialSpellRes = 21 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL20, oTarget))
|
||||
iRacialSpellRes = 20 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL19, oTarget))
|
||||
iRacialSpellRes = 19 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL18, oTarget))
|
||||
iRacialSpellRes = 18 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL17, oTarget))
|
||||
iRacialSpellRes = 17 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL16, oTarget))
|
||||
iRacialSpellRes = 16 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL15, oTarget))
|
||||
iRacialSpellRes = 15 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL14, oTarget))
|
||||
iRacialSpellRes = 14 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL13, oTarget))
|
||||
iRacialSpellRes = 13 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL11, oTarget))
|
||||
iRacialSpellRes = 11 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL10, oTarget))
|
||||
iRacialSpellRes = 10 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL8, oTarget))
|
||||
iRacialSpellRes = 8 + nHD;
|
||||
else if(GetHasFeat(FEAT_SPELL5, oTarget))
|
||||
iRacialSpellRes = 5 + nHD;
|
||||
if(iRacialSpellRes > iSpellRes)
|
||||
iSpellRes = iRacialSpellRes;
|
||||
|
||||
// Exalted Companion, can also be used for Celestial Template
|
||||
if(GetLocalInt(oTarget, "CelestialTemplate") || GetLocalInt(oTarget, "PseudonaturalTemplate"))
|
||||
{
|
||||
int nSR = nHD * 2;
|
||||
if (nSR > 25) nSR = 25;
|
||||
if (nSR > iSpellRes) iSpellRes = nSR;
|
||||
}
|
||||
|
||||
// Enlightened Fist SR = 10 + monk level + enlightened fist level
|
||||
if(GetHasFeat(FEAT_EF_DIAMOND_SOUL, oTarget))
|
||||
{
|
||||
int nEF = 10 + GetLevelByClass(CLASS_TYPE_ENLIGHTENEDFIST, oTarget) + GetLevelByClass(CLASS_TYPE_MONK, oTarget);
|
||||
if(nEF > iSpellRes)
|
||||
iSpellRes = nEF;
|
||||
}
|
||||
|
||||
// Contemplative SR = 15 + contemplative level
|
||||
if(GetHasFeat(FEAT_DIVINE_SOUL, oTarget))
|
||||
{
|
||||
int nCont = 15 + GetLevelByClass(CLASS_TYPE_CONTEMPLATIVE, oTarget);
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
|
||||
// Marrutact
|
||||
if(GetRacialType(oTarget) == RACIAL_TYPE_MARRUTACT)
|
||||
{
|
||||
int nCont = 9 + GetHitDice(oTarget);
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
|
||||
// Hobgoblin Wsrsoul
|
||||
if(GetRacialType(oTarget) == RACIAL_TYPE_HOBGOBLIN_WARSOUL)
|
||||
{
|
||||
int nCont = 8 + GetHitDice(oTarget);
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
|
||||
// Exordius Weapon of Legacy
|
||||
if(GetLocalInt(oTarget, "ExordiusSR"))
|
||||
{
|
||||
int nCont = 5 + GetHitDice(oTarget);
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
|
||||
// Hammer of Witches Weapon of Legacy
|
||||
if(GetLocalInt(oTarget, "HammerWitchesSR"))
|
||||
{
|
||||
// SR vs arcane only
|
||||
if(GetIsArcaneClass(PRCGetLastSpellCastClass(oCaster)))
|
||||
{
|
||||
int nCont = 5 + GetHitDice(oTarget);
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
}
|
||||
|
||||
// Ur-Priest
|
||||
int nPriestLevel = GetLevelByClass(CLASS_TYPE_UR_PRIEST, oTarget);
|
||||
if(nPriestLevel >= 4)
|
||||
{
|
||||
// SR vs divine only
|
||||
if(GetIsDivineClass(PRCGetLastSpellCastClass(oCaster)))
|
||||
{
|
||||
//if(nPriestLevel > 50) nPriestLevel = 50; //:: cap if needed
|
||||
|
||||
// Calculate bonus: 15 at level 4, then +5 for every additional 4 levels
|
||||
int nCont = 15 + (((nPriestLevel - 4) / 4) * 5);
|
||||
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
}
|
||||
/* // Ur-Priest
|
||||
if(GetLevelByClass(CLASS_TYPE_UR_PRIEST, oTarget) >= 4)
|
||||
{
|
||||
// SR vs divine only
|
||||
if(GetIsDivineClass(PRCGetLastSpellCastClass(oCaster)))
|
||||
{
|
||||
int nCont = 15;
|
||||
if (GetLevelByClass(CLASS_TYPE_UR_PRIEST, oTarget) >= 8) nCont = 20;
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
} */
|
||||
|
||||
// Dread Carapace Heart Bind
|
||||
if(GetIsIncarnumUser(oTarget))
|
||||
{
|
||||
if (GetIsMeldBound(oTarget, MELD_DREAD_CARAPACE) == CHAKRA_CROWN)
|
||||
{
|
||||
int nCont = 5 + (4 * GetEssentiaInvested(oTarget, MELD_DREAD_CARAPACE));
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
if (GetHasSpellEffect(MELD_SPELLWARD_SHIRT, oTarget)) // MELD_SPELLWARD_SHIRT
|
||||
{
|
||||
int nCont = 5 + (4 * GetEssentiaInvested(oTarget, MELD_SPELLWARD_SHIRT));
|
||||
if(nCont > iSpellRes)
|
||||
iSpellRes = nCont;
|
||||
}
|
||||
}
|
||||
|
||||
// Foe Hunter SR stacks with normal SR when a spell is cast by their hated enemy
|
||||
if(GetHasFeat(FEAT_HATED_ENEMY_SR, oTarget) && GetLocalInt(oTarget, "HatedFoe") == MyPRCGetRacialType(oCaster))
|
||||
{
|
||||
iSpellRes += 15 + GetLevelByClass(CLASS_TYPE_FOE_HUNTER, oTarget);
|
||||
}
|
||||
|
||||
// Adds +4 to SR
|
||||
if(GetHasFeat(FEAT_PSYCHIC_REFUSAL, oTarget))
|
||||
iSpellRes += 4;
|
||||
|
||||
// Forsaker SR adds to existing
|
||||
if(GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget))
|
||||
iSpellRes = iSpellRes + 10 + GetLevelByClass(CLASS_TYPE_FORSAKER, oTarget);
|
||||
|
||||
return iSpellRes;
|
||||
}
|
||||
|
||||
//
|
||||
// If a spell is resisted, display the effect
|
||||
//
|
||||
void PRCShowSpellResist(object oCaster, object oTarget, int nResist, float fDelay = 0.0)
|
||||
{
|
||||
// If either caster/target is a PC send them a message
|
||||
if (GetIsPC(oCaster))
|
||||
{
|
||||
string message = nResist == SPELL_RESIST_FAIL ?
|
||||
"Target is affected by the spell." : "Target resisted the spell.";
|
||||
SendMessageToPC(oCaster, message);
|
||||
}
|
||||
if (GetIsPC(oTarget))
|
||||
{
|
||||
string message = nResist == SPELL_RESIST_FAIL ?
|
||||
"You are affected by the spell." : "You resisted the spell.";
|
||||
SendMessageToPC(oTarget, message);
|
||||
}
|
||||
|
||||
if (nResist != SPELL_RESIST_FAIL) {
|
||||
// Default to a standard resistance
|
||||
int eve = VFX_IMP_MAGIC_RESISTANCE_USE;
|
||||
|
||||
// Check for other resistances
|
||||
if (nResist == SPELL_RESIST_GLOBE)
|
||||
eve = VFX_IMP_GLOBE_USE;
|
||||
else if (nResist == SPELL_RESIST_MANTLE)
|
||||
eve = VFX_IMP_SPELL_MANTLE_USE;
|
||||
|
||||
// Render the effect
|
||||
DelayCommand(fDelay, ApplyEffectToObject(DURATION_TYPE_INSTANT,
|
||||
EffectVisualEffect(eve), oTarget));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// This function overrides the BioWare MyResistSpell.
|
||||
// TODO: Change name to PRCMyResistSpell.
|
||||
//
|
||||
int PRCDoResistSpell(object oCaster, object oTarget, int nEffCasterLvl=0, float fDelay = 0.0)
|
||||
{
|
||||
int nResist;
|
||||
|
||||
// Check if the archmage shape mastery applies to this target
|
||||
if (CheckSpellfire(oCaster, oTarget) || CheckMasteryOfShapes(oCaster, oTarget) || ExtraordinarySpellAim(oCaster, oTarget) || (GetLocalInt(oCaster, "WOL_DesertWindFireball") && GetSpellId() == SPELL_FIREBALL))
|
||||
nResist = SPELL_RESIST_MANTLE;
|
||||
else if(GetLevelByClass(CLASS_TYPE_BEGUILER, oCaster) >= 20 && GetIsDeniedDexBonusToAC(oTarget, oCaster, TRUE))
|
||||
{
|
||||
//Beguilers of level 20+ automatically overcome SR of targets denied Dex bonus to AC
|
||||
nResist = SPELL_RESIST_FAIL;
|
||||
}
|
||||
else if(GetLocalInt(oCaster, "CunningBreach"))
|
||||
{
|
||||
//Factotum can pay to breach all SR for a round
|
||||
nResist = SPELL_RESIST_FAIL;
|
||||
}
|
||||
//using vitriolic blast with eldritch spellweave
|
||||
else if(oTarget == GetLocalObject(oCaster, "SPELLWEAVE_TARGET")
|
||||
&& GetLocalInt(oCaster, "BlastEssence") == INVOKE_VITRIOLIC_BLAST)
|
||||
{
|
||||
nResist = SPELL_RESIST_FAIL;
|
||||
}
|
||||
else {
|
||||
// Check immunities and mantles, otherwise ignore the result completely
|
||||
nResist = PRCResistSpell(oCaster, oTarget);
|
||||
|
||||
//Resonating Resistance
|
||||
if((nResist <= SPELL_RESIST_PASS) && (GetHasSpellEffect(SPELL_RESONATING_RESISTANCE, oTarget)))
|
||||
{
|
||||
nResist = PRCResistSpell(oCaster, oTarget);
|
||||
}
|
||||
|
||||
if (nResist <= SPELL_RESIST_PASS)
|
||||
{
|
||||
nResist = SPELL_RESIST_FAIL;
|
||||
|
||||
// Because the version of this function was recently changed to
|
||||
// optionally allow the caster level, we must calculate it here.
|
||||
// The result will be cached for a period of time.
|
||||
if (!nEffCasterLvl) {
|
||||
nEffCasterLvl = GetLocalInt(oCaster, CASTER_LEVEL_TAG);
|
||||
if (!nEffCasterLvl) {
|
||||
nEffCasterLvl = PRCGetCasterLevel(oCaster) + SPGetPenetr();
|
||||
SetLocalInt(oCaster, CASTER_LEVEL_TAG, nEffCasterLvl);
|
||||
DelayCommand(CACHE_TIMEOUT_CAST,
|
||||
DeleteLocalInt(oCaster, CASTER_LEVEL_TAG));
|
||||
}
|
||||
}
|
||||
|
||||
// Pernicious Magic
|
||||
// +4 caster level vs SR Weave user (not Evoc & Trans spells)
|
||||
int iWeav;
|
||||
if (GetHasFeat(FEAT_PERNICIOUSMAGIC,oCaster))
|
||||
{
|
||||
if (!GetHasFeat(FEAT_SHADOWWEAVE,oTarget))
|
||||
{
|
||||
int nSchool = GetLocalInt(oCaster, "X2_L_LAST_SPELLSCHOOL_VAR");
|
||||
if ( nSchool != SPELL_SCHOOL_EVOCATION && nSchool != SPELL_SCHOOL_TRANSMUTATION )
|
||||
iWeav=4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// A tie favors the caster.
|
||||
if ((nEffCasterLvl + d20(1)+iWeav) < PRCGetSpellResistance(oTarget, oCaster))
|
||||
nResist = SPELL_RESIST_PASS;
|
||||
}
|
||||
}
|
||||
|
||||
// Karsites heal from resisting a spell
|
||||
if(GetRacialType(oTarget) == RACIAL_TYPE_KARSITE && nResist == SPELL_RESIST_PASS)
|
||||
{
|
||||
int nSpellLevel = StringToInt(Get2DACache("spells", "Innate", PRCGetSpellId()));
|
||||
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(nSpellLevel*2), oTarget);
|
||||
}
|
||||
|
||||
PRCShowSpellResist(oCaster, oTarget, nResist, fDelay);
|
||||
|
||||
return nResist;
|
||||
}
|
||||
|
||||
//Returns the maximum number of spellfire levels oPC can store
|
||||
int SpellfireMax(object oPC)
|
||||
{
|
||||
//can't absorb spells without feat
|
||||
if(!GetHasFeat(FEAT_SPELLFIRE_WIELDER, oPC)) return 0;
|
||||
|
||||
int nCON = GetAbilityScore(oPC, ABILITY_CONSTITUTION);
|
||||
|
||||
int i, nCount;
|
||||
for (i = FEAT_EPIC_SPELLFIRE_WIELDER_I; i <= FEAT_EPIC_SPELLFIRE_WIELDER_X; i++)
|
||||
{
|
||||
if (GetHasFeat(i, oPC))
|
||||
nCON = nCON + 4;
|
||||
}
|
||||
if (DEBUG) DoDebug("SpellfireMax nCon is "+IntToString(nCON));
|
||||
|
||||
int nStorage = ((GetLevelByClass(CLASS_TYPE_SPELLFIRE, oPC) + 1) / 2) + 1;
|
||||
if(nStorage > 5) nStorage = 5;
|
||||
return nCON * nStorage;
|
||||
}
|
||||
|
||||
//Increases the number of stored spellfire levels on a creature
|
||||
void AddSpellfireLevels(object oPC, int nLevels)
|
||||
{
|
||||
int nMax = SpellfireMax(oPC);
|
||||
int nStored = GetPersistantLocalInt(oPC, "SpellfireLevelStored");
|
||||
nStored += nLevels;
|
||||
if(nStored > nMax) nStored = nMax; //capped
|
||||
SetPersistantLocalInt(oPC, "SpellfireLevelStored", nStored);
|
||||
}
|
||||
|
||||
//Checks if spell target can absorb spells by being a spellfire wielder
|
||||
int CheckSpellfire(object oCaster, object oTarget, int bFriendly = FALSE)
|
||||
{
|
||||
//can't absorb spells without feat
|
||||
if(!GetHasFeat(FEAT_SPELLFIRE_WIELDER, oTarget)) return 0;
|
||||
|
||||
//Can't absorb own spells/powers if switch is set
|
||||
if(GetPRCSwitch(PRC_SPELLFIRE_DISALLOW_CHARGE_SELF) && oTarget == oCaster) return 0;
|
||||
|
||||
//abilities rely on access to weave
|
||||
if(GetHasFeat(FEAT_SHADOWWEAVE, oTarget)) return 0;
|
||||
|
||||
int nSpellID = PRCGetSpellId();
|
||||
if(!bFriendly && GetLocalInt(oCaster, "IsAOE_" + IntToString(nSpellID)))
|
||||
return 0; //can't absorb hostile AOE spells
|
||||
|
||||
int nSpellfireLevel = GetPersistantLocalInt(oTarget, "SpellfireLevelStored");
|
||||
if(DEBUG) DoDebug("CheckSpellfire: " + IntToString(nSpellfireLevel) + " levels stored", oTarget);
|
||||
|
||||
int nMax = SpellfireMax(oTarget);
|
||||
|
||||
if(DEBUG) DoDebug("CheckSpellfire: Maximum " + IntToString(nMax), oTarget);
|
||||
|
||||
//can't absorb any more spells, sanity check
|
||||
if(nSpellfireLevel >= nMax) return 0;
|
||||
|
||||
//increasing stored levels
|
||||
int nSpellLevel = GetLocalInt(oCaster, "PRC_CurrentManifest_PowerLevel"); //replicates GetPowerLevel(oCaster);
|
||||
if(!nSpellLevel) //not a power //avoids compiler problems
|
||||
{ //with includes
|
||||
string sInnate = Get2DACache("spells", "Innate", nSpellID);//lookup_spell_innate(nSpellID);
|
||||
if(sInnate == "") return 0; //no innate level, unlike cantrips
|
||||
nSpellLevel = StringToInt(sInnate);
|
||||
}
|
||||
/*
|
||||
string sInnate = Get2DACache("spells", "Innate", nSpellID);
|
||||
if(sInnate == "") return 0; //no innate level, unlike cantrips
|
||||
int nSpellLevel = StringToInt(sInnate);
|
||||
*/
|
||||
|
||||
AddSpellfireLevels(oTarget, nSpellLevel);
|
||||
|
||||
//absorbed
|
||||
return 1;
|
||||
}
|
||||
|
||||
//:: void main(){}
|
||||
Reference in New Issue
Block a user