PRC8/nwn/nwnprc/trunk/include/psi_inc_psifunc.nss
Jaysyn904 e641b42f84 Exalted update
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.
2025-01-29 22:46:38 -05:00

1079 lines
48 KiB
Plaintext

//::///////////////////////////////////////////////
//:: Psionics include: Manifesting
//:: psi_inc_psifunc - was psi_inc_manifest
//::///////////////////////////////////////////////
/** @file
Defines structures and functions for handling
manifesting a power and main nexus for
Psi function access.
Acts as inclusion nexus for the general
psionics includes. In other words, don't include
them directly in your scripts, instead include this.
@author Ornedan
@date Created - 2005.11.19
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////
//void main (){}
//////////////////////////////////////////////////
/* Constants */
//////////////////////////////////////////////////
// Constants are provided via psi_inc_core
//////////////////////////////////////////////////
/* Function prototypes */
//////////////////////////////////////////////////
/**
* Determines if the power that is currently being attempted to be manifested
* can in fact be manifested. Calculates PP cost and pays it. Determines
* augmentation and metapsionics used.
*
* @param oManifester A creature attempting to manifest a power at this moment.
* @param oTarget The target of the power, if any. For pure Area of Effect.
* powers, this should be OBJECT_INVALID. Otherwise the main
* target of the power as returned by PRCGetSpellTargetObject().
* @param pap A power augmentation profile generated by a call to
* PowerAugmentationProfile(). This specifies how the power may
* be augmented.
* @param nMetaPsiFlags The metapsionics that may be used to modify this power. Any number
* of METAPSIONIC_* constants ORd together using the | operator.
* For example (METAPSIONIC_EMPOWER | METAPSIONIC_MAXIMIZE)
*
* @return A manifestation structure that contains the data about whether
* the power was successfully manifested, what augmentation and
* metapsionics were used and some other commonly used data, like
* the manifester's manifester level for this manifestation.
*/
struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags);
/**
* Causes OBJECT_SELF to use the given power.
*
* @param nPower The index of the power to use in spells.2da or a POWER_*
* @param nClass The index of the class to use the power as in classes.2da or a CLASS_TYPE_*
* @param bIsPsiLike Whether the power to be used is to be a normal use or a psi-like ability, which
* acts somewhat differently.
* Default: FALSE, meaning a normal power.
* @param nLevelOverride An optional override to normal manifester level. This is necessary when
* using the power as a psi-like ability.
* Default: 0, which means the parameter is ignored.
*/
void UsePower(int nPower, int nClass, int bIsPsiLike = FALSE, int nLevelOverride = 0);
/**
* A debugging function. Takes a manifestation structure and
* makes a string describing the contents.
*
* @param manif A set of manifestation data
* @return A string describing the contents of manif
*/
string DebugManifestation2Str(struct manifestation manif);
/**
* Stores a manifestation structure as a set of local variables. If
* a structure was already stored with the same name on the same object,
* it is overwritten.
*
* @param oObject The object on which to store the structure
* @param sName The name under which to store the structure
* @param manif The manifestation structure to store
*/
void SetLocalManifestation(object oObject, string sName, struct manifestation manif);
/**
* Retrieves a previously stored manifestation structure. If no structure is stored
* by the given name, the structure returned is empty.
*
* @param oObject The object from which to retrieve the structure
* @param sName The name under which the structure is stored
* @return The structure built from local variables stored on oObject under sName
*/
struct manifestation GetLocalManifestation(object oObject, string sName);
/**
* Sets the evaluation functions to ignore constraints on manifesting.
* Call this just prior to EvaluateManifestation() in a power script.
* That evaluation will then ignore lacking manifestation ability score,
* Power Points and Psionic Focuses.
*
* @param oManifester A creature attempting to manifest a power at this moment.
*/
void DebugIgnoreConstraints(object oManifester);
/**
* Determines if the power that is currently being attempted to be manifested
* can in fact be manifested. Calculates PP cost and pays it. Determines
* augmentation and metapsionics used.
*
* @param oManifester A creature attempting to manifest a power at this moment.
* @param oTarget The target of the power, if any. For pure Area of Effect.
* powers, this should be OBJECT_INVALID. Otherwise the main
* target of the power as returned by PRCGetSpellTargetObject().
* @param nPowerLevel The "power level" of the ability, used for determining PP cost.
* @param pap A power augmentation profile generated by a call to
* PowerAugmentationProfile(). This specifies how the power may
* be augmented.
* @param nPowerLevel The "power level" of the ability, used for determining PP cost.
*
* @return A manifestation structure that contains the data about whether
* the ability was successfully manifested, what augmentation and
* metapsionics were used and some other commonly used data, like
* the manifester's manifester level for this manifestation.
*/
struct manifestation EvaluateDiaDragChannel(object oManifester, object oTarget, struct power_augment_profile pap, int nPowerLevel);
//////////////////////////////////////////////////
/* Includes */
//////////////////////////////////////////////////
#include "psi_inc_metapsi"
#include "psi_inc_ppoints" //
#include "psi_inc_augment" //
#include "psi_inc_psicraft" // Provides Psicraft identifying
#include "psi_inc_powknown" //
//////////////////////////////////////////////////
/* Internal functions */
//////////////////////////////////////////////////
/** Internal function.
* Calculates PP cost reduction from various factors. Currently accounts for:
* - Thrallherd
* - Shadowmind
*
* @param manif The manifestation data relating to this particular manifesation
* @retrun The manifestation data, possibly with modified costs
*/
struct manifestation _GetPPCostReduced(struct manifestation manif)
{
int nSpell = PRCGetSpellId();
int nThrall = GetLevelByClass(CLASS_TYPE_THRALLHERD, manif.oManifester);
int nShadow = GetLevelByClass(CLASS_TYPE_SHADOWMIND, manif.oManifester);
if(nThrall > 0)
{
if(GetLocalInt(manif.oManifester, "ThrallCharm") && nSpell == POWER_CHARMPERSON)
{
DeleteLocalInt(manif.oManifester, "ThrallCharm");
manif.nPPCost -= nThrall;
}
if(GetLocalInt(manif.oManifester, "ThrallDom") && nSpell == POWER_DOMINATE)
{
DeleteLocalInt(manif.oManifester, "ThrallDom");
manif.nPPCost -= nThrall;
}
// Reduced cost for augmenting the Dominate power. These do not count for the DC increase
if(nThrall >= 7 && nSpell == POWER_DOMINATE && manif.nTimesAugOptUsed_1 > 0)
{
manif.nPPCost -= 2;
manif.nTimesGenericAugUsed -= 1;
}
if(nThrall >= 9 && nSpell == POWER_DOMINATE && manif.nTimesAugOptUsed_2 > 0)
{
manif.nPPCost -= 4;
manif.nTimesGenericAugUsed -= 2;
}
if(manif.nPPCost < 1) manif.nPPCost = 1;
}
if (nShadow > 0)
{
if(GetLocalInt(manif.oManifester, "ShadowDistract") && nSpell == POWER_DISTRACT)
{
DeleteLocalInt(manif.oManifester, "ShadowDistract");
manif.nPPCost -= nShadow;
}
if(GetLocalInt(manif.oManifester, "ShadowCloudMind") && nSpell == POWER_CLOUD_MIND)
{
DeleteLocalInt(manif.oManifester, "ShadowCloudMind");
manif.nPPCost -= nShadow;
}
if(GetLocalInt(manif.oManifester, "ShadowCloudMindMass") && nSpell == POWER_CLOUD_MIND_MASS)
{
DeleteLocalInt(manif.oManifester, "ShadowCloudMindMass");
manif.nPPCost -= nShadow;
}
if(manif.nPPCost < 1) manif.nPPCost = 1;
}
return manif;
}
/** Internal function.
* A wilder that is of high enough level to posses the Volatile Mind class
* feature causes extra cost to be applied to telepathy powers manifested
* on it.
*
* @param oTarget Target of the power being manifested at the moment
* @param oManifester Creature manifesting the power
* @return Either 0 if the character does not posses the Volatile
* Mind class feature or the power is not of the telepathy
* discipline. Otherwise, a number determined by the target's
* Wilder level.
*/
int _VolatileMind(object oTarget, object oManifester)
{
int nWilder = GetLevelByClass(CLASS_TYPE_WILDER, oTarget);
int nTelepathy = GetIsTelepathyPower();
int nCost = 0;
if(nTelepathy && // Only affects telepathy powers.
nWilder >= 5 && // Only wilders need apply
// Since the "As a standard action, a wilder can choose to lower this effect for 1 round."
// bit is not particularly doable in NWN, we implement it so that the class feature
// only affects powers from hostile manifesters
GetIsEnemy(oTarget, oManifester)
)
{
nCost = ((nWilder - 5) / 4) + 1;
}
return nCost;
}
/** Internal function.
* Calculates the extra cost caused by the Psionic Hole feat.
*
* @param oTarget The target of a power currently being manifested
* @return If the target has the Psionic Hole feat, the greater of
* it's Wisdom modifier and 0. Otherwise, just 0.
*/
int _PsionicHole(object oTarget)
{
int nCost = 0;
if(GetHasFeat(FEAT_PSIONIC_HOLE, oTarget))
// Psionic Hole will never decrease power cost, even if the target is lacking in wisdom bonus
nCost = PRCMax(GetAbilityModifier(ABILITY_WISDOM, oTarget), 0);
return nCost;
}
/** Internal function.
* Applies Hostile Mind damage if the target posses the feat and is being
* targeted with a telepathy power.
*
* @param oManifester A creature currently manifesting a power at oTarget
* @param oTarget The target of the power being manifested.
*/
void _HostileMind(object oManifester, object oTarget)
{
if(GetHasFeat(FEAT_HOSTILE_MIND, oTarget) && GetIsTelepathyPower())
{
// Save DC is 10 + HD/2 + ChaMod
int nDC = 10 + GetHitDice(oTarget) / 2 + GetAbilityModifier(ABILITY_CHARISMA, oTarget);
if(!PRCMySavingThrow(SAVING_THROW_WILL, oManifester, nDC, SAVING_THROW_TYPE_NONE))
{
//Apply damage and some VFX
SPApplyEffectToObject(DURATION_TYPE_INSTANT,
EffectLinkEffects(EffectDamage(d6(2)), EffectVisualEffect(VFX_FNF_SPELL_FAIL_HEA)),
oManifester
);
}
}
}
/** Internal function.
* Applies the damage caused by use of Overchannel feat.
*
* @param oManifester The creature currently manifesting a power
* @param bIsPsiLike Whether the power being manifester is a psi-like ability or not
*/
void _DoOverchannelDamage(object oManifester, int bIsPsiLike)
{
int nOverchannel = GetLocalInt(oManifester, PRC_OVERCHANNEL);
if(nOverchannel > 0 && !bIsPsiLike)
{
int nDam = d8(nOverchannel * 2 - 1);
// Check if Talented applies
if(GetPowerLevel(oManifester) <= 3)
{
if(GetLocalInt(oManifester, "TalentedActive") && UsePsionicFocus(oManifester))
return;
/* Should we be merciful and let the feat be "retroactively activated" if the damage were enough to kill?
else if(GetCurrentHitPoints(oCaster) < nDam && GetHasFeat(FEAT_TALENTED, oCaster) && UsePsionicFocus(oCaster))
return;*/
}
effect eDam = EffectDamage(nDam);
ApplyEffectToObject(DURATION_TYPE_INSTANT, eDam, oManifester);
}
}
/** Internal function.
* If the manifester is a wilder who is using Wild Surge, rolls and
* applies Psychic Enervation or Surging Euphoria based on the result.
*
* @param oManifester The creature currently manifesting a power
*/
void _SurgingEuphoriaOrPsychicEnervation(object oManifester, int nWildSurge)
{
int nWilder = GetLevelByClass(CLASS_TYPE_WILDER, oManifester);
// Only Wilders need apply (at least so far)
if(nWilder > 0 && nWildSurge)
{
// Psychic Enervation has a 5% chance to happen per point Wild Surged by
if(nWildSurge >= d20())
{
effect eMind = EffectVisualEffect(VFX_DUR_MIND_AFFECTING_NEGATIVE);
effect eDaze = EffectDazed();
effect eLink = EffectLinkEffects(eMind, eDaze);
eLink = ExtraordinaryEffect(eLink);
FloatingTextStrRefOnCreature(16823620, oManifester, FALSE); // "You have become psychically enervated and lost power points"
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oManifester, RoundsToSeconds(1));
LosePowerPoints(oManifester, nWilder);
}
// Need minimum wilder level 4 to be eligible for Surging Euphoria. And it only happens when there is no Enervation
else if(nWilder >= 4)
{
// Euphoria is 1 at levels 4 - 11, 2 at L 12 - 19, 3 at L 20 - 27, etc.
int nEuphoria = ((nWilder - 4) / 8) + 1;
effect eBonAttack = EffectAttackIncrease(nEuphoria);
effect eBonDam = EffectDamageIncrease(nEuphoria, DAMAGE_TYPE_MAGICAL);
effect eVis = EffectVisualEffect(VFX_IMP_MAGIC_PROTECTION);
effect eSave = EffectSavingThrowIncrease(SAVING_THROW_ALL, nEuphoria, SAVING_THROW_TYPE_SPELL);
effect eDur = EffectVisualEffect(VFX_DUR_CESSATE_POSITIVE);
effect eDur2 = EffectVisualEffect(VFX_DUR_MAGIC_RESISTANCE);
effect eLink = EffectLinkEffects(eSave, eDur);
eLink = EffectLinkEffects(eLink, eDur2);
eLink = EffectLinkEffects(eLink, eBonDam);
eLink = EffectLinkEffects(eLink, eBonAttack);
eLink = ExtraordinaryEffect(eLink);
FloatingTextStringOnCreature(GetStringByStrRef(16823616) + ": " + IntToString(nWildSurge), oManifester, FALSE); // "Surging Euphoria: "
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oManifester, RoundsToSeconds(nWildSurge));
}
}
}
/** Internal function.
* Applies the PP loss caused by the target having Mind Trap active.
*
* @param oManifester A creature currently manifesting a power at oTarget
* @param oTarget The target of the power being manifested
*/
void _DoMindTrapPPLoss(object oManifester, object oTarget)
{
if(oManifester != oTarget && // Does not apply to own powers
GetLocalInt(oTarget, "PRC_Power_MindTrap_Active") && // The target does have Mind Trap active
GetIsTelepathyPower() // And the power being used is a telepathy power
)
{
LosePowerPoints(oManifester, d6());
}
}
/** Internal function.
* Handles Spellfire absorption when a power is used on a friendly spellfire
* user.
*/
struct manifestation _DoSpellfireFriendlyAbsorption(struct manifestation manif, object oTarget)
{
if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") &&
GetIsFriend(oTarget, manif.oManifester)
)
{
if(CheckSpellfire(manif.oManifester, oTarget, TRUE))
{
PRCShowSpellResist(manif.oManifester, oTarget, SPELL_RESIST_MANTLE);
manif.bCanManifest = FALSE;
}
}
return manif;
}
/** Internal function.
* Deletes manifestation-related local variables.
*
* @param oManifester The creature currently manifesting a power
*/
void _CleanManifestationVariables(object oManifester)
{
DeleteLocalInt(oManifester, PRC_MANIFESTING_CLASS);
DeleteLocalInt(oManifester, PRC_POWER_LEVEL);
DeleteLocalInt(oManifester, PRC_IS_PSILIKE);
DeleteLocalInt(oManifester, PRC_AUGMENT_OVERRIDE);
}
/** Internal function.
* Determines whether a manifestation token exists. If one does, returns it.
*
* @param oManifester A creature whose manifestation token to get
* @return The manifestation token if it exists, OBJECT_INVALID otherwise.
*/
object _GetManifestationToken(object oManifester)
{
object oMfToken = GetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR);
// Special case - variable not set up yet, so this is the characters first manifestation since entering the module
/* Obsoleted by the manifestation token chest in Limbo
if(oMfToken == OBJECT_INVALID)
{
object oSkin = GetPCSkin(oManifester);
object oTest = GetFirstItemInInventory(oSkin);
// Seek for tokens in the creature's inventory and destroy them
while(GetIsObjectValid(oTest))
{
if(GetTag(oTest) == PRC_MANIFESTATION_TOKEN_NAME)
DestroyObject(oTest);
oTest = GetNextItemInInventory(oSkin);
}
}
*/
// If the token object is no longer valid, set the variable to point at manifester
if(!GetIsObjectValid(oMfToken))
{
oMfToken = oManifester;
SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oMfToken);
}
// Check if there is no token
if(oMfToken == oManifester)
oMfToken = OBJECT_INVALID;
return oMfToken;
}
/** Internal function.
* Destroys the given manifestation token and sets the creature's manifestation token variable
* to point at itself.
*
* @param oManifester The manifester whose token to destroy
* @param oMfToken The token to destroy
*/
void _DestroyManifestationToken(object oManifester, object oMfToken)
{
DestroyObject(oMfToken);
SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oManifester);
}
/** Internal function.
* Destroys the previous manifestation token, if any, and creates a new one.
*
* @param oManifester A creature for whom to create a manifestation token
* @return The newly created token
*/
object _CreateManifestationToken(object oManifester)
{
object oMfToken = _GetManifestationToken(oManifester);
object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oManifester);
// Delete any previous tokens
if(GetIsObjectValid(oMfToken))
_DestroyManifestationToken(oManifester, oMfToken);
// Create new token and store a reference to it
oMfToken = CreateItemOnObject(PRC_MANIFESTATION_TOKEN_NAME, oStore);
SetLocalObject(oManifester, PRC_MANIFESTATION_TOKEN_VAR, oMfToken);
Assert(GetIsObjectValid(oMfToken), "GetIsObjectValid(oMfToken)", "ERROR: Unable to create manifestation token! Store object: " + DebugObject2Str(oStore), "psi_inc_psifunc", "_CreateManifestationToken()");
return oMfToken;
}
/** Internal function.
* Determines whether the given manifester is doing something that would
* interrupt manifesting a power or affected by an effect that would do
* the same.
*
* @param oManifester A creature on which _ManifestationHB() is running
* @return TRUE if the creature can continue manifesting,
* FALSE otherwise
*/
int _ManifestationStateCheck(object oManifester)
{
int nAction = GetCurrentAction(oManifester);
// If the current action is not among those that could either be used to manifest the power or movement, the power fails
if(!(nAction || ACTION_CASTSPELL || nAction == ACTION_INVALID ||
nAction || ACTION_ITEMCASTSPELL || nAction == ACTION_MOVETOPOINT ||
nAction || ACTION_USEOBJECT || nAction == ACTION_WAIT
) )
return FALSE;
// Affected by something that prevents one from manifesting
effect eTest = GetFirstEffect(oManifester);
int nEType;
while(GetIsEffectValid(eTest))
{
nEType = GetEffectType(eTest);
if(nEType == EFFECT_TYPE_CUTSCENE_PARALYZE ||
nEType == EFFECT_TYPE_DAZED ||
nEType == EFFECT_TYPE_PARALYZE ||
nEType == EFFECT_TYPE_PETRIFY ||
nEType == EFFECT_TYPE_SLEEP ||
nEType == EFFECT_TYPE_STUNNED
)
return FALSE;
// Get next effect
eTest = GetNextEffect(oManifester);
}
return TRUE;
}
/** Internal function.
* Runs while the given creature is manifesting. If they move, take other actions
* that would cause them to interrupt manifesting the power or are affected by an
* effect that would cause such interruption, deletes the manifestation token.
* Stops if such condition occurs or something else destroys the token.
*
* @param oManifester A creature manifesting a power
* @param lManifester The location where the manifester was when starting the manifestation
* @param oMfToken The manifestation token that controls the ongoing manifestation
*/
void _ManifestationHB(object oManifester, location lManifester, object oMfToken)
{
if(DEBUG) DoDebug("_ManifestationHB() running:\n"
+ "oManifester = " + DebugObject2Str(oManifester) + "\n"
+ "lManifester = " + DebugLocation2Str(lManifester) + "\n"
+ "oMfToken = " + DebugObject2Str(oMfToken) + "\n"
+ "Distance between manifestation start location and current location: " + FloatToString(GetDistanceBetweenLocations(lManifester, GetLocation(oManifester))) + "\n"
);
if(GetIsObjectValid(oMfToken))
{
// Continuance check
if(GetDistanceBetweenLocations(lManifester, GetLocation(oManifester)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting
!_ManifestationStateCheck(oManifester) // Action and effect check
)
{
if(DEBUG) DoDebug("_ManifestationHB(): Manifester moved or lost concentration, destroying token");
_DestroyManifestationToken(oManifester, oMfToken);
// Inform manifester
FloatingTextStrRefOnCreature(16828435, oManifester, FALSE); // "You have lost concentration on the power you were attempting to manifest!"
}
// Schedule next HB
else
DelayCommand(PRC_MANIFESTATION_HB_DELAY, _ManifestationHB(oManifester, lManifester, oMfToken));
}
}
/** Internal function.
* Checks if the manifester is in range to use the power they are trying to use.
* If not, queues commands to make the manifester to run into range.
*
* @param oManifester A creature manifesting a power
* @param nPower SpellID of the power being manifested
* @param lTarget The target location or the location of the target object
*/
void _ManifestationRangeCheck(object oManifester, int nPower, location lTarget)
{
float fDistance = GetDistanceBetweenLocations(GetLocation(oManifester), lTarget);
float fRangeLimit;
string sRange = Get2DACache("spells", "Range", nPower);
// Personal range powers are always in range
if(sRange == "P")
return;
// Ranges according to the CCG spells.2da page
else if(sRange == "T")
fRangeLimit = 2.25f;
else if(sRange == "S")
fRangeLimit = 8.0f;
else if(sRange == "M")
fRangeLimit = 20.0f;
else if(sRange == "L")
fRangeLimit = 40.0f;
// See if we are out of range
if(fDistance > fRangeLimit)
{
// Create waypoint for the movement
object oWP = CreateObject(OBJECT_TYPE_WAYPOINT, "nw_waypoint001", lTarget);
// Move into range, with a bit of fudge-factor
//ActionMoveToObject(oWP, TRUE, fRangeLimit - 0.15f);
// Cleanup
ActionDoCommand(DestroyObject(oWP));
// Cleanup, paranoia
AssignCommand(oWP, ActionDoCommand(DestroyObject(oWP, 60.0f)));
}
}
/** Internal function.
* Assigns the fakecast command that is used to display the conjuration VFX when using a power.
* Separated from UsePower() due to a bug with ActionFakeCastSpellAtObject(), which requires
* use of ClearAllActions() to work around.
* The problem is that if the target is an item on the ground, if the actor is out of spell
* range when doing the fakecast, they will run on top of the item instead of to the edge of
* the spell range. This only happens if there was a "real action" in the actor's action queue
* immediately prior to the fakecast.
*/
void _AssignUsePowerFakeCastCommands(object oManifester, object oTarget, location lTarget, int nSpellID)
{
// Nuke actions to prevent the fakecast action from bugging
ClearAllActions();
if(GetIsObjectValid(oTarget))
ActionCastFakeSpellAtObject(nSpellID, oTarget, PROJECTILE_PATH_TYPE_DEFAULT);
else
ActionCastFakeSpellAtLocation(nSpellID, lTarget, PROJECTILE_PATH_TYPE_DEFAULT);
}
/** Internal function.
* Places the cheatcasting of the real power into the manifester's action queue.
*/
void _UsePowerAux(object oManifester, object oMfToken, int nSpellId,
object oTarget, location lTarget,
int nPower, int nClass, int bIsPsiLike, int nLevelOverride,
int bQuickened
)
{
if(DEBUG) DoDebug("_UsePowerAux() running:\n"
+ "oManifester = " + DebugObject2Str(oManifester) + "\n"
+ "oMfToken = " + DebugObject2Str(oMfToken) + "\n"
+ "nSpellId = " + IntToString(nSpellId) + "\n"
+ "oTarget = " + DebugObject2Str(oTarget) + "\n"
+ "lTarget = " + DebugLocation2Str(lTarget) + "\n"
+ "nPower = " + IntToString(nPower) + "\n"
+ "nClass = " + IntToString(nClass) + "\n"
+ "bIsPsiLike = " + DebugBool2String(bIsPsiLike) + "\n"
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
+ "bQuickened = " + DebugBool2String(bQuickened) + "\n"
);
// Make sure nothing has interrupted this manifestation
if(GetIsObjectValid(oMfToken))
{
if(DEBUG) DoDebug("_UsePowerAux(): Token was valid, queueing actual manifestation");
// Set the class to manifest as
SetLocalInt(oManifester, PRC_MANIFESTING_CLASS, nClass + 1);
// Set the power's level
SetLocalInt(oManifester, PRC_POWER_LEVEL, StringToInt(lookup_spell_innate(nSpellId)));
// Set whether the power is to run as a psi-like ability
SetLocalInt(oManifester, PRC_IS_PSILIKE, bIsPsiLike);
// Set whether the power was quickened
SetLocalInt(oManifester, PRC_POWER_IS_QUICKENED, bQuickened);
// Queue the real manifestation
//ActionCastSpell(nPower, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, TRUE, TRUE, oTarget);
if(nLevelOverride != 0)
AssignCommand(oManifester, ActionDoCommand(SetLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride)));
if(GetIsObjectValid(oTarget))
AssignCommand(oManifester, ActionCastSpellAtObject(nPower, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
else
AssignCommand(oManifester, ActionCastSpellAtLocation(nPower, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
if(nLevelOverride != 0)
AssignCommand(oManifester, ActionDoCommand(DeleteLocalInt(oManifester, PRC_CASTERLEVEL_OVERRIDE)));
// Destroy the manifestation token for this manifestation
_DestroyManifestationToken(oManifester, oMfToken);
}
}
//////////////////////////////////////////////////
/* Function definitions */
//////////////////////////////////////////////////
struct manifestation EvaluateManifestation(object oManifester, object oTarget, struct power_augment_profile pap, int nMetaPsiFlags)
{
/* Get some data */
int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE;
// Manifester-related stuff
int nManifesterLevel = GetManifesterLevel(oManifester);
int nPowerLevel = GetPowerLevel(oManifester);
int nClass = GetManifestingClass(oManifester);
int nWildSurge = GetWildSurge(oManifester);
int nManifesterPP = GetCurrentPowerPoints(oManifester);
int bIsPsiLike = GetLocalInt(oManifester, PRC_IS_PSILIKE);
// Target-specific stuff
int nVolatileMindCost = _VolatileMind(oTarget, oManifester);
int nPsionicHoleCost = _PsionicHole(oTarget);
/* Initialise the manifestation structure */
struct manifestation manif;
manif.oManifester = oManifester;
manif.bCanManifest = TRUE; // Assume successfull manifestation by default
manif.nPPCost = nPowerLevel * 2 - 1; // Initialise the cost to the base cost of the power
manif.nPsiFocUsesRemain = GetPsionicFocusesAvailable(oManifester);// Determine how many times psionic focus could be used
manif.nManifesterLevel = nManifesterLevel;
manif.nSpellID = PRCGetSpellId();
// Run an ability score check to see if the manifester can manifest the power at all
if (bIsPsiLike)
{
manif.bCanManifest = TRUE;
}
else if(GetAbilityScoreOfClass(oManifester, nClass) - 10 < nPowerLevel && !bIgnoreConstraints && !bIsPsiLike && !GetHasSpellEffect(VESTIGE_ARETE, oManifester) && !GetHasSpellEffect(VESTIGE_THETRIAD, oManifester) && !GetHasSpellEffect(VESTIGE_ABYSM, oManifester) && manif.nSpellID != POWER_ELAN_RESILIANCE)
{
FloatingTextStrRefOnCreature(16826411, oManifester, FALSE); // "You do not have a high enough ability score to manifest this power"
manif.bCanManifest = FALSE;
}
// Account for metapsionics
if(!bIsPsiLike) // Skipped for psi-like abilities
manif = EvaluateMetapsionics(manif, nMetaPsiFlags);
// This has an initial power cost of 0, all cost comes from augmentation
if (manif.nSpellID == POWER_ELAN_RESILIANCE) manif.nPPCost = 0;
// Account for augmentation. This factors in Wild Surge cost reduction
manif = EvaluateAugmentation(manif, pap);
//* APPLY COST INCREASES THAT DO NOT CAUSE ONE TO LOSE PP ON FAILURE HERE *//
if (manif.nSpellID != POWER_ELAN_RESILIANCE)
{
// Catapsi check
if(GetLocalInt(oManifester, "PRC_IsInCatapsi") && // Manifester is in Catapsi field
!PRCMySavingThrow(SAVING_THROW_WILL, oManifester, GetLocalInt(oManifester, "PRC_Catapsi_DC"), // And fails the will save VS it
SAVING_THROW_TYPE_MIND_SPELLS, GetLocalObject(oManifester, "PRC_Catapsi_Manifester")
)
)
{
manif.nPPCost += 4;
}
}
//* APPLY COST INCREASES THAT DO NOT CAUSE ONE TO LOSE PP ON FAILURE ABOVE *//
// Skip paying anything if something has prevented successfull manifestation already by this point
if(manif.bCanManifest)
{
/* The manifester level value includes the manifester level increase from
* Wild Surge, but since the calculated cost already contains the augmentation
* cost reduction provided by Wild Surge, it should not apply here.
*/
if((nManifesterLevel - nWildSurge) >= manif.nPPCost || bIsPsiLike || bIgnoreConstraints)
{
// Reduced cost of manifesting a power, but does not allow you to exceed the manifester level cap
if(!bIsPsiLike) // Skipped for psi-like abilities
manif = _GetPPCostReduced(manif);
//If the manifester does not have enough points before hostile modifiers, cancel power
if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints)
{
FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power"
manif.bCanManifest = FALSE;
}
// The manifester has enough power points that they would be able to use the power, barring extra costs
else
{
//* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL HERE *//
// Psionic Hole does not count against manifester level cap, but causes the power to fail if the manifester can't pay
manif.nPPCost += nPsionicHoleCost;
// Volatile Mind behaves the same
manif.nPPCost += nVolatileMindCost;
//* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL ABOVE *//
if(manif.nPPCost > nManifesterPP && !bIsPsiLike && !bIgnoreConstraints)
{
FloatingTextStrRefOnCreature(16826413, oManifester, FALSE); // "Your target's abilities cause you to use more Power Points than you have. The power fails"
manif.bCanManifest = FALSE;
}
// Psi-like abilities ignore PP costs and metapsi
if(!bIsPsiLike)
{
// Set the power points to their new value and inform the manifester
LosePowerPoints(oManifester, manif.nPPCost, TRUE);
// Psionic focus loss from using metapsionics. Has a side effect of telling the manifester which metapsionics were actually active
PayMetapsionicsFocuses(manif);
}
//* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL MANIFESTATION HERE *//
// Psicraft for all those who can see
IdentifyPower(oManifester, manif.nSpellID);
// Damage from overchanneling happens only if one actually spends PP
_DoOverchannelDamage(oManifester, bIsPsiLike);
// Apply Hostile Mind damage, as necessary
_HostileMind(oManifester, oTarget);
// Apply Wild Surge side-effects
_SurgingEuphoriaOrPsychicEnervation(oManifester, nWildSurge);
// Apply Mind Trap PP loss
_DoMindTrapPPLoss(oManifester, oTarget);
// Spellfire friendly absorption - This may set bCananifest to FALSE
manif = _DoSpellfireFriendlyAbsorption(manif, oTarget);
//* APPLY SIDE-EFFECTS THAT RESULT FROM SUCCESSFULL MANIFESTATION ABOVE *//
}
}
// Cost was over the manifester cap
else
{// "Your manifester level is not high enough to spend X Power Points"
FloatingTextStringOnCreature(GetStringByStrRef(16826410) + " " + IntToString(manif.nPPCost) + " " + GetStringByStrRef(16826409), oManifester, FALSE);
manif.bCanManifest = FALSE;
}
}//end if - Something hadn't prevented successfull manifestation already before paying the power costs
if(DEBUG) DoDebug("EvaluateManifestation(): Final result:\n" + DebugManifestation2Str(manif));
// Initiate manifestation-related variable cleanup
DelayCommand(0.5f, _CleanManifestationVariables(oManifester));
return manif;
}
void UsePower(int nPower, int nClass, int bIsPsiLike = FALSE, int nLevelOverride = 0)
{
object oManifester = OBJECT_SELF;
object oSkin = GetPCSkin(oManifester);
object oTarget = PRCGetSpellTargetObject();
object oMfToken;
location lTarget = PRCGetSpellTargetLocation();
int nSpellID = PRCGetSpellId();
int nManifDur = StringToInt(Get2DACache("spells", "ConjTime", nPower)) + StringToInt(Get2DACache("spells", "CastTime", nPower));
int bQuicken = FALSE;
if (GetLocalInt(oManifester, "MindStabDur")) nManifDur = 0;
// Normally swift action powers check
if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nPower, nClass)) == "SWIFT_ACTION") // The power is swift action to use
{
if (TakeSwiftAction(oManifester)) // And the Manifester can take a swift action now
nManifDur = 0;
else
return;
}
// Quicken Power check
else if(nManifDur <= 6000 && // If the power could be quickened by having manifesting time of 1 round or less
GetLocalInt(oManifester, METAPSIONIC_QUICKEN_VAR) && // And the manifester has Quicken Power active
GetIsPsionicallyFocused(oManifester) && // And might be able to pay the psionic focus for it
TakeSwiftAction(oManifester) // And the manifester can take a swift action
)
{
// Set the manifestation time to 0 to skip VFX
nManifDur = 0;
// And set the Quicken Power used marker to TRUE
bQuicken = TRUE;
}
if(DEBUG) DoDebug("UsePower(): Manifester is " + DebugObject2Str(oManifester) + "\n"
+ "nPower = " + IntToString(nPower) + "\n"
+ "nClass = " + IntToString(nClass) + "\n"
+ "bIsPsiLike = " + DebugBool2String(bIsPsiLike) + "\n"
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
+ "Manifestation duration = " + IntToString(nManifDur) + "ms \n"
+ "bQuicken = " + DebugBool2String(bQuicken) + "\n"
//+ "Token exists = " + DebugBool2String(GetIsObjectValid(oMfToken))
);
// Create the manifestation token. Deletes any old tokens and cancels corresponding manifestations as a side effect
oMfToken = _CreateManifestationToken(oManifester);
/// @todo Hook to the manifester's OnDamaged event for the concentration checks to avoid losing the power
// Nuke action queue to prevent cheating with creative power stacking.
// Probably not necessary anymore - Ornedan
if(DEBUG) SendMessageToPC(oManifester, "Clearing all actions in preparation for second stage of the power.");
ClearAllActions();
// If out of range, move to range
_ManifestationRangeCheck(oManifester, nPower, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget);
// Start the manifestation monitor HB
DelayCommand(nManifDur / 1000.0f, ActionDoCommand(_ManifestationHB(oManifester, GetLocation(oManifester), oMfToken)));
// Assuming the spell isn't used as a swift action, fakecast for visuals
if(nManifDur > 0)
{
// Hack. Workaround of a bug with the fakecast actions. See function comment for details
ActionDoCommand(_AssignUsePowerFakeCastCommands(oManifester, oTarget, lTarget, nSpellID));
}
// Action queue the function that will cheatcast the actual power
DelayCommand(nManifDur / 1000.0f, AssignCommand(oManifester, ActionDoCommand(_UsePowerAux(oManifester, oMfToken, nSpellID, oTarget, lTarget, nPower, nClass, bIsPsiLike, nLevelOverride, bQuicken))));
}
string DebugManifestation2Str(struct manifestation manif)
{
string sRet;
sRet += "oManifester = " + DebugObject2Str(manif.oManifester) + "\n";
sRet += "bCanManifest = " + DebugBool2String(manif.bCanManifest) + "\n";
sRet += "nPPCost = " + IntToString(manif.nPPCost) + "\n";
sRet += "nPsiFocUsesRemain = " + IntToString(manif.nPsiFocUsesRemain) + "\n";
sRet += "nManifesterLevel = " + IntToString(manif.nManifesterLevel) + "\n";
sRet += "nTimesAugOptUsed_1 = " + IntToString(manif.nTimesAugOptUsed_1) + "\n";
sRet += "nTimesAugOptUsed_2 = " + IntToString(manif.nTimesAugOptUsed_2) + "\n";
sRet += "nTimesAugOptUsed_3 = " + IntToString(manif.nTimesAugOptUsed_3) + "\n";
sRet += "nTimesAugOptUsed_4 = " + IntToString(manif.nTimesAugOptUsed_4) + "\n";
sRet += "nTimesAugOptUsed_5 = " + IntToString(manif.nTimesAugOptUsed_5) + "\n";
sRet += "nTimesGenericAugUsed = " + IntToString(manif.nTimesGenericAugUsed) + "\n";
sRet += "bChain = " + DebugBool2String(manif.bChain) + "\n";
sRet += "bEmpower = " + DebugBool2String(manif.bEmpower) + "\n";
sRet += "bExtend = " + DebugBool2String(manif.bExtend) + "\n";
sRet += "bMaximize = " + DebugBool2String(manif.bMaximize) + "\n";
sRet += "bSplit = " + DebugBool2String(manif.bSplit) + "\n";
sRet += "bTwin = " + DebugBool2String(manif.bTwin) + "\n";
sRet += "bWiden = " + DebugBool2String(manif.bWiden) + "\n";
sRet += "bQuicken = " + DebugBool2String(manif.bQuicken);// + "\n";
return sRet;
}
void SetLocalManifestation(object oObject, string sName, struct manifestation manif)
{
//SetLocal (oObject, sName + "_", );
SetLocalObject(oObject, sName + "_oManifester", manif.oManifester);
SetLocalInt(oObject, sName + "_bCanManifest", manif.bCanManifest);
SetLocalInt(oObject, sName + "_nPPCost", manif.nPPCost);
SetLocalInt(oObject, sName + "_nPsiFocUsesRemain", manif.nPsiFocUsesRemain);
SetLocalInt(oObject, sName + "_nManifesterLevel", manif.nManifesterLevel);
SetLocalInt(oObject, sName + "_nSpellID", manif.nSpellID);
SetLocalInt(oObject, sName + "_nTimesAugOptUsed_1", manif.nTimesAugOptUsed_1);
SetLocalInt(oObject, sName + "_nTimesAugOptUsed_2", manif.nTimesAugOptUsed_2);
SetLocalInt(oObject, sName + "_nTimesAugOptUsed_3", manif.nTimesAugOptUsed_3);
SetLocalInt(oObject, sName + "_nTimesAugOptUsed_4", manif.nTimesAugOptUsed_4);
SetLocalInt(oObject, sName + "_nTimesAugOptUsed_5", manif.nTimesAugOptUsed_5);
SetLocalInt(oObject, sName + "_nTimesGenericAugUsed", manif.nTimesGenericAugUsed);
SetLocalInt(oObject, sName + "_bChain", manif.bChain);
SetLocalInt(oObject, sName + "_bEmpower", manif.bEmpower);
SetLocalInt(oObject, sName + "_bExtend", manif.bExtend);
SetLocalInt(oObject, sName + "_bMaximize", manif.bMaximize);
SetLocalInt(oObject, sName + "_bSplit", manif.bSplit);
SetLocalInt(oObject, sName + "_bTwin", manif.bTwin);
SetLocalInt(oObject, sName + "_bWiden", manif.bWiden);
SetLocalInt(oObject, sName + "_bQuicken", manif.bQuicken);
}
struct manifestation GetLocalManifestation(object oObject, string sName)
{
struct manifestation manif;
manif.oManifester = GetLocalObject(oObject, sName + "_oManifester");
manif.bCanManifest = GetLocalInt(oObject, sName + "_bCanManifest");
manif.nPPCost = GetLocalInt(oObject, sName + "_nPPCost");
manif.nPsiFocUsesRemain = GetLocalInt(oObject, sName + "_nPsiFocUsesRemain");
manif.nManifesterLevel = GetLocalInt(oObject, sName + "_nManifesterLevel");
manif.nSpellID = GetLocalInt(oObject, sName + "_nSpellID");
manif.nTimesAugOptUsed_1 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_1");
manif.nTimesAugOptUsed_2 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_2");
manif.nTimesAugOptUsed_3 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_3");
manif.nTimesAugOptUsed_4 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_4");
manif.nTimesAugOptUsed_5 = GetLocalInt(oObject, sName + "_nTimesAugOptUsed_5");
manif.nTimesGenericAugUsed = GetLocalInt(oObject, sName + "_nTimesGenericAugUsed");
manif.bChain = GetLocalInt(oObject, sName + "_bChain");
manif.bEmpower = GetLocalInt(oObject, sName + "_bEmpower");
manif.bExtend = GetLocalInt(oObject, sName + "_bExtend");
manif.bMaximize = GetLocalInt(oObject, sName + "_bMaximize");
manif.bSplit = GetLocalInt(oObject, sName + "_bSplit");
manif.bTwin = GetLocalInt(oObject, sName + "_bTwin");
manif.bWiden = GetLocalInt(oObject, sName + "_bWiden");
manif.bQuicken = GetLocalInt(oObject, sName + "_bQuicken");
return manif;
}
void DebugIgnoreConstraints(object oManifester)
{
SetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS, TRUE);
DelayCommand(0.0f, DeleteLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS));
}
//Below is a modification of Evaluate Manifestation for the Diamond Dragon abilities
struct manifestation EvaluateDiaDragChannel(object oManifester, object oTarget, struct power_augment_profile pap, int nPowerLevel)
{
/* Get some data */
int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oManifester, PRC_DEBUG_IGNORE_CONSTRAINTS) : FALSE;
// Manifester-related stuff
int nClass = GetPrimaryPsionicClass(oManifester);
int nWildSurge = GetWildSurge(oManifester);
//When Manifester level is retrieved, remove Wildsurge and Overchannel bonuses as they don't apply
int nManifesterLevel = GetManifesterLevel(oManifester, nClass);
int nManifesterPP = GetCurrentPowerPoints(oManifester);
/* Initialise the manifestation structure */
struct manifestation manif;
manif.oManifester = oManifester;
manif.bCanManifest = TRUE; // Assume successfull manifestation by default
manif.nPPCost = nPowerLevel * 2 - 1; // Initialise the cost to the base cost of the power
manif.nPsiFocUsesRemain = 0; // Not needed
manif.nManifesterLevel = nManifesterLevel;
manif.nSpellID = PRCGetSpellId();
// Run an ability score check to see if the manifester can manifest the power at all
if(GetAbilityScoreOfClass(oManifester, nClass) - 10 < nPowerLevel && (!bIgnoreConstraints))
{
FloatingTextStrRefOnCreature(16826411, oManifester, FALSE); // "You do not have a high enough ability score to manifest this power"
manif.bCanManifest = FALSE;
}
// Account for augmentation. This factors in Wild Surge cost reduction
manif = EvaluateAugmentation(manif, pap);
// Skip paying anything if something has prevented successfull manifestation already by this point
if(manif.bCanManifest)
{
//Remove the Wild Surge cost reduction
manif.nPPCost += nWildSurge;
/* The manifester level value includes the manifester level increase from
* Wild Surge, but since the calculated cost already contains the augmentation
* cost reduction provided by Wild Surge, it should not apply here.
*/
if((nManifesterLevel) >= manif.nPPCost || bIgnoreConstraints)
{
//If the manifester does not have enough points before hostile modifiers, cancel power
if(manif.nPPCost > nManifesterPP && !bIgnoreConstraints)
{
FloatingTextStrRefOnCreature(16826412, oManifester, FALSE); // "You do not have enough Power Points to manifest this power"
manif.bCanManifest = FALSE;
}
// The manifester has enough power points that they would be able to use the power, barring extra costs
else
{
//* ADD ALL COST INCREASING FACTORS THAT WILL CAUSE PP LOSS EVEN IF THEY MAKE THE POWER FAIL ABOVE *//
if(manif.nPPCost > nManifesterPP && !bIgnoreConstraints)
{
FloatingTextStrRefOnCreature(16826413, oManifester, FALSE); // "Your target's abilities cause you to use more Power Points than you have. The power fails"
manif.bCanManifest = FALSE;
}
// Set the power points to their new value and inform the manifester
LosePowerPoints(oManifester, manif.nPPCost, TRUE);
}
}
// Cost was over the manifester cap
else
{// "Your manifester level is not high enough to spend X Power Points"
FloatingTextStringOnCreature(GetStringByStrRef(16826410) + " " + IntToString(manif.nPPCost) + " " + GetStringByStrRef(16826409), oManifester, FALSE);
manif.bCanManifest = FALSE;
}
}//end if - Something hadn't prevented successfull manifestation already before paying the power costs
if(DEBUG) DoDebug("EvaluateManifestation(): Final result:\n" + DebugManifestation2Str(manif));
// Initiate manifestation-related variable cleanup
DelayCommand(0.5f, _CleanManifestationVariables(oManifester));
return manif;
}
// Test main
//void main(){}