Further file organization
Further file organization
This commit is contained in:
255
nwn/nwnprc/trunk/psionics/psi_pow_fatelink.nss
Normal file
255
nwn/nwnprc/trunk/psionics/psi_pow_fatelink.nss
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
----------------
|
||||
Fate Link
|
||||
|
||||
psi_pow_fatelink
|
||||
----------------
|
||||
|
||||
15/7/05 by Stratovarius
|
||||
*/ /** @file
|
||||
|
||||
Fate Link
|
||||
|
||||
Clairsentience
|
||||
Level: Seer 3
|
||||
Manifesting Time: 1 standard action
|
||||
Range: Close (25 ft. + 5 ft./2 levels)
|
||||
Target: Any two living creatures that are initially no more than 30 ft. apart.
|
||||
Duration: 10 min./level
|
||||
Saving Throw: Will negates
|
||||
Power Resistance: Yes
|
||||
Power Points: 5
|
||||
Metapsionics: Extend
|
||||
|
||||
You temporarily link the fates of any two creatures, if both fail their
|
||||
saving throws. If either linked creature experiences pain, both feel it.
|
||||
When one loses hit points, the other loses the same amount. If one takes
|
||||
nonlethal damage, so does the other. If one creature is subjected to an
|
||||
effect to which it is immune (such as a type of energy damage), the linked
|
||||
creature is not subjected to it either. If one dies, the other must
|
||||
immediately succeed on a Fortitude save against this power's save DC or gain
|
||||
two negative levels.
|
||||
|
||||
No other effects are transferred by the fate link.
|
||||
|
||||
Augment: For every 2 additional power points you spend, this power's save DC
|
||||
increases by 1.
|
||||
*/
|
||||
|
||||
#include "psi_inc_psifunc"
|
||||
#include "psi_inc_pwresist"
|
||||
#include "psi_spellhook"
|
||||
#include "prc_inc_spells"
|
||||
|
||||
//ebonfowl: new HB loop to handle damage, no more on hit
|
||||
void FateLinkHB (object oTarget1, object oTarget2);
|
||||
|
||||
void DispelMonitor(object oManifester, object oTarget1, object oTarget2, int nSpellID, int nBeatsRemaining);
|
||||
|
||||
void main()
|
||||
{
|
||||
if(!PsiPrePowerCastCode()) return;
|
||||
|
||||
object oManifester = OBJECT_SELF;
|
||||
object oFirstTarget = PRCGetSpellTargetObject();
|
||||
struct manifestation manif =
|
||||
EvaluateManifestation(oManifester, oFirstTarget,
|
||||
PowerAugmentationProfile(PRC_NO_GENERIC_AUGMENTS,
|
||||
2, PRC_UNLIMITED_AUGMENTATION
|
||||
),
|
||||
METAPSIONIC_EXTEND
|
||||
);
|
||||
|
||||
if(manif.bCanManifest)
|
||||
{
|
||||
int nDC = GetManifesterDC(oManifester) + manif.nTimesAugOptUsed_1;
|
||||
int nPen = GetPsiPenetration(oManifester);
|
||||
int nHP1 = GetCurrentHitPoints(oFirstTarget);
|
||||
int nRacialType, nHP2;
|
||||
effect eVis = EffectVisualEffect(VFX_IMP_HEAD_MIND);
|
||||
effect eDur = EffectVisualEffect(VFX_DUR_PROTECTION_EVIL_MINOR);
|
||||
location lTarget = GetLocation(oFirstTarget);
|
||||
float fRadius = FeetToMeters(30.0f);
|
||||
float fDuration = 600.0f * manif.nManifesterLevel;
|
||||
if(manif.bExtend) fDuration *= 2;
|
||||
|
||||
// Make sure the first target is alive
|
||||
nRacialType = MyPRCGetRacialType(oFirstTarget);
|
||||
if((nRacialType != RACIAL_TYPE_CONSTRUCT || GetIsWarforged(oFirstTarget)) &&
|
||||
nRacialType != RACIAL_TYPE_UNDEAD &&
|
||||
!GetIsDead(oFirstTarget)
|
||||
)
|
||||
{
|
||||
// Get the second target to link with the first
|
||||
object oSecondTarget = OBJECT_INVALID;
|
||||
object oTest = MyFirstObjectInShape(SHAPE_SPHERE, fRadius, lTarget, TRUE, OBJECT_TYPE_CREATURE);
|
||||
while(GetIsObjectValid(oTest))
|
||||
{
|
||||
// Reasonable targeting
|
||||
if(oTest != oManifester &&
|
||||
spellsIsTarget(oTest, SPELL_TARGET_SELECTIVEHOSTILE, oManifester)
|
||||
)
|
||||
{
|
||||
// Livingness check
|
||||
nRacialType = MyPRCGetRacialType(oTest);
|
||||
if((nRacialType != RACIAL_TYPE_CONSTRUCT || GetIsWarforged(oTest)) &&
|
||||
nRacialType != RACIAL_TYPE_UNDEAD &&
|
||||
!GetIsDead(oTest)
|
||||
)
|
||||
{
|
||||
// Found the target
|
||||
oSecondTarget = oTest;
|
||||
// End loop
|
||||
break;
|
||||
}// end if - The target is alive
|
||||
}// end if - Target validity check
|
||||
}// end while - Target loop
|
||||
|
||||
nHP2 = GetCurrentHitPoints(oSecondTarget);
|
||||
|
||||
// Make sure we have two valid targets
|
||||
if(GetIsObjectValid(oFirstTarget) && GetIsObjectValid(oSecondTarget))
|
||||
{
|
||||
// Let the AI know
|
||||
PRCSignalSpellEvent(oFirstTarget, TRUE, manif.nSpellID, oManifester);
|
||||
PRCSignalSpellEvent(oSecondTarget, TRUE, manif.nSpellID, oManifester);
|
||||
|
||||
// SR checks
|
||||
if(PRCMyResistPower(oManifester, oFirstTarget, nPen) &&
|
||||
PRCMyResistPower(oManifester, oSecondTarget, nPen)
|
||||
)
|
||||
{
|
||||
if(!PRCMySavingThrow(SAVING_THROW_WILL, oFirstTarget, nDC, SAVING_THROW_TYPE_NONE) &&
|
||||
!PRCMySavingThrow(SAVING_THROW_WILL, oSecondTarget, nDC, SAVING_THROW_TYPE_NONE)
|
||||
)
|
||||
{
|
||||
// No stacking - concurrency issues
|
||||
if(!GetLocalInt(oFirstTarget, "PRC_Power_FateLink_DC") &&
|
||||
!GetLocalInt(oSecondTarget, "PRC_Power_FateLink_DC")
|
||||
)
|
||||
{
|
||||
// Impact visuals
|
||||
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oFirstTarget);
|
||||
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oSecondTarget);
|
||||
|
||||
// VFX for the monitor to look for
|
||||
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDur, oFirstTarget, fDuration, TRUE, manif.nSpellID, manif.nManifesterLevel);
|
||||
SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDur, oSecondTarget, fDuration, TRUE, manif.nSpellID, manif.nManifesterLevel);
|
||||
|
||||
// Set up the Fate Link locals
|
||||
SetLocalInt(oFirstTarget, "PRC_Power_FateLink_DC", nDC);
|
||||
SetLocalInt(oSecondTarget, "PRC_Power_FateLink_DC", nDC);
|
||||
SetLocalObject(oFirstTarget, "PRC_Power_FateLink_LinkedTo", oSecondTarget);
|
||||
SetLocalObject(oSecondTarget, "PRC_Power_FateLink_LinkedTo", oFirstTarget);
|
||||
SetLocalInt(oFirstTarget, "FateLinkHP", nHP1);
|
||||
SetLocalInt(oSecondTarget, "FateLinkHP", nHP2);
|
||||
|
||||
//ebonfowl: Start HB
|
||||
FateLinkHB(oFirstTarget, oSecondTarget);
|
||||
|
||||
// Start end monitor
|
||||
DelayCommand(6.0f, DispelMonitor(oManifester, oFirstTarget, oSecondTarget, manif.nSpellID, FloatToInt(fDuration) / 6));
|
||||
}// end if - Neither of the targets is already affected by Fate Link
|
||||
}// end if - Targets failed their saves
|
||||
}// end if - Targets failed SR
|
||||
}// end if - Both targets are valid
|
||||
}// end if - The first target is living
|
||||
}// end if - Successfull manifestation
|
||||
}
|
||||
|
||||
void FateLinkHB (object oTarget1, object oTarget2)
|
||||
{
|
||||
int nDC = GetLocalInt(oTarget1, "PRC_Power_FateLink_DC");
|
||||
|
||||
effect eDrain = EffectNegativeLevel(2);
|
||||
|
||||
//Death checks
|
||||
if(GetIsDead(oTarget1))
|
||||
{
|
||||
if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget2, nDC, SAVING_THROW_TYPE_NONE))
|
||||
{
|
||||
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDrain, oTarget2, HoursToSeconds(24));
|
||||
}
|
||||
|
||||
//Kill the HB
|
||||
SetLocalInt(oTarget2, "FateLinkStop", TRUE);
|
||||
DelayCommand(1.0, DeleteLocalInt(oTarget2, "FateLinkStop"));
|
||||
}
|
||||
if(GetIsDead(oTarget2))
|
||||
{
|
||||
if(!PRCMySavingThrow(SAVING_THROW_WILL, oTarget1, nDC, SAVING_THROW_TYPE_NONE))
|
||||
{
|
||||
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDrain, oTarget1, HoursToSeconds(24));
|
||||
}
|
||||
|
||||
//Kill the HB
|
||||
SetLocalInt(oTarget1, "FateLinkStop", TRUE);
|
||||
DelayCommand(1.0, DeleteLocalInt(oTarget1, "FateLinkStop"));
|
||||
}
|
||||
|
||||
//Now check for damage
|
||||
int nCurHP1 = GetCurrentHitPoints(oTarget1);
|
||||
int nPreHP1 = GetLocalInt(oTarget1, "FateLinkHP");
|
||||
int nCurHP2 = GetCurrentHitPoints(oTarget2);
|
||||
int nPreHP2 = GetLocalInt(oTarget2, "FateLinkHP");
|
||||
int nDamageTaken1, nDamageTaken2, nNewHP1, nNewHP2;
|
||||
|
||||
//Calculate damage taken
|
||||
if (nPreHP1 > nCurHP1) nDamageTaken1 = nPreHP1 - nCurHP1;
|
||||
if (nPreHP2 > nCurHP2) nDamageTaken2 = nPreHP2 - nCurHP2;
|
||||
|
||||
//This could also be done with a damage effect, but I like this way as nothing can resist it
|
||||
if (nPreHP2 > nCurHP2)
|
||||
{
|
||||
//Reduce first target's HP
|
||||
nNewHP1 = nCurHP1 - nDamageTaken2;
|
||||
SetCurrentHitPoints(oTarget1, nNewHP1);
|
||||
}
|
||||
|
||||
if (nPreHP1 > nCurHP1)
|
||||
{
|
||||
//Reduce second target's HP
|
||||
nNewHP2 = nCurHP2 - nDamageTaken1;
|
||||
SetCurrentHitPoints(oTarget2, nNewHP2);
|
||||
}
|
||||
|
||||
//New HP total to transfer to next HB - try and use the new HP calculation to hedge against intra-script damage
|
||||
if (nNewHP1 > 0) SetLocalInt(oTarget1, "FateLinkHP", nNewHP1);
|
||||
else SetLocalInt(oTarget1, "FateLinkHP", GetCurrentHitPoints(oTarget1));
|
||||
|
||||
if (nNewHP2 > 0) SetLocalInt(oTarget2, "FateLinkHP", nNewHP2);
|
||||
else SetLocalInt(oTarget2, "FateLinkHP", GetCurrentHitPoints(oTarget2));
|
||||
|
||||
if (!GetLocalInt(oTarget1, "FateLinkStop") && !GetLocalInt(oTarget2, "FateLinkStop"))
|
||||
DelayCommand(0.25, FateLinkHB(oTarget1, oTarget2));
|
||||
}
|
||||
|
||||
void DispelMonitor(object oManifester, object oTarget1, object oTarget2, int nSpellID, int nBeatsRemaining)
|
||||
{
|
||||
// Has the power ended since the last beat, or does the duration run out now
|
||||
if((--nBeatsRemaining == 0) ||
|
||||
PRCGetDelayedSpellEffectsExpired(nSpellID, oTarget1, oManifester) ||
|
||||
PRCGetDelayedSpellEffectsExpired(nSpellID, oTarget2, oManifester)
|
||||
)
|
||||
{
|
||||
if(DEBUG) DoDebug("psi_pow_fatelink: Clearing");
|
||||
// Clear the effect presence marker
|
||||
DeleteLocalInt(oTarget1, "PRC_Power_FateLink_DC");
|
||||
DeleteLocalInt(oTarget2, "PRC_Power_FateLink_DC");
|
||||
DeleteLocalObject(oTarget1, "PRC_Power_FateLink_LinkedTo");
|
||||
DeleteLocalObject(oTarget2, "PRC_Power_FateLink_LinkedTo");
|
||||
|
||||
//ebonfowl: kill the new HB function
|
||||
SetLocalInt(oTarget1, "FateLinkStop", TRUE);
|
||||
DelayCommand(1.0, DeleteLocalInt(oTarget1, "FateLinkStop"));
|
||||
SetLocalInt(oTarget2, "FateLinkStop", TRUE);
|
||||
DelayCommand(1.0, DeleteLocalInt(oTarget2, "FateLinkStop"));
|
||||
|
||||
// Remove remaining effects
|
||||
//ebonfowl: will this remove the negative levels prematurely if one target died? If so, this should be done with TagEffect for more control
|
||||
PRCRemoveSpellEffects(nSpellID, oManifester, oTarget1);
|
||||
PRCRemoveSpellEffects(nSpellID, oManifester, oTarget2);
|
||||
}
|
||||
else
|
||||
DelayCommand(6.0f, DispelMonitor(oManifester, oTarget1, oTarget2, nSpellID, nBeatsRemaining));
|
||||
}
|
Reference in New Issue
Block a user