Updated Vow of Poverty. Added Sanctify Ki Strike, Holy Strike, Fist of Heavens, Vow of Abstinence, Vow of Chastity & Gift of Faith. (@fenac). Turned off the Taunt & Parry skills. Re-disabled AC & save bonuses from Tumble & Spellcraft. Updated min() & max() to PRCmin() & PRCmax() to not conflict with similarly named NUI adjacent functions. Set Point Blank Shot to 30' per PnP. Added icon for Chosen of Evil. Started work on Hidden Talent. Created Psionics function cheatsheet. Updated release archive.
262 lines
9.9 KiB
Plaintext
262 lines
9.9 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: Soul Eater: Energy Drain
|
|
//:: prc_sleat_edrain
|
|
//:://////////////////////////////////////////////
|
|
/** @file
|
|
Implements all of the Soul Eater's Energy Drain
|
|
-related abilities.
|
|
|
|
Energy Drain (Su): A soul eater gains the ability to drain energy, bestowing
|
|
negative levels upon it's victims. Beginning at 1st level, the touch of a
|
|
soul eater bestows one negative level on it's target. At 7th level, the
|
|
soul eater bestows two negative levels with a touch.
|
|
|
|
Soul Strength (Su): When a 2nd-level soul eater uses it's energy drain
|
|
ability, it gains a +4 bonus to Strenth for 24 hours.
|
|
|
|
Soul Enhancement (Su): When a 4th-level soul eater uses it's energy drain
|
|
ability, it gains a +2 enhancement bonus on all saving throws and skill
|
|
checks for 24 hours.
|
|
|
|
Soul Endurance (Su): When a 5nd-level soul eater uses it's energy drain
|
|
ability, it gains a +4 bonus to Constitution for 24 hours.
|
|
|
|
Soul Radiance (Su): If a 6th-level soul eater completely drains a creature
|
|
of energy, it may adopt the creature's soul radiance, taking the victim's
|
|
form, appearance, and abilities for 24 hours.
|
|
|
|
Soul Agility (Su): When a 8th-level soul eater uses it's energy drain
|
|
ability, it gains a +4 bonus to Dexterity for 24 hours.
|
|
|
|
Soul Slave (Su): If a 9th-level soul eater completely drains a creature of
|
|
energy, the victim becomes a wight under the command of the soul eater.
|
|
|
|
Soul Power (Su): After a 10th-level soul eater has drained energy, all
|
|
spell-like and supernatural abilities gain a +2 profane bonus to their
|
|
saving throw DC for 24 hours. Further, it may use it's Soul Blast ability
|
|
up to two times instead of one during that 24-hour period.
|
|
|
|
@date Modified - 04.12.2006
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:://////////////////////////////////////////////
|
|
|
|
#include "prc_inc_sp_tch"
|
|
#include "prc_inc_shifting"
|
|
#include "prc_spell_const"
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function prototypes */
|
|
//////////////////////////////////////////////////
|
|
|
|
void DoEnergyDrain(object oEater, object oTarget, int nDamage);
|
|
void DoDeathDependent(object oEater, object oTarget, string sResRef, string sName);
|
|
void LevelUpWight(int nTargetLevel, object oCreature);
|
|
void IncrementMarker(object oEater);
|
|
void DecrementMarker(object oEater);
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function definitions */
|
|
//////////////////////////////////////////////////
|
|
|
|
void main()
|
|
{
|
|
object oEater = OBJECT_SELF;
|
|
object oTarget = PRCGetSpellTargetObject();
|
|
object oItem = PRCGetSpellCastItem();
|
|
effect eImpact = EffectVisualEffect(VFX_IMP_REDUCE_ABILITY_SCORE);
|
|
int nDrain = GetLevelByClass(CLASS_TYPE_SOUL_EATER, oEater) < 7 ? 1 : 2;
|
|
|
|
// Sanity check - can't affect self or dead stuff. Also, check PvP limits
|
|
if(oTarget == oEater ||
|
|
GetIsDead(oTarget) ||
|
|
!spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oEater)
|
|
)
|
|
return;
|
|
|
|
// Let the target's AI know about hostile action
|
|
SignalEvent(oTarget, EventSpellCastAt(oEater, PRCGetSpellId(), TRUE));
|
|
|
|
int bHit = FALSE;
|
|
if (GetIsObjectValid(oItem))
|
|
{
|
|
//If happening due to an On Hit Cast Spell: skip the touch attack (we've already touched when we hit)
|
|
//Have to use local variable on the item because GetSpellCastItem() can return valid object from
|
|
//previously cast spells that are not this spell at all.
|
|
//TODO: Is there a better way to do this?
|
|
bHit = GetLocalInt(oItem, "PRC_SOULEATER_ONHIT_ENERGYDRAIN");
|
|
}
|
|
else
|
|
{
|
|
// Melee touch attack to actually do anything
|
|
bHit = PRCDoMeleeTouchAttack(oTarget, TRUE, oEater);
|
|
}
|
|
|
|
if(bHit)
|
|
{
|
|
ApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, oTarget);
|
|
DoEnergyDrain(oEater, oTarget, nDrain);
|
|
}
|
|
}
|
|
|
|
void DoEnergyDrain(object oEater, object oTarget,int nDamage)
|
|
{
|
|
// Immunity prevents anything from actually happening
|
|
if(!GetIsImmune(oTarget, IMMUNITY_TYPE_NEGATIVE_LEVEL))
|
|
{
|
|
// Apply the actual drain
|
|
effect eDrain = SupernaturalEffect(EffectNegativeLevel(nDamage));
|
|
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eDrain, oTarget);
|
|
|
|
// Update marker
|
|
IncrementMarker(oEater);
|
|
DelayCommand(HoursToSeconds(24), DecrementMarker(oEater));
|
|
|
|
|
|
/// Soul X side effects
|
|
// Clear out old effects
|
|
PRCRemoveSpellEffects(PRCGetSpellId(), oEater, oEater);
|
|
|
|
// Generate new effects
|
|
int nClassLevel = GetLevelByClass(CLASS_TYPE_SOUL_EATER, oEater);
|
|
effect eSideEffect = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE);
|
|
|
|
// Soul Strength
|
|
if(nClassLevel >= 2)
|
|
{
|
|
eSideEffect = EffectLinkEffects(eSideEffect, EffectAbilityIncrease(ABILITY_STRENGTH, 4));
|
|
}
|
|
|
|
// Soul Enchancement
|
|
if(nClassLevel >= 4)
|
|
{
|
|
eSideEffect = EffectLinkEffects(eSideEffect, EffectSavingThrowIncrease(SAVING_THROW_TYPE_ALL, 2));
|
|
eSideEffect = EffectLinkEffects(eSideEffect, EffectSkillIncrease(SKILL_ALL_SKILLS, 2));
|
|
}
|
|
|
|
// Soul Endurance
|
|
if(nClassLevel >= 5)
|
|
{
|
|
eSideEffect = EffectLinkEffects(eSideEffect, EffectAbilityIncrease(ABILITY_CONSTITUTION, 4));
|
|
}
|
|
|
|
// Soul Agility
|
|
if(nClassLevel >= 8)
|
|
{
|
|
eSideEffect = EffectLinkEffects(eSideEffect, EffectAbilityIncrease(ABILITY_DEXTERITY, 4));
|
|
}
|
|
|
|
// Apply the gathered side effects. All the abilities are supernatural and last 24h
|
|
ApplyEffectToObject(DURATION_TYPE_TEMPORARY,
|
|
SupernaturalEffect(eSideEffect),
|
|
oEater,
|
|
HoursToSeconds(24)
|
|
);
|
|
|
|
|
|
// Soul Power
|
|
// Rebalanced to give +2 to all DCs and just double Soul Blast uses, due to it not being sanely
|
|
// possible to find out all use-limited abilities one may have
|
|
if(nClassLevel >= 10)
|
|
{
|
|
// +2 DCs
|
|
// Handled based on "PRC_SoulEater_HasDrained" and class level in the relevant places
|
|
|
|
// 2x special abilities uses
|
|
//IncrementRemainingFeatUses(oEater, FEAT_SLEAT_SBLAST); // Handled via 2da instead
|
|
}
|
|
|
|
|
|
|
|
// Soul Radiance and Soul Slave work only if the target was killed.
|
|
// And death by level loss only gets calculated once the script has terminated.
|
|
// Therefore, delay by 0.0
|
|
DelayCommand(0.0f, DoDeathDependent(oEater, oTarget, GetResRef(oTarget), GetName(oTarget)));
|
|
}
|
|
else
|
|
FloatingTextStrRefOnCreature(16832115, oEater, FALSE); // "Target is immune to negative levels"
|
|
}
|
|
|
|
void DoDeathDependent(object oEater, object oTarget, string sResRef, string sName)
|
|
{
|
|
// For anything to happen here, the target needs to be dead. And if it is, due to having only been delayed by 0
|
|
// we know that the only reason for it's death can have been level drain
|
|
if(GetIsDead(oTarget))
|
|
{
|
|
// Soul Radiance
|
|
if(GetLevelByClass(CLASS_TYPE_SOUL_EATER, oEater) >= 6)
|
|
{
|
|
// If the user has toggled Soul Radiance active, use the shifting code to turn into the target
|
|
if(GetLocalInt(oEater, "PRC_SoulEater_SoulRadianceActive"))
|
|
{
|
|
StoreCurrentAppearanceAsTrueAppearance(oEater, TRUE);
|
|
ShiftIntoCreature(oEater, SHIFTER_TYPE_SOULEATER, oTarget, TRUE); // Gain special abilities
|
|
//TODO: only if there are uses left
|
|
}
|
|
}
|
|
|
|
|
|
// Soul Slave
|
|
if(GetLevelByClass(CLASS_TYPE_SOUL_EATER, oEater) >= 9)
|
|
{
|
|
int nMaxHenchmen = GetMaxHenchmen();
|
|
int nNumSlaves = 0;
|
|
int nMaxSlaves = GetPRCSwitch(PRC_SOUL_EATER_MAX_SLAVES);
|
|
effect eSummon = EffectVisualEffect(VFX_FNF_SUMMON_UNDEAD);
|
|
|
|
// Special switch values handling
|
|
if(nMaxSlaves == 0 ) nMaxSlaves = 4;
|
|
if(nMaxSlaves == -1) nMaxSlaves = 0;
|
|
|
|
// Determine current number of slaves
|
|
int i = 1;
|
|
object oHench;
|
|
do {
|
|
oHench = GetAssociate(ASSOCIATE_TYPE_HENCHMAN, oEater, i++);
|
|
if(GetResRef(oHench) == "soul_wight_test")
|
|
nNumSlaves++;
|
|
} while(GetIsObjectValid(oHench));
|
|
|
|
// If we can add more wights, do so. Spawn the wight with some VFX at the corpse's location
|
|
if(nNumSlaves < nMaxSlaves)
|
|
{
|
|
location lSpawn = GetLocation(oTarget);
|
|
|
|
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eSummon, lSpawn);
|
|
|
|
object oSlave = CreateObject(OBJECT_TYPE_CREATURE, "soul_wight_test", lSpawn);
|
|
if(GetIsObjectValid(oSlave))
|
|
{
|
|
SetMaxHenchmen(PRCMax(nMaxHenchmen, i)); // Temporarily set the number of max henchmen high enough that we can add another
|
|
AddHenchman(oEater, oSlave);
|
|
SetMaxHenchmen(nMaxHenchmen);
|
|
|
|
// Level up the wight a bit to make it usefull. Needs to be delayed a bit to let the object creation routines happen first
|
|
DelayCommand(3.0f, LevelUpWight(GetHitDice(oEater) - 3, oSlave));
|
|
}
|
|
else if(DEBUG)
|
|
DoDebug("prc_sleat_edrain: ERROR: Failed to create wight at location " + DebugLocation2Str(lSpawn));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void LevelUpWight(int nTargetLevel, object oCreature)
|
|
{
|
|
int n;
|
|
for(n = 1; n < nTargetLevel; n++)
|
|
LevelUpHenchman(oCreature, CLASS_TYPE_INVALID, TRUE);
|
|
}
|
|
|
|
void IncrementMarker(object oEater)
|
|
{
|
|
SetLocalInt(oEater, "PRC_SoulEater_HasDrained", GetLocalInt(oEater, "PRC_SoulEater_HasDrained") + 1);
|
|
}
|
|
|
|
void DecrementMarker(object oEater)
|
|
{
|
|
SetLocalInt(oEater, "PRC_SoulEater_HasDrained", GetLocalInt(oEater, "PRC_SoulEater_HasDrained") - 1);
|
|
}
|