/* ---------------- Energy Push psi_pow_enpush ---------------- 6/11/04 by Stratovarius */ /** @file Energy Push Psychokinesis [see text] Level: Psion/wilder 2 Manifesting Time: 1 standard action Range: Medium (100 ft. + 10 ft./ level) Duration: Instantaneous Saving Throw: Reflex half or Fortitude half; see text Power Resistance: Yes Power Points: 3 Metapsionics: Chain, Empower, Maximize, Twin Upon manifesting this power, you choose cold, electricity, fire, or sonic. You project a solid blast of energy of the chosen type at a target, dealing it 2d6 points of damage. In addition, if a subject of up to one size category larger than you fails a Strength check (DC equal to the save DC of this power), the driving force of the energy blast pushes it back 5 feet plus another 5 feet for every 5 points of damage it takes. If a wall or other solid object prevents the subject from being pushed back, the subject instead slams into the object and takes an extra 2d6 points of damage from the impact (no save). Cold: A blast of this energy type deals +1 point of damage per die (damage from impact remains at 2d6 points). The saving throw to reduce damage from a cold push is a Fortitude save instead of a Reflex save. Electricity: Manifesting a blast of this energy type provides a +2 bonus to the save DC and a +2 bonus on manifester level checks for the purpose of overcoming power resistance. Fire: A blast of this energy type deals +1 point of damage per die (damage from impact remains at 2d6 points). Sonic: A blast of this energy type deals -1 point of damage per die (damage from impact remains at 2d6 points) and ignores an object’s hardness. This power’s subtype is the same as the type of energy you manifest. Augment: For every 2 additional power points you spend, this power’s damage increases by one die (d6) and its save DC increases by 1. The damage increase applies to both the initial blast and any damage from impact with an object. */ #include "psi_inc_psifunc" #include "psi_inc_pwresist" #include "psi_spellhook" #include "prc_inc_spells" #include "psi_inc_enrgypow" void DoPush(object oTarget, object oManifester, int nDC, int nNumberOfDice, int nDamageDealt); 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 oMainTarget = PRCGetSpellTargetObject(); struct manifestation manif = EvaluateManifestation(oManifester, oMainTarget, PowerAugmentationProfile(PRC_NO_GENERIC_AUGMENTS, 2, PRC_UNLIMITED_AUGMENTATION ), METAPSIONIC_CHAIN | METAPSIONIC_EMPOWER | METAPSIONIC_MAXIMIZE | METAPSIONIC_TWIN ); if(manif.bCanManifest) { struct energy_adjustments enAdj = EvaluateEnergy(manif.nSpellID, POWER_ENERGYPUSH_COLD, POWER_ENERGYPUSH_ELEC, POWER_ENERGYPUSH_FIRE, POWER_ENERGYPUSH_SONIC, VFX_BEAM_COLD, VFX_BEAM_LIGHTNING, VFX_BEAM_FIRE, VFX_BEAM_MIND); int nDC = GetManifesterDC(oManifester) + manif.nTimesAugOptUsed_1 + enAdj.nDCMod; int nPen = GetPsiPenetration(oManifester) + enAdj.nPenMod; int nNumberOfDice = 2 + manif.nTimesAugOptUsed_1; int nDieSize = 6; int nOriginalDamage, nDamage, i; effect eVis = EffectVisualEffect(enAdj.nVFX1); effect eRay = EffectBeam(enAdj.nVFX2, oManifester, BODY_NODE_HAND); effect eDamage; object oChainTarget; // Determine Chain Power targets if(manif.bChain) EvaluateChainPower(manif, oMainTarget, TRUE); // Let the AI know PRCSignalSpellEvent(oMainTarget, TRUE, manif.nSpellID, oManifester); if(manif.bChain) for(i = 0; i < array_get_size(oManifester, PRC_CHAIN_POWER_ARRAY); i++) PRCSignalSpellEvent(array_get_object(oManifester, PRC_CHAIN_POWER_ARRAY, i), TRUE, manif.nSpellID, oManifester); // Handle Twin Power int nRepeats = manif.bTwin ? 2 : 1; for(; nRepeats > 0; nRepeats--) { // Make an SR check if(PRCMyResistPower(oManifester, oMainTarget, nPen)) { // Roll damage nDamage = MetaPsionicsDamage(manif, nDieSize, nNumberOfDice, 0, enAdj.nBonusPerDie, TRUE, FALSE); // Target-specific stuff nDamage = GetTargetSpecificChangesToDamage(oMainTarget, oManifester, nDamage, TRUE, TRUE); // Do save if(enAdj.nSaveType == SAVING_THROW_TYPE_COLD) { // Cold has a fort save for half if(PRCMySavingThrow(SAVING_THROW_FORT, oMainTarget, nDC, enAdj.nSaveType)) { if (GetHasMettle(oMainTarget, SAVING_THROW_FORT)) // This script does nothing if it has Mettle, bail nDamage = 0; nDamage /= 2; } } else // Adjust damage according to Reflex Save, Evasion or Improved Evasion nDamage = PRCGetReflexAdjustedDamage(nDamage, oMainTarget, nDC, enAdj.nSaveType); // Apply VFX and damage to chained target, assuming there is still damage left to deal after modification if(nDamage > 0) { // Apply damage eDamage = EffectDamage(nDamage, enAdj.nDamageType); SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oMainTarget); SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oMainTarget); SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, eRay, oMainTarget, 1.7, FALSE); // Do the push effect on the target DoPush(oMainTarget, oManifester, nDC, nNumberOfDice, nDamage); // Apply damage to Chain targets if(manif.bChain) { // Halve the damage nOriginalDamage = nDamage / 2; for(i = 0; i < array_get_size(oManifester, PRC_CHAIN_POWER_ARRAY); i++) { oChainTarget = array_get_object(oManifester, PRC_CHAIN_POWER_ARRAY, i); // Determine damage nDamage = nOriginalDamage; // Target-specific stuff nDamage = GetTargetSpecificChangesToDamage(oChainTarget, oManifester, nDamage, TRUE, TRUE); // Do save if(enAdj.nSaveType == SAVING_THROW_TYPE_COLD) { // Cold has a fort save for half if(PRCMySavingThrow(SAVING_THROW_FORT, oChainTarget, nDC, enAdj.nSaveType)) nDamage /= 2; } else // Adjust damage according to Reflex Save, Evasion or Improved Evasion nDamage = PRCGetReflexAdjustedDamage(nDamage, oChainTarget, nDC, enAdj.nSaveType); // Apply VFX and damage to chained target, assuming there is still damage left to deal after modification if(nDamage > 0) { eDamage = EffectDamage(nDamage, enAdj.nDamageType); SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oChainTarget); SPApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oChainTarget); SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(enAdj.nVFX2, oMainTarget, BODY_NODE_CHEST), oChainTarget, 1.7, FALSE); // Do the push effect on the target DoPush(oChainTarget, oManifester, nDC, nNumberOfDice, nDamage); }// end if - There was still damage remaining to be dealt after adjustments }// end for - Chain targets }// end if - Chain Power }// end if - There was still damage remaining to be dealt after adjustments }// end if - SR check }// end for - Twin Power }// end if - Successfull manifestation } void DoPush(object oTarget, object oManifester, int nDC, int nNumberOfDice, int nDamageDealt) { // Check size if(PRCGetCreatureSize(oTarget) <= (PRCGetCreatureSize(oManifester) + 1)) { // Check STR if((d20() + GetAbilityModifier(ABILITY_STRENGTH, oTarget)) < nDC) { // Calculate how far the creature gets pushed float fDistance = FeetToMeters(5.0f) * (1 + (nDamageDealt / 5)); // Determine if they hit a wall on the way location lManifester = GetLocation(oManifester); location lTargetOrigin = GetLocation(oTarget); vector vAngle = AngleToVector(GetRelativeAngleBetweenLocations(lManifester, lTargetOrigin)); vector vTargetOrigin = GetPosition(oTarget); vector vTarget = vTargetOrigin + (vAngle * fDistance); if(!LineOfSightVector(vTargetOrigin, vTarget)) { // Hit a wall, binary search for the wall 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 = vTargetOrigin + (vAngle * fDistance); // Determine which bound to move. if(LineOfSightVector(vTargetOrigin, vTarget)) fLowerBound = fDistance; else fUpperBound = fDistance; // Get the new middle point fDistance = (fUpperBound + fLowerBound) / 2; }while(fabs(fUpperBound - fLowerBound) > fEpsilon); } // Create the final target vector vTarget = vTargetOrigin + (vAngle * fDistance); // Determine damage and apply it int nDamage = d6(nNumberOfDice); // Assume the die size stays static effect eDamage = EffectDamage(nDamage, DAMAGE_TYPE_BLUDGEONING, DAMAGE_POWER_ENERGY); // Slamming into a solid object SPApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oTarget); // Move the target location lTargetDestination = Location(GetArea(oTarget), vTarget, GetFacing(oTarget)); AssignCommand(oTarget, ClearAllActions(TRUE)); AssignCommand(oTarget, JumpToLocation(lTargetDestination)); }// end if - The target failed the Strength check }// end if - The target is small enough }