Updated Release Archive. Fixed Mage-killer prereqs. Removed old LETO & ConvoCC related files. Added organized spell scroll store. Fixed Gloura spellbook. Various TLK fixes. Reorganized Repo. Removed invalid user folders. Added DocGen back in.
748 lines
30 KiB
Plaintext
748 lines
30 KiB
Plaintext
//::///////////////////////////////////////////////
|
|
//:: Tome of Battle include: Initiating
|
|
//:: tob_inc_move
|
|
//::///////////////////////////////////////////////
|
|
/** @file
|
|
Defines structures and functions for handling
|
|
initiating a maneuver
|
|
|
|
@author Stratovarius
|
|
@date Created - 2007.3.20
|
|
@thanks to Ornedan for his work on Psionics upon which this is based.
|
|
*/
|
|
//:://////////////////////////////////////////////
|
|
//:://////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Constants */
|
|
//////////////////////////////////////////////////
|
|
|
|
const string TOB_DEBUG_IGNORE_CONSTRAINTS = "TOB_DEBUG_IGNORE_CONSTRAINTS";
|
|
|
|
/**
|
|
* The variable in which the maneuver token is stored. If no token exists,
|
|
* the variable is set to point at the initiator itself. That way OBJECT_INVALID
|
|
* means the variable is unitialised.
|
|
*/
|
|
const string PRC_MANEVEUR_TOKEN_VAR = "PRC_ManeuverToken";
|
|
const string PRC_MANEVEUR_TOKEN_NAME = "PRC_MOVETOKEN";
|
|
const float PRC_MANEVEUR_HB_DELAY = 0.5f;
|
|
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Structures */
|
|
//////////////////////////////////////////////////
|
|
|
|
/**
|
|
* A structure that contains common data used during maneuver.
|
|
*/
|
|
struct maneuver{
|
|
/* Generic stuff */
|
|
/// The creature Truespeaking the Maneuver
|
|
object oInitiator;
|
|
/// Whether the maneuver is successful or not
|
|
int bCanManeuver;
|
|
/// The creature's initiator level in regards to this maneuver
|
|
int nInitiatorLevel;
|
|
/// The maneuver's spell ID
|
|
int nMoveId;
|
|
};
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function prototypes */
|
|
//////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Determines if the maneuver that is currently being attempted to be TrueSpoken
|
|
* can in fact be truespoken. Determines metamaneuvers used.
|
|
*
|
|
* @param oInitiator A creature attempting to truespeak a maneuver at this moment.
|
|
* @param oTarget The target of the maneuver, if any. For pure Area of Effect.
|
|
* maneuvers, this should be OBJECT_INVALID. Otherwise the main
|
|
* target of the maneuver as returned by PRCGetSpellTargetObject().
|
|
*
|
|
* @return A maneuver structure that contains the data about whether
|
|
* the maneuver was successfully initiated and some other
|
|
* commonly used data, like the PC's initiator level for this maneuver.
|
|
*/
|
|
struct maneuver EvaluateManeuver(object oInitiator, object oTarget = OBJECT_INVALID, int bTOBAbility = FALSE);
|
|
|
|
/**
|
|
* Causes OBJECT_SELF to use the given maneuver.
|
|
*
|
|
* @param nManeuver The index of the maneuver to use in spells.2da or an UTTER_*
|
|
* @param nClass The index of the class to use the maneuver as in classes.2da or a CLASS_TYPE_*
|
|
* @param nLevelOverride An optional override to normal initiator level.
|
|
* Default: 0, which means the parameter is ignored.
|
|
*/
|
|
void UseManeuver(int nManeuver, int nClass, int nLevelOverride = 0);
|
|
|
|
/**
|
|
* A debugging function. Takes a maneuver structure and
|
|
* makes a string describing the contents.
|
|
*
|
|
* @param move A set of maneuver data
|
|
* @return A string describing the contents of move
|
|
*/
|
|
string DebugManeuver2Str(struct maneuver move);
|
|
|
|
/**
|
|
* Stores a maneuver 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 maneuver structure to store
|
|
*/
|
|
void SetLocalManeuver(object oObject, string sName, struct maneuver move);
|
|
|
|
/**
|
|
* Retrieves a previously stored maneuver 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 maneuver GetLocalManeuver(object oObject, string sName);
|
|
|
|
/**
|
|
* Deletes a stored maneuver structure.
|
|
*
|
|
* @param oObject The object on which the structure is stored
|
|
* @param sName The name under which the structure is stored
|
|
*/
|
|
void DeleteLocalManeuver(object oObject, string sName);
|
|
|
|
/**
|
|
* Checks whether the maneuver is a class ability
|
|
*
|
|
* @param nMoveId The SpellID
|
|
*
|
|
* @return TRUE if it is a class ability, FALSE otherwise
|
|
*/
|
|
int GetIsTOBAbility(int nMoveId);
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Includes */
|
|
//////////////////////////////////////////////////
|
|
|
|
#include "tob_inc_martlore"
|
|
#include "tob_inc_recovery"
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Internal functions */
|
|
//////////////////////////////////////////////////
|
|
|
|
/** Internal function.
|
|
* Deletes maneuver-related local variables.
|
|
*
|
|
* @param oInitiator The creature currently initiating a maneuver
|
|
*/
|
|
void _CleanManeuverVariables(object oInitiator)
|
|
{
|
|
DeleteLocalInt(oInitiator, PRC_INITIATING_CLASS);
|
|
DeleteLocalInt(oInitiator, PRC_MANEUVER_LEVEL);
|
|
DeleteLocalInt(oInitiator, "PCIsInitiating");
|
|
}
|
|
|
|
/** Internal function.
|
|
* Determines whether a maneuver token exists. If one does, returns it.
|
|
*
|
|
* @param oInitiator A creature whose maneuver token to get
|
|
* @return The maneuver token if it exists, OBJECT_INVALID otherwise.
|
|
*/
|
|
object _GetManeuverToken(object oInitiator)
|
|
{
|
|
object oMoveToken = GetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR);
|
|
|
|
// If the token object is no longer valid, set the variable to point at initiator
|
|
if(!GetIsObjectValid(oMoveToken))
|
|
{
|
|
oMoveToken = oInitiator;
|
|
SetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR, oMoveToken);
|
|
}
|
|
|
|
|
|
// Check if there is no token
|
|
if(oMoveToken == oInitiator)
|
|
oMoveToken = OBJECT_INVALID;
|
|
|
|
return oMoveToken;
|
|
}
|
|
|
|
/** Internal function.
|
|
* Destroys the given maneuver token and sets the creature's maneuver token variable
|
|
* to point at itself.
|
|
*
|
|
* @param oInitiator The initiator whose token to destroy
|
|
* @param oMoveToken The token to destroy
|
|
*/
|
|
void _DestroyManeuverToken(object oInitiator, object oMoveToken)
|
|
{
|
|
DestroyObject(oMoveToken);
|
|
if(DEBUG) DoDebug("_DestroyManeuverToken(): Destroying Token");
|
|
SetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR, oInitiator);
|
|
}
|
|
|
|
/** Internal function.
|
|
* Destroys the previous maneuver token, if any, and creates a new one.
|
|
*
|
|
* @param oInitiator A creature for whom to create a maneuver token
|
|
* @return The newly created token
|
|
*/
|
|
object _CreateManeuverToken(object oInitiator)
|
|
{
|
|
object oMoveToken = _GetManeuverToken(oInitiator);
|
|
object oStore = GetObjectByTag("PRC_MANIFTOKEN_STORE"); //GetPCSkin(oInitiator);
|
|
|
|
// Delete any previous tokens
|
|
if(GetIsObjectValid(oMoveToken))
|
|
_DestroyManeuverToken(oInitiator, oMoveToken);
|
|
|
|
// Create new token and store a reference to it
|
|
oMoveToken = CreateItemOnObject(PRC_MANEVEUR_TOKEN_NAME, oStore);
|
|
SetLocalObject(oInitiator, PRC_MANEVEUR_TOKEN_VAR, oMoveToken);
|
|
|
|
Assert(GetIsObjectValid(oMoveToken), "GetIsObjectValid(oMoveToken)", "ERROR: Unable to create maneuver token! Store object: " + DebugObject2Str(oStore), "true_inc_Utter", "_CreateManeuverToken()");
|
|
|
|
return oMoveToken;
|
|
}
|
|
|
|
/** Internal function.
|
|
* Determines whether the given initiator is doing something that would
|
|
* interrupt initiating a maneuver or affected by an effect that would do
|
|
* the same.
|
|
*
|
|
* @param oInitiator A creature on which _ManeuverHB() is running
|
|
* @return TRUE if the creature can continue initiating,
|
|
* FALSE otherwise
|
|
*/
|
|
int _ManeuverStateCheck(object oInitiator)
|
|
{
|
|
if(GetIsDead(oInitiator)) return FALSE;
|
|
int nAction = GetCurrentAction(oInitiator);
|
|
// If the current action is not among those that could either be used to truespeak the maneuver or movement, the maneuver 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(oInitiator);
|
|
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(oInitiator);
|
|
}
|
|
|
|
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 maneuver or are affected by an
|
|
* effect that would cause such interruption, deletes the maneuver token.
|
|
* Stops if such condition occurs or something else destroys the token.
|
|
*
|
|
* @param oInitiator A creature initiating a maneuver
|
|
* @param lInitiator The location where the initiator was when starting the maneuver
|
|
* @param oMoveToken The maneuver token that controls the ongoing maneuver
|
|
*/
|
|
void _ManeuverHB(object oInitiator, location lInitiator, object oMoveToken)
|
|
{
|
|
float fDistance;
|
|
if(DEBUG) DoDebug("_ManeuverHB() running:\n"
|
|
+ "oInitiator = " + DebugObject2Str(oInitiator) + "\n"
|
|
+ "lInitiator = " + DebugLocation2Str(lInitiator) + "\n"
|
|
+ "oMoveToken = " + DebugObject2Str(oMoveToken) + "\n"
|
|
+ "Distance between maneuver start location and current location: " + FloatToString(GetDistanceBetweenLocations(lInitiator, GetLocation(oInitiator))) + "\n"
|
|
);
|
|
if(GetIsObjectValid(oMoveToken))
|
|
{
|
|
// Continuance check
|
|
fDistance = GetDistanceBetweenLocations(lInitiator, GetLocation(oInitiator));
|
|
if(fDistance > 2.0f || fDistance < 0.0 || // Allow some variance in the location to account for dodging and random fidgeting
|
|
!_ManeuverStateCheck(oInitiator) // Action and effect check
|
|
)
|
|
{
|
|
if(DEBUG) DoDebug("_ManeuverHB(): initiator moved or lost concentration, destroying token");
|
|
_DestroyManeuverToken(oInitiator, oMoveToken);
|
|
|
|
// Inform initiator
|
|
FloatingTextStringOnCreature("You have lost concentration on the maneuver you were attempting to initiate!", oInitiator, FALSE);
|
|
}
|
|
// Schedule next HB
|
|
else
|
|
DelayCommand(PRC_MANEVEUR_HB_DELAY, _ManeuverHB(oInitiator, lInitiator, oMoveToken));
|
|
}
|
|
}
|
|
|
|
/** Internal function.
|
|
* Checks if the initiator is in range to use the maneuver they are trying to use.
|
|
* If not, queues commands to make the initiator to run into range.
|
|
*
|
|
* @param oInitiator A creature initiating a maneuver
|
|
* @param nManeuver SpellID of the maneuver being initiated
|
|
* @param lTarget The target location or the location of the target object
|
|
*/
|
|
void _ManeuverRangeCheck(object oInitiator, int nManeuver, location lTarget)
|
|
{
|
|
float fDistance = GetDistanceBetweenLocations(GetLocation(oInitiator), lTarget);
|
|
float fRangeLimit;
|
|
string sRange = Get2DACache("spells", "Range", nManeuver);
|
|
|
|
// Personal range maneuvers 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 maneuver.
|
|
* Separated from UseManeuver() 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 _AssignUseManeuverFakeCastCommands(object oInitiator, 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 maneuver into the initiator's action queue.
|
|
*/
|
|
void _UseManeuverAux(object oInitiator, object oMoveToken, int nSpellId,
|
|
object oTarget, location lTarget,
|
|
int nManeuver, int nClass, int nLevelOverride)
|
|
{
|
|
if(DEBUG) DoDebug("_UseManeuverAux() running:\n"
|
|
+ "oInitiator = " + DebugObject2Str(oInitiator) + "\n"
|
|
+ "oMoveToken = " + DebugObject2Str(oMoveToken) + "\n"
|
|
+ "nSpellId = " + IntToString(nSpellId) + "\n"
|
|
+ "oTarget = " + DebugObject2Str(oTarget) + "\n"
|
|
+ "lTarget = " + DebugLocation2Str(lTarget) + "\n"
|
|
+ "nManeuver = " + IntToString(nManeuver) + "\n"
|
|
+ "nClass = " + IntToString(nClass) + "\n"
|
|
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
|
|
);
|
|
|
|
// Make sure nothing has interrupted this maneuver
|
|
if(GetIsObjectValid(oMoveToken))
|
|
{
|
|
if(DEBUG) DoDebug("_UseManeuverAux(): Token was valid, queueing actual maneuver");
|
|
// Set the class to maneuver as
|
|
SetLocalInt(oInitiator, PRC_INITIATING_CLASS, nClass + 1);
|
|
|
|
// Set the maneuver's level
|
|
SetLocalInt(oInitiator, PRC_MANEUVER_LEVEL, StringToInt(lookup_spell_innate(nSpellId)));
|
|
|
|
if(nLevelOverride != 0)
|
|
AssignCommand(oInitiator, ActionDoCommand(SetLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE, nLevelOverride)));
|
|
if(GetIsObjectValid(oTarget))
|
|
AssignCommand(oInitiator, ActionCastSpellAtObject(nManeuver, oTarget, METAMAGIC_NONE, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
|
|
else
|
|
AssignCommand(oInitiator, ActionCastSpellAtLocation(nManeuver, lTarget, METAMAGIC_NONE, TRUE, PROJECTILE_PATH_TYPE_DEFAULT, TRUE));
|
|
if(nLevelOverride != 0)
|
|
AssignCommand(oInitiator, ActionDoCommand(DeleteLocalInt(oInitiator, PRC_CASTERLEVEL_OVERRIDE)));
|
|
|
|
// Begins the Crusader Granting Maneuver process
|
|
if (nClass == CLASS_TYPE_CRUSADER)
|
|
{
|
|
BeginCrusaderGranting(oInitiator);
|
|
//if(DEBUG) DoDebug("_UseManeuverAux(): BeginCrusaderGranting");
|
|
}
|
|
|
|
SetLocalInt(oInitiator, "PCIsInitiating", TRUE);
|
|
// Destroy the maneuver token for this maneuver
|
|
_DestroyManeuverToken(oInitiator, oMoveToken);
|
|
}
|
|
}
|
|
|
|
int _GetIsManeuverWeaponAppropriate(object oInitiator)
|
|
{
|
|
object oItem = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oInitiator);
|
|
// If the initiator is empty handed, unarmed strikes are good
|
|
if (!GetIsObjectValid(oItem)) return TRUE;
|
|
// If melee weapon, all good.
|
|
if (IPGetIsMeleeWeapon(oItem)) return TRUE;
|
|
// Add other legal items in here, like Bloodstorm Blade throwing
|
|
|
|
// If one of the other's hasn't tripped, fail here
|
|
return FALSE;
|
|
}
|
|
|
|
void _StanceSpecificChecks(object oInitiator, int nMoveId)
|
|
{
|
|
int nStanceToKeep = -1;
|
|
if (GetLevelByClass(CLASS_TYPE_WARBLADE, oInitiator) >= 20)
|
|
{
|
|
nStanceToKeep = GetHasActiveStance(oInitiator);
|
|
}
|
|
if (GetLevelByClass(CLASS_TYPE_DEEPSTONE_SENTINEL, oInitiator) >= 3 &&
|
|
GetHasSpellEffect(MOVE_MOUNTAIN_FORTRESS, oInitiator) &&
|
|
GetDisciplineByManeuver(nMoveId) == DISCIPLINE_STONE_DRAGON)
|
|
{
|
|
nStanceToKeep = GetHasActiveStance(oInitiator);
|
|
}
|
|
// Master of Nine can keep two stances active for 2 rounds per class level.
|
|
if (GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator) >= 3 && GetLocalInt(oInitiator, "MoNDualStance"))
|
|
{
|
|
nStanceToKeep = GetHasActiveStance(oInitiator);
|
|
float fDelay = 12.0 * GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator);
|
|
// Clean up the stance when the timer runs out
|
|
DelayCommand(fDelay, ClearStances(oInitiator, -1));
|
|
DeleteLocalInt(oInitiator, "MoNDualStance");
|
|
}
|
|
|
|
//if(DEBUG) DoDebug("tob_inc_move: ClearStances");
|
|
if(nStanceToKeep == nMoveId)
|
|
nStanceToKeep = -1;
|
|
// Can only have one stance active, except for a level 20+ Warblade
|
|
ClearStances(oInitiator, nStanceToKeep);
|
|
|
|
// Mark current stance as atcive
|
|
MarkStanceActive(oInitiator, nMoveId);
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
/* Function definitions */
|
|
//////////////////////////////////////////////////
|
|
|
|
struct maneuver EvaluateManeuver(object oInitiator, object oTarget = OBJECT_INVALID, int bTOBAbility = FALSE)
|
|
{
|
|
/* Get some data */
|
|
// initiator-related stuff
|
|
int nInitiatorLevel = GetInitiatorLevel(oInitiator);
|
|
int nManeuverLevel = GetManeuverLevel(oInitiator);
|
|
int nClass = GetInitiatingClass(oInitiator);
|
|
|
|
/* Initialise the maneuver structure */
|
|
struct maneuver move;
|
|
move.oInitiator = oInitiator;
|
|
move.bCanManeuver = TRUE; // Assume successfull maneuver by default
|
|
move.nInitiatorLevel = nInitiatorLevel;
|
|
move.nMoveId = PRCGetSpellId();
|
|
|
|
//if(DEBUG) DoDebug("move.bCanManeuver: " + IntToString(move.bCanManeuver));
|
|
// Skip doing anything if something has prevented a successful maneuver already by this point
|
|
if(/*move.bCanManeuver && */bTOBAbility == FALSE) // set up identification later
|
|
{
|
|
// If you're this far in, you always succeed, there are very few checks.
|
|
// Deletes any active stances, and allows a Warblade 20 to have his two stances active.
|
|
/* GC - TMI is being caused from GetIsStance being run on the swordsage. Let's not run it twice.*/
|
|
if (GetIsStance(move.nMoveId)) _StanceSpecificChecks(oInitiator, move.nMoveId);
|
|
else ExpendManeuver(move.oInitiator, nClass, move.nMoveId);
|
|
// Allows the Master of Nine to Counter Stance.
|
|
if(GetManeuverType(move.nMoveId) == MANEUVER_TYPE_COUNTER
|
|
&& GetLevelByClass(CLASS_TYPE_MASTER_OF_NINE, oInitiator) >= 4)
|
|
{
|
|
SetLocalInt(oInitiator, "MoNCounterStance", TRUE);
|
|
DelayCommand(6.0, DeleteLocalInt(oInitiator, "MoNCounterStance"));
|
|
}
|
|
//if(DEBUG) DoDebug("tob_inc_move: _StanceSpecificChecks");
|
|
// Expend the Maneuver until recovered
|
|
//if (!GetIsStance(move.nMoveId)) ExpendManeuver(move.oInitiator, nClass, move.nMoveId);
|
|
//if(DEBUG) DoDebug("tob_inc_move: ExpendManeuver");
|
|
// Do Martial Lore data
|
|
IdentifyManeuver(move.oInitiator, move.nMoveId);
|
|
//if(DEBUG) DoDebug("tob_inc_move: IdentifyManeuver");
|
|
IdentifyDiscipline(move.oInitiator);
|
|
//if(DEBUG) DoDebug("tob_inc_move: IdentifyDiscipline");
|
|
|
|
}//end if
|
|
|
|
if(DEBUG) DoDebug("EvaluateManeuver(): Final result:\n" + DebugManeuver2Str(move));
|
|
|
|
// Initiate maneuver-related variable CleanUp
|
|
DelayCommand(0.5f, _CleanManeuverVariables(oInitiator));
|
|
|
|
return move;
|
|
}
|
|
|
|
void UseManeuver(int nManeuver, int nClass, int nLevelOverride = 0)
|
|
{
|
|
object oInitiator = OBJECT_SELF;
|
|
object oSkin = GetPCSkin(oInitiator);
|
|
object oTarget = PRCGetSpellTargetObject();
|
|
object oMoveToken;
|
|
location lTarget = PRCGetSpellTargetLocation();
|
|
int nSpellID = PRCGetSpellId();
|
|
|
|
// Divine Surge Greater hardcoding to fix radial issue
|
|
// Yes it's deliberate that it's both
|
|
if (nSpellID == 15840 || nSpellID == 16051 || nSpellID == 16262)
|
|
{
|
|
nManeuver = MOVE_DS_GREATER_DIVINE_SURGE;
|
|
nSpellID = MOVE_DS_GREATER_DIVINE_SURGE;
|
|
}
|
|
|
|
// Hardcoding to make Reth Dekala maneuvers work without forcing a massive redo of everything
|
|
if (nSpellID == 19331) nManeuver = MOVE_DS_MARTIAL_SPIRIT;
|
|
else if (nSpellID == 19332) nManeuver = MOVE_DS_THICKET_BLADES;
|
|
else if (nSpellID == 19333) nManeuver = MOVE_DS_DAUNTING_STRIKE;
|
|
else if (nSpellID == 19334) nManeuver = MOVE_TC_DEATH_FROM_ABOVE;
|
|
else if (nSpellID == 19335) nManeuver = MOVE_IH_DISARMING_STRIKE;
|
|
else if (nSpellID == 19336) nManeuver = MOVE_DS_ENTANGLING_BLADE;
|
|
else if (nSpellID == 19337) nManeuver = MOVE_IH_WALL_BLADES;
|
|
|
|
if (DEBUG) DoDebug(GetName(oInitiator)+" initiating "+IntToString(nManeuver)+" on "+GetName(oTarget));
|
|
|
|
float fDistance = MetersToFeet(GetDistanceBetweenLocations(GetLocation(oInitiator), GetLocation(oTarget)));
|
|
float fDelay = FeetToMeters(fDistance)/10;
|
|
|
|
// Moved the checks here, so they don't use an action.
|
|
|
|
// Don't hit yourself, dumbass
|
|
if (Get2DACache("feat", "TARGETSELF", GetClassFeatFromPower(nManeuver, nClass)) != "1" && oInitiator == oTarget && nSpellID != 19331 && nSpellID != 19332 && nSpellID != 19337)
|
|
{
|
|
//if(DEBUG) DoDebug("tob_inc_move: _GetTargetSelfStrike");
|
|
FloatingTextStringOnCreature("You cannot target yourself with this maneuver.", oInitiator, FALSE);
|
|
return;
|
|
}
|
|
// If the weapon is not appropriate, fail.
|
|
if (!_GetIsManeuverWeaponAppropriate(oInitiator))
|
|
{
|
|
//if(DEBUG) DoDebug("tob_inc_move: _GetIsManeuverWeaponAppropriate");
|
|
FloatingTextStringOnCreature("You do not have an appropriate weapon to initiate this maneuver.", oInitiator, FALSE);
|
|
return;
|
|
}
|
|
// If the maneuver is not readied, fail.
|
|
// Stances don't need to be readied
|
|
if (!GetIsManeuverReadied(oInitiator, nClass, nManeuver) && !GetIsStance(nManeuver) && !GetIsTOBAbility(nManeuver))
|
|
{
|
|
//if(DEBUG) DoDebug("tob_inc_move: GetIsManeuverReadied");
|
|
FloatingTextStringOnCreature(GetManeuverName(nManeuver) + " is not readied.", oInitiator, FALSE);
|
|
return;
|
|
}
|
|
// If the maneuver is expended, fail.
|
|
if (GetIsManeuverExpended(oInitiator, nClass, nManeuver) && !GetIsTOBAbility(nManeuver))
|
|
{
|
|
//if(DEBUG) DoDebug("tob_inc_move: GetIsManeuverExpended");
|
|
FloatingTextStringOnCreature(GetManeuverName(nManeuver) + " is already expended.", oInitiator, FALSE);
|
|
return;
|
|
}
|
|
// If the PC is in a Warblade recovery round, fail
|
|
if (GetIsWarbladeRecoveryRound(oInitiator))
|
|
{
|
|
//if(DEBUG) DoDebug("tob_inc_move: GetIsWarbladeRecoveryRound");
|
|
FloatingTextStringOnCreature(GetName(oInitiator) + " is recovering Warblade maneuvers.", oInitiator, FALSE);
|
|
return;
|
|
}
|
|
// Is the maneuver granted, and is the class a Crusader
|
|
if (nClass == CLASS_TYPE_CRUSADER && !GetIsManeuverGranted(oInitiator, nManeuver) && !GetIsStance(nManeuver) && !GetIsTOBAbility(nManeuver))
|
|
{
|
|
//if(DEBUG) DoDebug("tob_inc_move: GetIsManeuverGranted");
|
|
FloatingTextStringOnCreature(GetManeuverName(nManeuver) + " is not a granted maneuver.", oInitiator, FALSE);
|
|
return;
|
|
}
|
|
|
|
// These maneuvers don't have a delay otherwise, because the distance is always 0
|
|
if (Get2DACache("feat", "TARGETSELF", GetClassFeatFromPower(nManeuver, nClass)) == "1" && GetManeuverType(nManeuver) == MANEUVER_TYPE_STRIKE)
|
|
fDelay = 3.0;
|
|
|
|
// Dual Boost check
|
|
if(GetManeuverType(nManeuver) == MANEUVER_TYPE_BOOST // If the maneuver is a boost
|
|
&& GetLocalInt(oInitiator, "SSDualBoost")) // And the initiator can Dual boost.
|
|
{
|
|
// Set the maneuver time to 0 to skip VFX
|
|
DeleteLocalInt(oInitiator, "SSDualBoost");
|
|
fDelay = 0.0;
|
|
}
|
|
// Stance of Alacrity check
|
|
// Only works on Counters, not Boosts
|
|
else if(GetManeuverType(nManeuver) == MANEUVER_TYPE_COUNTER // If the maneuver is counter
|
|
&& GetHasSpellEffect(MOVE_DM_STANCE_ALACRITY, oInitiator)) // And the initiator has the stance
|
|
{
|
|
// Set the maneuver time to 0 to skip VFX
|
|
fDelay = 0.0;
|
|
}
|
|
else if((Get2DACache("feat", "Constant", GetClassFeatFromPower(nManeuver, nClass)) == "SWIFT_ACTION" // Normally swift action maneuvers check
|
|
|| GetManeuverType(nManeuver) == MANEUVER_TYPE_BOOST
|
|
|| GetManeuverType(nManeuver) == MANEUVER_TYPE_COUNTER
|
|
|| GetManeuverType(nManeuver) == MANEUVER_TYPE_STANCE) // The maneuver is swift action to use
|
|
&& GetLocalInt(oInitiator, "RKVDivineImpetus") // And the initiator can take a swift action now
|
|
)
|
|
{
|
|
fDelay = 0.0;
|
|
DeleteLocalInt(oInitiator, "RKVDivineImpetus");
|
|
}
|
|
else if(GetManeuverType(nManeuver) == MANEUVER_TYPE_STANCE && // The maneuver is a stance
|
|
GetLocalInt(oInitiator, "MoNCounterStance") // Has used a counter already this round
|
|
)
|
|
{
|
|
fDelay = 0.0;
|
|
DeleteLocalInt(oInitiator, "MoNCounterStance");
|
|
}
|
|
else if((Get2DACache("feat", "Constant", GetClassFeatFromPower(nManeuver, nClass)) == "SWIFT_ACTION" // Normally swift action maneuvers check
|
|
|| GetManeuverType(nManeuver) == MANEUVER_TYPE_BOOST
|
|
|| GetManeuverType(nManeuver) == MANEUVER_TYPE_COUNTER
|
|
|| GetManeuverType(nManeuver) == MANEUVER_TYPE_STANCE)) // The maneuver is swift action to use
|
|
{
|
|
if (TakeSwiftAction(oInitiator)) // And the initiator can take a swift action now
|
|
fDelay = 0.0;
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (nManeuver == MOVE_DW_BURNING_BRAND)
|
|
fDelay = FeetToMeters(fDistance*2)/10; // Because of how it has to be implemented.
|
|
|
|
if(DEBUG) DoDebug("UseManeuver(): initiator is " + DebugObject2Str(oInitiator) + "\n"
|
|
+ "nManeuver = " + IntToString(nManeuver) + "\n"
|
|
+ "nClass = " + IntToString(nClass) + "\n"
|
|
+ "nLevelOverride = " + IntToString(nLevelOverride) + "\n"
|
|
+ "maneuver duration = " + FloatToString(fDelay) + "ms \n"
|
|
//+ "Token exists = " + DebugBool2String(GetIsObjectValid(oMoveToken))
|
|
);
|
|
|
|
// Create the maneuver token. Deletes any old tokens and cancels corresponding maneuvers as a side effect
|
|
oMoveToken = _CreateManeuverToken(oInitiator);
|
|
|
|
/// @todo Hook to the initiator's OnDamaged event for the concentration checks to avoid losing the maneuver
|
|
|
|
// Nuke action queue to prevent cheating with creative maneuver stacking.
|
|
// Probably not necessary anymore - Ornedan
|
|
if(DEBUG) SendMessageToPC(oInitiator, "Clearing all actions in preparation for second stage of the maneuver.");
|
|
ClearAllActions();
|
|
|
|
// If out of range, move to range
|
|
_ManeuverRangeCheck(oInitiator, nManeuver, GetIsObjectValid(oTarget) ? GetLocation(oTarget) : lTarget);
|
|
|
|
// Start the maneuver monitor HB
|
|
DelayCommand(fDelay, ActionDoCommand(_ManeuverHB(oInitiator, GetLocation(oInitiator), oMoveToken)));
|
|
if(DEBUG) DoDebug("Starting _ManeuverHB");
|
|
|
|
// Assuming the spell isn't used as a swift action, fakecast for visuals
|
|
if(fDelay > 0.0)
|
|
{
|
|
// Hack. Workaround of a bug with the fakecast actions. See function comment for details
|
|
ActionDoCommand(_AssignUseManeuverFakeCastCommands(oInitiator, oTarget, lTarget, nSpellID));
|
|
if(DEBUG) DoDebug("Starting _AssignUseManeuverFakeCastCommands");
|
|
}
|
|
|
|
if(DEBUG) DoDebug("Starting _UseManeuverAux");
|
|
// Action queue the function that will cheatcast the actual maneuver
|
|
DelayCommand(fDelay, AssignCommand(oInitiator, ActionDoCommand(_UseManeuverAux(oInitiator, oMoveToken, nSpellID, oTarget, lTarget, nManeuver, nClass, nLevelOverride))));
|
|
}
|
|
|
|
string DebugManeuver2Str(struct maneuver move)
|
|
{
|
|
string sRet;
|
|
|
|
sRet += "oInitiator = " + DebugObject2Str(move.oInitiator) + "\n";
|
|
sRet += "bCanManeuver = " + DebugBool2String(move.bCanManeuver) + "\n";
|
|
sRet += "nInitiatorLevel = " + IntToString(move.nInitiatorLevel);
|
|
|
|
return sRet;
|
|
}
|
|
|
|
void SetLocalManeuver(object oObject, string sName, struct maneuver move)
|
|
{
|
|
//SetLocal (oObject, sName + "_", );
|
|
SetLocalObject(oObject, sName + "_oInitiator", move.oInitiator);
|
|
|
|
SetLocalInt(oObject, sName + "_bCanManeuver", move.bCanManeuver);
|
|
SetLocalInt(oObject, sName + "_nInitiatorLevel", move.nInitiatorLevel);
|
|
SetLocalInt(oObject, sName + "_nSpellID", move.nMoveId);
|
|
}
|
|
|
|
struct maneuver GetLocalManeuver(object oObject, string sName)
|
|
{
|
|
struct maneuver move;
|
|
move.oInitiator = GetLocalObject(oObject, sName + "_oInitiator");
|
|
|
|
move.bCanManeuver = GetLocalInt(oObject, sName + "_bCanManeuver");
|
|
move.nInitiatorLevel = GetLocalInt(oObject, sName + "_nInitiatorLevel");
|
|
move.nMoveId = GetLocalInt(oObject, sName + "_nSpellID");
|
|
|
|
return move;
|
|
}
|
|
|
|
void DeleteLocalManeuver(object oObject, string sName)
|
|
{
|
|
DeleteLocalObject(oObject, sName + "_oInitiator");
|
|
|
|
DeleteLocalInt(oObject, sName + "_bCanManeuver");
|
|
DeleteLocalInt(oObject, sName + "_nInitiatorLevel");
|
|
DeleteLocalInt(oObject, sName + "_nSpellID");
|
|
}
|
|
|
|
void ManeuverDebugIgnoreConstraints(object oInitiator)
|
|
{
|
|
SetLocalInt(oInitiator, TOB_DEBUG_IGNORE_CONSTRAINTS, TRUE);
|
|
DelayCommand(0.0f, DeleteLocalInt(oInitiator, TOB_DEBUG_IGNORE_CONSTRAINTS));
|
|
}
|
|
|
|
int GetIsTOBAbility(int nMoveId)
|
|
{
|
|
// Shadow Sun Ninja and Eternal Blade class abilities
|
|
if(nMoveId == 19320 ||
|
|
nMoveId == 19321 ||
|
|
nMoveId == 19322 ||
|
|
nMoveId == 19323 ||
|
|
nMoveId == 19324 ||
|
|
nMoveId == 19325 ||
|
|
nMoveId == 19326 ||
|
|
nMoveId == 19316 ||
|
|
nMoveId == 19315 ||
|
|
nMoveId == 19314 ||
|
|
nMoveId == 19317 ||
|
|
nMoveId == 19318 ||
|
|
nMoveId == 19319)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Test main
|
|
//void main(){}
|