//:://///////////////////////////////////////////// //:: 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(){}