2025/12/11 Update

Inscribe Rune should be Craft Armor 1 not Craft General 1.
This commit is contained in:
Jaysyn904
2025-12-11 19:50:43 -05:00
parent 2735b3968f
commit 25bdd2ed4f
16 changed files with 5201 additions and 2 deletions

View File

@@ -2463,7 +2463,7 @@
2459 TactileTrapsmith 16824612 16824613 ife_lightflex **** **** **** **** **** **** **** **** **** **** 0 0 1 **** **** **** **** 1 **** **** **** **** **** **** **** **** **** **** **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 1 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 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 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 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 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 2465 ImprovedRunecasting 16831977 16831978 ife_dweomerthief **** **** **** **** **** **** **** **** **** **** 0 0 0 **** **** 1624 **** 1 -1 **** 1 **** **** **** **** **** **** **** **** **** FEAT_ALERTNESS 5 **** **** **** **** **** 0 0

View 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

View File

@@ -40753,7 +40753,7 @@ Epic Levels:
<entry id="54699" lang="en" sex="m">####END_OF_NEW_SPELLBOOK_RESERVE</entry> <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="54700" lang="en" sex="m">Inscribe Rune</entry>
<entry id="54701" lang="en" sex="m">Type of Feat: Item Creation <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. 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> Use: Selected.</entry>
<entry id="54702" lang="en" sex="m">Cast Rune</entry> <entry id="54702" lang="en" sex="m">Cast Rune</entry>

View File

@@ -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 (){}

View File

@@ -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 (){}

View File

@@ -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

View File

@@ -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 (){}

View File

@@ -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(){}

View File

@@ -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(){}

View File

@@ -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(){}

View File

@@ -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 (){}

View File

@@ -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(){}

View File

@@ -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(){}