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<65>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<65>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
|
|||
|
);
|
|||
|
}
|