PRC8/nwn/nwnprc/trunk/include/psi_inc_psifunc.nss
Jaysyn904 6ec137a24e Updated AMS marker feats
Updated AMS marker feats.  Removed arcane & divine marker feats.  Updated Dread Necromancer for epic progression. Updated weapon baseitem models.  Updated new weapons for crafting & npc equip.
 Updated prefix.  Updated release archive.
2024-02-11 14:01:05 -05:00

1074 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
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////
//////////////////////////////////////////////////
/* 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 = max(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(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(){}