Files
PRC8/nwn/nwnprc/trunk/include/inv_inc_invoke.nss
Jaysyn904 c1188ebb28 2026/02/19 Update
Eldritch Disciple should have Verminlord as an epic bonus feat.
Only spellcasters can use Craft (Alchemy).
Added Great Charisma and removed Great Wisdom as Force Missile Mage epic bonus feats.
Frenzied Berserker was missing Great STR 10 as an epic bonus feat.
Oozemaster had several epic bonus feats only grantable at 10th lvl.
Pyromancer's Great Charisma bonus feats were pointing at wrong or non-existent feats.
Corrected Frenzied Berserker's skill list.
Corrected Legendary Dreadnought's skill list.
Added placeholders for Combat Form feats.
Added Combat Forms masterfeats.
Fixed ASF issue with Eldritch Sculptor's 2nd blast.
Gated debug in CheckIfDeleveled().
Updated AddRacialRestrictions() for latest races.
Vow of Poverty & Forsaker work better together at level up.
Maybe fixed the mass ability buffs not hitting all targets issue.  Needs mulitplayer testing.
Updated some creature abilities to use PRC functions.
2026-02-19 21:10:22 -05:00

579 lines
23 KiB
Plaintext

//::///////////////////////////////////////////////
//:: Invocation include: Casting
//:: inv_inc_invoke
//::///////////////////////////////////////////////
/** @file
Defines structures and functions for handling
initiating a invocation
@author Fox
@date Created - 2008.1.26
@thanks to Ornedan for his work on Psionics upon which this is based.
*/
//:://////////////////////////////////////////////
//:://////////////////////////////////////////////
//////////////////////////////////////////////////
/* Constants */
//////////////////////////////////////////////////
const string PRC_INVOKING_CLASS = "PRC_CurrentInvocation_InitiatingClass";
const string PRC_INVOCATION_LEVEL = "PRC_CurrentInvocation_Level";
const string INV_DEBUG_IGNORE_CONSTRAINTS = "INV_DEBUG_IGNORE_CONSTRAINTS";
/**
* The variable in which the invocation token is stored. If no token exists,
* the variable is set to point at the invoker itself. That way OBJECT_INVALID
* means the variable is unitialised.
*/
//const string PRC_INVOCATION_TOKEN_VAR = "PRC_InvocationToken";
//const string PRC_INVOCATION_TOKEN_NAME = "PRC_INVOKETOKEN";
//const float PRC_INVOCATION_HB_DELAY = 0.5f;
//////////////////////////////////////////////////
/* Structures */
//////////////////////////////////////////////////
/**
* A structure that contains common data used during invocation.
*/
struct invocation{
/* Generic stuff */
/// The creature Truespeaking the Invocation
object oInvoker;
/// Whether the invocation is successful or not
int bCanInvoke;
/// The creature's invoker level in regards to this invocation
int nInvokerLevel;
/// The invocation's spell ID
int nInvocationId;
};
//////////////////////////////////////////////////
/* Function prototypes */
//////////////////////////////////////////////////
/**
* Determines if the invocation that is currently being attempted to be TrueSpoken
* can in fact be truespoken. Determines metainvocations used.
*
* @param oInvoker A creature attempting to truespeak a invocation at this moment.
* @param oTarget The target of the invocation, if any. For pure Area of Effect.
* invocations, this should be OBJECT_INVALID. Otherwise the main
* target of the invocation as returned by PRCGetSpellTargetObject().
*
* @return A invocation structure that contains the data about whether
* the invocation was successfully initiated and some other
* commonly used data, like the PC's invoker level for this invocation.
*/
struct invocation EvaluateInvocation(object oInvoker, object oTarget);
/**
* Causes OBJECT_SELF to use the given invocation.
*
* @param nInvocation The index of the invocation to use in spells.2da or an UTTER_*
* @param nClass The index of the class to use the invocation as in classes.2da or a CLASS_TYPE_*
* @param nLevelOverride An optional override to normal invoker level.
* @param bInstant If true invocation will be used without casting animations (eldritch sculptor)
* Default: 0, which means the parameter is ignored.
*/
void UseInvocation(int nInvocation, int nClass, int nLevelOverride = 0, int bInstant = FALSE);
/**
* A debugging function. Takes a invocation structure and
* makes a string describing the contents.
*
* @param move A set of invocation data
* @return A string describing the contents of move
*/
string DebugInvocation2Str(struct invocation invoked);
/**
* Stores a invocation 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 move The invocation structure to store
*/
void SetLocalInvocation(object oObject, string sName, struct invocation invoked);
/**
* Retrieves a previously stored invocation 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 invocation GetLocalInvocation(object oObject, string sName);
/**
* Deletes a stored invocation structure.
*
* @param oObject The object on which the structure is stored
* @param sName The name under which the structure is stored
*/
void DeleteLocalInvocation(object oObject, string sName);
//////////////////////////////////////////////////
/* Includes */
//////////////////////////////////////////////////
#include "inv_inc_invfunc" //Access in parent
#include "prc_spellf_inc"
//////////////////////////////////////////////////
/* Internal functions */
//////////////////////////////////////////////////
/** Internal function.
* Handles Spellfire absorption when a utterance is used on a friendly spellfire
* user.
*/
struct invocation _DoInvocationSpellfireFriendlyAbsorption(struct invocation invoked, object oTarget)
{
if(GetLocalInt(oTarget, "SpellfireAbsorbFriendly") &&
GetIsFriend(oTarget, invoked.oInvoker)
)
{
if(CheckSpellfire(invoked.oInvoker, oTarget, TRUE))
{
PRCShowSpellResist(invoked.oInvoker, oTarget, SPELL_RESIST_MANTLE);
invoked.bCanInvoke = FALSE;
}
}
return invoked;
}
/** Internal function.
* Sets invocation-related local variables.
*
* @param oInvoker The creature currently casting invocation
* @param nClass Invocation casting class constant
* @param nLevel Invocation level
*/
void _SetInvocationVariables(object oInvoker, int nClass, int nLevel)
{
if (DEBUG) FloatingTextStringOnCreature(GetName(oInvoker)+" is a "+IntToString(nClass)+" at "+IntToString(nLevel)+" invocation level", oInvoker);
SetLocalInt(oInvoker, PRC_INVOKING_CLASS, nClass + 1);
SetLocalInt(oInvoker, PRC_INVOCATION_LEVEL, nLevel);
}
/** Internal function.
* Deletes invocation-related local variables.
*
* @param oInvoker The creature currently initiating a invocation
*/
void _CleanInvocationVariables(object oInvoker)
{
DeleteLocalInt(oInvoker, PRC_INVOKING_CLASS);
DeleteLocalInt(oInvoker, PRC_INVOCATION_LEVEL);
}
/** Internal function.
* Determines whether a invocation token exists. If one does, returns it.
*
* @param oInvoker A creature whose invocation token to get
* @return The invocation token if it exists, OBJECT_INVALID otherwise.
*/
/*object _GetInvocationToken(object oInvoker)
{
object oInvokeToken = GetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR);
// If the token object is no longer valid, set the variable to point at invoker
if(!GetIsObjectValid(oInvokeToken))
{
oInvokeToken = oInvoker;
SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvokeToken);
}
// Check if there is no token
if(oInvokeToken == oInvoker)
oInvokeToken = OBJECT_INVALID;
return oInvokeToken;
}*/
/** Internal function.
* Destroys the given invocation token and sets the creature's invocation token variable
* to point at itself.
*
* @param oInvoker The invoker whose token to destroy
* @param oInvokeToken The token to destroy
*/
/*void _DestroyInvocationToken(object oInvoker, object oInvokeToken)
{
DestroyObject(oInvokeToken);
SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvoker);
}*/
/** Internal function.
* Destroys the previous invocation token, if any, and creates a new one.
*
* @param oInvoker A creature for whom to create a invocation token
* @return The newly created token
*/
/*object _CreateInvocationToken(object oInvoker)
{
object oInvokeToken = _GetInvocationToken(oInvoker);
object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oInvoker);
// Delete any previous tokens
if(GetIsObjectValid(oInvokeToken))
_DestroyInvocationToken(oInvoker, oInvokeToken);
// Create new token and store a reference to it
oInvokeToken = CreateItemOnObject(PRC_INVOCATION_TOKEN_NAME, oStore);
SetLocalObject(oInvoker, PRC_INVOCATION_TOKEN_VAR, oInvokeToken);
Assert(GetIsObjectValid(oInvokeToken), "GetIsObjectValid(oInvokeToken)", "ERROR: Unable to create invocation token! Store object: " + DebugObject2Str(oStore), "inv_inc_invoke", "_CreateInvocationToken()");
return oInvokeToken;
}*/
/** Internal function.
* Determines whether the given invoker is doing something that would
* interrupt initiating a invocation or affected by an effect that would do
* the same.
*
* @param oInvoker A creature on which _InvocationHB() is running
* @return TRUE if the creature can continue initiating,
* FALSE otherwise
*/
/*int _InvocationStateCheck(object oInvoker)
{
int nAction = GetCurrentAction(oInvoker);
// If the current action is not among those that could either be used to truespeak the invocation or movement, the invocation 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 initiating
effect eTest = GetFirstEffect(oInvoker);
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(oInvoker);
}
return TRUE;
}*/
/** Internal function.
* Runs while the given creature is initiating. If they move, take other actions
* that would cause them to interrupt initiating the invocation or are affected by an
* effect that would cause such interruption, deletes the invocation token.
* Stops if such condition occurs or something else destroys the token.
*
* @param oInvoker A creature initiating a invocation
* @param lInvoker The location where the invoker was when starting the invocation
* @param oInvokeToken The invocation token that controls the ongoing invocation
*/
/*void _InvocationHB(object oInvoker, location lInvoker, object oInvokeToken)
{
if(DEBUG) DoDebug("_InvocationHB() running:\n"
+ "oInvoker = " + DebugObject2Str(oInvoker) + "\n"
+ "lInvoker = " + DebugLocation2Str(lInvoker) + "\n"
+ "oInvokeToken = " + DebugObject2Str(oInvokeToken) + "\n"
+ "Distance between invocation start location and current location: " + FloatToString(GetDistanceBetweenLocations(lInvoker, GetLocation(oInvoker))) + "\n"
);
if(GetIsObjectValid(oInvokeToken))
{
// Continuance check
if(GetDistanceBetweenLocations(lInvoker, GetLocation(oInvoker)) > 2.0f || // Allow some variance in the location to account for dodging and random fidgeting
!_InvocationStateCheck(oInvoker) // Action and effect check
)
{
if(DEBUG) DoDebug("_InvocationHB(): invoker moved or lost concentration, destroying token");
_DestroyInvocationToken(oInvoker, oInvokeToken);
// Inform invoker
FloatingTextStrRefOnCreature(16832980, oInvoker, FALSE); // "You have lost concentration on the invocation you were attempting to cast!"
}
// Schedule next HB
else
DelayCommand(PRC_INVOCATION_HB_DELAY, _InvocationHB(oInvoker, lInvoker, oInvokeToken));
}
}*/
/** Internal function.
* Checks if the invoker is in range to use the invocation they are trying to use.
* If not, queues commands to make the invoker to run into range.
*
* @param oInvoker A creature initiating a invocation
* @param nInvocation SpellID of the invocation being initiated
* @param lTarget The target location or the location of the target object
*/
/*void _InvocationRangeCheck(object oInvoker, int nInvocation, location lTarget)
{
float fDistance = GetDistanceBetweenLocations(GetLocation(oInvoker), lTarget);
float fRangeLimit;
string sRange = Get2DACache("spells", "Range", nInvocation);
// Personal range invocations 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 an invocation.
* Separated from UseInvocation() 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 _AssignUseInvocationFakeCastCommands(object oInvoker, 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 invocation into the invoker's action queue.
*/
/*void _UseInvocationAux(object oInvoker, object oInvokeToken, int nSpellId,
object oTarget, location lTarget,
int nInvocation, int nClass, int nLevelOverride)
{
if(DEBUG) DoDebug("_UseInvocationAux() running:\n"
+ "oInvoker = " + DebugObject2Str(oInvoker) + "\n"
+ "oInvokeToken = " + DebugObject2Str(oInvokeToken) + "\n"
+ "nSpellId = " + IntToString(nSpellId) + "\n"
+ "oTarget = " + DebugObject2Str(oTarget) + "\n"
+ "lTarget = " + DebugLocation2Str(lTarget) + "\n"
+ "nInvocation = " + IntToString(nInvocation) + "\n"
+ "nClass = " + IntToString(nClass) + "\n"
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
);
// Make sure nothing has interrupted this invocation
if(GetIsObjectValid(oInvokeToken))
{
if(DEBUG) DoDebug("_UseInvocationAux(): Token was valid, queueing actual invocation");
// Set the class to cast as
SetLocalInt(oInvoker, PRC_INVOKING_CLASS, nClass + 1);
// Set the invocation's level
SetLocalInt(oInvoker, PRC_INVOCATION_LEVEL, StringToInt(lookup_spell_innate(nSpellId)));
if(nLevelOverride != 0)
AssignCommand(oInvoker, ActionDoCommand(SetLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride)));
if(GetIsObjectValid(oTarget))
AssignCommand(oInvoker, ActionCastSpellAtObject(nInvocation, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
else
AssignCommand(oInvoker, ActionCastSpellAtLocation(nInvocation, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
if(nLevelOverride != 0)
AssignCommand(oInvoker, ActionDoCommand(DeleteLocalInt(oInvoker, PRC_CASTERLEVEL_OVERRIDE)));
// Destroy the invocation token for this invocation
_DestroyInvocationToken(oInvoker, oInvokeToken);
}
}*/
//////////////////////////////////////////////////
/* Function definitions */
//////////////////////////////////////////////////
struct invocation EvaluateInvocation(object oInvoker, object oTarget)
{
/* Get some data */
int bIgnoreConstraints = (DEBUG) ? GetLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS) : FALSE;
// invoker-related stuff
int nInvokerLevel = GetInvokerLevel(oInvoker);
int nInvocationLevel = GetInvocationLevel(oInvoker);
int nClass = GetInvokingClass(oInvoker);
/* Initialise the invocation structure */
struct invocation invoked;
invoked.oInvoker = oInvoker;
invoked.bCanInvoke = TRUE; // Assume successfull invocation by default
invoked.nInvokerLevel = nInvokerLevel;
invoked.nInvocationId = PRCGetSpellId();
if (DEBUG) FloatingTextStringOnCreature(GetName(oInvoker)+" is a "+IntToString(nClass)+" casting invocation "+IntToString(invoked.nInvocationId)+", a "+IntToString(nInvocationLevel)+" level invocation, at "+IntToString(nInvokerLevel)+" invoker level", oInvoker);
// Skip doing anything if something has prevented a successful invocation already by this point
//if(invoked.bCanInvoke)
//{
invoked = _DoInvocationSpellfireFriendlyAbsorption(invoked, oTarget);
//}//end if
if(DEBUG) DoDebug("EvaluateInvocation(): Final result:\n" + DebugInvocation2Str(invoked));
// Initiate invocation-related variable CleanUp
//DelayCommand(0.5f, _CleanInvocationVariables(oInvoker));
return invoked;
}
void UseInvocation(int nInvocation, int nClass, int nLevelOverride = 0, int bInstant = FALSE)
{
if(nClass < 0)
nClass = CLASS_TYPE_WARLOCK;
object oInvoker = OBJECT_SELF;
// object oSkin = GetPCSkin(oInvoker);
// object oTarget = PRCGetSpellTargetObject();
// object oInvokeToken;
// location lTarget = PRCGetSpellTargetLocation();
// int nSpellID = PRCGetSpellId();
//int nInvocationDur = StringToInt(Get2DACache("spells", "ConjTime", nInvocation)) + StringToInt(Get2DACache("spells", "CastTime", nInvocation));
// This is a test case to speed up the impact of the melee attacks, as PerformAttackRound takes the full 6 second.
// int nInvocationDur = 0;
// Setup invocation-related variables
ActionDoCommand(_SetInvocationVariables(oInvoker, nClass, StringToInt(lookup_spell_innate(nInvocation))));
// Cast the actual invocation
//ActionCastSpell(nInvocation, nLevelOverride, 0, 0, METAMAGIC_NONE, CLASS_TYPE_INVALID, 0, 0, OBJECT_INVALID, bInstant);
ActionCastSpell(nInvocation, nLevelOverride, 0, 0, METAMAGIC_NONE, nClass, 0, 0, OBJECT_INVALID, bInstant);
// Initiate invocation-related variable CleanUp
ActionDoCommand(_CleanInvocationVariables(oInvoker));
// Normally swift action invocations check
/*if(Get2DACache("feat", "Constant", GetClassFeatFromPower(nInvocation, nClass)) == "SWIFT_ACTION" && // The invocation is swift action to use
TakeSwiftAction(oInvoker) // And the invoker can take a swift action now
)
{
nInvocationDur = 0;
}*/
/*if(DEBUG) DoDebug("UseInvocation(): invoker is " + DebugObject2Str(oInvoker) + "\n"
+ "nInvocation = " + IntToString(nInvocation) + "\n"
+ "nClass = " + IntToString(nClass) + "\n"
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
+ "invocation duration = " + IntToString(nInvocationDur) + "ms \n"
//+ "Token exists = " + DebugBool2String(GetIsObjectValid(oInvokeToken))
);*/
// Create the invocation token. Deletes any old tokens and cancels corresponding invocations as a side effect
//oInvokeToken = _CreateInvocationToken(oInvoker);
/// @todo Hook to the invoker's OnDamaged event for the concentration checks to avoid losing the invocation
// Nuke action queue to prevent cheating with creative invocation stacking.
// Probably not necessary anymore - Ornedan
//if(DEBUG) SendMessageToPC(oInvoker, "Clearing all actions in preparation for second stage of the invocation.");
//ClearAllActions();
// If out of range, move to range
//_InvocationRangeCheck(oInvoker, nInvocation, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget);
// Start the invocation monitor HB
//DelayCommand(IntToFloat(nInvocationDur), ActionDoCommand(_InvocationHB(oInvoker, GetLocation(oInvoker), oInvokeToken)));
// Assuming the spell isn't used as a swift action, fakecast for visuals
/*if(nInvocationDur > 0)
{
// Hack. Workaround of a bug with the fakecast actions. See function comment for details
ActionDoCommand(_AssignUseInvocationFakeCastCommands(oInvoker, oTarget, lTarget, nSpellID));
}*/
// Action queue the function that will cheatcast the actual invocation
//DelayCommand(IntToFloat(nInvocationDur), AssignCommand(oInvoker, ActionDoCommand(_UseInvocationAux(oInvoker, oInvokeToken, nSpellID, oTarget, lTarget, nInvocation, nClass, nLevelOverride))));
}
string DebugInvocation2Str(struct invocation invoked)
{
string sRet;
sRet += "oInvoker = " + DebugObject2Str(invoked.oInvoker) + "\n";
sRet += "bCanInvoke = " + DebugBool2String(invoked.bCanInvoke) + "\n";
sRet += "nInvokerLevel = " + IntToString(invoked.nInvokerLevel);
return sRet;
}
void SetLocalInvocation(object oObject, string sName, struct invocation invoked)
{
//SetLocal (oObject, sName + "_", );
SetLocalObject(oObject, sName + "_oInvoker", invoked.oInvoker);
SetLocalInt(oObject, sName + "_bCanInvoke", invoked.bCanInvoke);
SetLocalInt(oObject, sName + "_nInvokerLevel", invoked.nInvokerLevel);
SetLocalInt(oObject, sName + "_nSpellID", invoked.nInvocationId);
}
struct invocation GetLocalInvocation(object oObject, string sName)
{
struct invocation invoked;
invoked.oInvoker = GetLocalObject(oObject, sName + "_oInvoker");
invoked.bCanInvoke = GetLocalInt(oObject, sName + "_bCanInvoke");
invoked.nInvokerLevel = GetLocalInt(oObject, sName + "_nInvokerLevel");
invoked.nInvocationId = GetLocalInt(oObject, sName + "_nSpellID");
return invoked;
}
void DeleteLocalInvocation(object oObject, string sName)
{
DeleteLocalObject(oObject, sName + "_oInvoker");
DeleteLocalInt(oObject, sName + "_bCanInvoke");
DeleteLocalInt(oObject, sName + "_nInvokerLevel");
DeleteLocalInt(oObject, sName + "_nSpellID");
}
void InvocationDebugIgnoreConstraints(object oInvoker)
{
SetLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS, TRUE);
DelayCommand(0.0f, DeleteLocalInt(oInvoker, INV_DEBUG_IGNORE_CONSTRAINTS));
}
// Test main
//void main(){}