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.
1074 lines
48 KiB
Plaintext
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(){}
|