228 lines
9.5 KiB
Plaintext
228 lines
9.5 KiB
Plaintext
/*
|
||
----------------
|
||
Tornado Blast
|
||
|
||
psi_pow_trnblst
|
||
----------------
|
||
|
||
6/10/05 by Stratovarius
|
||
*/ /** @file
|
||
|
||
Tornado Blast
|
||
|
||
Psychokinesis
|
||
Level: Kineticist 9
|
||
Manifesting Time: 1 round
|
||
Range: Long (400 ft. + 40 ft./level)
|
||
Area: 40-ft.-radius spread
|
||
Duration: Instantaneous
|
||
Saving Throw: Reflex half; see text
|
||
Power Resistance: No
|
||
Power Points: 17
|
||
Metapsionics: Empower, Maximize, Twin, Widen
|
||
|
||
You induce the formation of a slender vortex of fiercely swirling air. When
|
||
you manifest it, a vortex of air visibly and audibly snakes out from your
|
||
outstretched hand.
|
||
|
||
If you want to aim the vortex at a specific creature, you can make a ranged
|
||
touch attack to strike the creature. If you succeed, direct contact with the
|
||
vortex deals 8d6 points of damage to the creature (no save).
|
||
|
||
Regardless of whether your ranged touch attack hits (and even if you forgo
|
||
the attack), all creatures in the area (including the one possibly damaged
|
||
by direct contact) are picked up and violently dashed about, dealing 17d6
|
||
points of damage to each one. Creatures that make a successful Reflex save
|
||
take half damage.
|
||
|
||
After being dashed about, each creature that was affected finds itself
|
||
situated in a new space 1d4 x 10 feet away from its original space in a
|
||
random direction. Walls and other barriers can restrict this relocation; in
|
||
such a case, the creature ends up adjacent to the barrier.
|
||
|
||
Augment: For every additional power point you spend, this power’s area
|
||
damage (not the damage from direct contact dealt to a specific
|
||
creature) increases by 1d6 points. For each extra 2d6 points of
|
||
damage, this power’s save DC increases by 1.
|
||
|
||
|
||
@todo Find better VFX
|
||
*/
|
||
|
||
#include "psi_inc_psifunc"
|
||
#include "psi_inc_pwresist"
|
||
#include "psi_spellhook"
|
||
#include "prc_inc_sp_tch"
|
||
|
||
void DoRandomMove(object oTarget);
|
||
|
||
void main()
|
||
{
|
||
/*
|
||
Spellcast Hook Code
|
||
Added 2004-11-02 by Stratovarius
|
||
If you want to make changes to all powers,
|
||
check psi_spellhook to find out more
|
||
|
||
*/
|
||
|
||
if (!PsiPrePowerCastCode())
|
||
{
|
||
// If code within the PrePowerCastHook (i.e. UMD) reports FALSE, do not run this spell
|
||
return;
|
||
}
|
||
|
||
// End of Spell Cast Hook
|
||
|
||
object oManifester = OBJECT_SELF;
|
||
object oBallTarget = PRCGetSpellTargetObject();
|
||
struct manifestation manif =
|
||
EvaluateManifestation(oManifester, oBallTarget,
|
||
PowerAugmentationProfile(2,
|
||
1, PRC_UNLIMITED_AUGMENTATION
|
||
),
|
||
METAPSIONIC_EMPOWER | METAPSIONIC_MAXIMIZE | METAPSIONIC_TWIN | METAPSIONIC_WIDEN
|
||
);
|
||
|
||
if(manif.bCanManifest)
|
||
{
|
||
int nDC = GetManifesterDC(oManifester);
|
||
int nPen = GetPsiPenetration(oManifester);
|
||
int nNumberOfDice_Explode = 17 + manif.nTimesAugOptUsed_1;
|
||
int nNumberOfDice_Ball = 8;
|
||
int nDieSize = 6;
|
||
int nDamage, nTouchAttack;
|
||
location lTarget = PRCGetSpellTargetLocation();
|
||
effect eImpact = EffectVisualEffect(VFX_IMP_PULSE_WIND);
|
||
effect eExplode = EffectVisualEffect(VFX_FNF_STORM);
|
||
effect eDamage;
|
||
float fRadius = EvaluateWidenPower(manif, FeetToMeters(40.0f));
|
||
object oExplodeTarget;
|
||
|
||
// Handle Twin Power
|
||
int nRepeats = manif.bTwin ? 2 : 1;
|
||
for(; nRepeats > 0; nRepeats--)
|
||
{
|
||
// Check if a particular creature was targeted
|
||
if(GetIsObjectValid(oBallTarget))
|
||
{
|
||
// Let the AI know
|
||
PRCSignalSpellEvent(oBallTarget, TRUE, manif.nSpellID, oManifester);
|
||
|
||
// Roll touch attack
|
||
nTouchAttack = PRCDoRangedTouchAttack(oBallTarget);
|
||
if(nTouchAttack > 0)
|
||
{
|
||
// Roll damage
|
||
nDamage = MetaPsionicsDamage(manif, nDieSize, nNumberOfDice_Ball, 0, 0, TRUE, TRUE);
|
||
// Target-specific stuff
|
||
nDamage = GetTargetSpecificChangesToDamage(oBallTarget, oManifester, nDamage, TRUE, FALSE);
|
||
|
||
// Apply the damage and VFX. No Reflex save here, they got hit by a touch attack
|
||
ApplyTouchAttackDamage(oManifester, oBallTarget, nTouchAttack, nDamage, DAMAGE_TYPE_MAGICAL);
|
||
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, oBallTarget);
|
||
}// end if - Touch attack hit
|
||
}// end if - A creature was targeted with the ball
|
||
|
||
// Either way, shrapnel time
|
||
ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eExplode, lTarget);
|
||
oExplodeTarget = MyFirstObjectInShape(SHAPE_SPHERE, fRadius, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
|
||
while(GetIsObjectValid(oExplodeTarget))
|
||
{
|
||
if(spellsIsTarget(oExplodeTarget, SPELL_TARGET_STANDARDHOSTILE, oManifester))
|
||
{
|
||
// Let the AI know
|
||
PRCSignalSpellEvent(oExplodeTarget, TRUE, manif.nSpellID, oManifester);
|
||
|
||
// Roll damage
|
||
nDamage = MetaPsionicsDamage(manif, nDieSize, nNumberOfDice_Explode, 0, 0, TRUE, TRUE);
|
||
// Target-specific stuff
|
||
nDamage = GetTargetSpecificChangesToDamage(oExplodeTarget, oManifester, nDamage, TRUE, FALSE);
|
||
// Adjust damage according to Reflex Save, Evasion or Improved Evasion
|
||
nDamage = PRCGetReflexAdjustedDamage(nDamage, oExplodeTarget, nDC, SAVING_THROW_TYPE_NONE);
|
||
|
||
// Apply effects if the target didn't Evade
|
||
if(nDamage > 0)
|
||
{
|
||
eDamage = EffectDamage(nDamage, DAMAGE_TYPE_BLUDGEONING, DAMAGE_POWER_ENERGY);
|
||
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oExplodeTarget);
|
||
SPApplyEffectToObject(DURATION_TYPE_INSTANT, eImpact, oExplodeTarget);
|
||
|
||
// Toss the target around. Only applies to creatures to avoid messing modules up
|
||
if(GetObjectType(oExplodeTarget) == OBJECT_TYPE_CREATURE)
|
||
DoRandomMove(oExplodeTarget);
|
||
}// end if - There was still damage remaining to be dealt after adjustments
|
||
}// end if - Difficulty check
|
||
|
||
// Get next target
|
||
oExplodeTarget = MyNextObjectInShape(SHAPE_SPHERE, fRadius, lTarget, TRUE, OBJECT_TYPE_CREATURE | OBJECT_TYPE_DOOR | OBJECT_TYPE_PLACEABLE);
|
||
}// end while - Explosion target loop
|
||
}// end for - Twin Power
|
||
}// end if - Successfull manifestation
|
||
}
|
||
|
||
void DoRandomMove(object oTarget)
|
||
{
|
||
// Calculate the new location
|
||
object oArea = GetArea(oTarget);
|
||
float fDistance = FeetToMeters(10.0f * d4());
|
||
float fAngle = IntToFloat(Random(3600)) / 10.0;
|
||
float fFacing = IntToFloat(Random(3600)) / 10.0;
|
||
vector vOrigin = GetPosition(oTarget);
|
||
vector vAngle = AngleToVector(fAngle);
|
||
vector vTarget = vOrigin + (vAngle * fDistance);
|
||
|
||
// If there is blocking geometry on the way, determine where
|
||
if(!LineOfSightVector(vOrigin, vTarget))
|
||
{
|
||
float fEpsilon = 1.0f; // Search precision
|
||
float fLowerBound = 0.0f; // The lower search bound, initialise to 0
|
||
float fUpperBound = fDistance; // The upper search bound, initialise to the initial distance
|
||
fDistance = fDistance / 2; // The search position, set to middle of the range
|
||
|
||
do{
|
||
// Create test vector for this iteration
|
||
vTarget = vOrigin + (vAngle * fDistance);
|
||
|
||
// Determine which bound to move.
|
||
if(LineOfSightVector(vOrigin, vTarget))
|
||
fLowerBound = fDistance;
|
||
else
|
||
fUpperBound = fDistance;
|
||
|
||
// Get the new middle point
|
||
fDistance = (fUpperBound + fLowerBound) / 2;
|
||
}while(fabs(fUpperBound - fLowerBound) > fEpsilon);
|
||
}// end if - The location was blocked by geometry
|
||
|
||
// Create the final target vector
|
||
vTarget = vOrigin + (vAngle * fDistance);
|
||
|
||
// Move the target
|
||
int bCommandable = GetCommandable(oTarget);
|
||
location lTargetDestination = Location(oArea, vTarget, fFacing);
|
||
float fDelay = PRCGetRandomDelay();
|
||
|
||
// Set commandable if it isn't already
|
||
/* if(!bCommandable)
|
||
SetCommandable(TRUE, oTarget);*/
|
||
|
||
// Assign jump commands
|
||
AssignCommand(oTarget, ClearAllActions(TRUE));
|
||
AssignCommand(oTarget, JumpToLocation(lTargetDestination));
|
||
|
||
// Set commandability off it it was off in the beginning
|
||
/* if(!bCommandable)
|
||
AssignCommand(oTarget, ActionDoCommand(SetCommandable(FALSE, oTarget)));*/
|
||
/*DoDebug("DoRandomMove(): Jumping creature " + DebugObject2Str(oTarget) + "\n"
|
||
+ "Old location = " + DebugLocation2Str(GetLocation(oTarget)) + "\n"
|
||
+ "New location = " + DebugLocation2Str(lTargetDestination) + "\n"
|
||
+ "Commandability = " + DebugBool2String(bCommandable) + "\n"
|
||
);*/
|
||
// Do some VFX during the jump delay
|
||
DrawLineFromVectorToVector(DURATION_TYPE_INSTANT, VFX_IMP_WIND, oArea, vOrigin, vTarget, 0.0f,
|
||
FloatToInt(GetDistanceBetweenLocations(GetLocation(oTarget), lTargetDestination)), // One VFX every meter
|
||
fDelay
|
||
);
|
||
}
|