// Axe Spells Library script
//:://////////////////////////
// _axe_spells_inc
//:://////////////////////////
// Contains functions and constants shared by Axe Spells v1.1 scripts.
//:://////////////////////////
#include “X0_I0_SPELLS”
#include “x2_inc_spellhook”
#include “nw_i0_generic”
#include “nw_i0_plot”
// This constant is the row number from the spells.2da file.
const int SPELL_BONE_CLONE = 860;
const int SPELL_SINGLE_BOUND = 861;
const int SPELL_BLINK = 862;
const int SPELL_BATTLE_ECHO = 863;
const int SPELL_BLOOD_LEECH = 864;
const int SPELL_FLASH = 865;
const int SPELL_MAGIC_BANDAGE = 866;
const int SPELL_HERBAL_REMEDY = 867;
const int SPELL_DEATH_BIND = 868;
const int SPELL_CAMARADERIE = 869;
const int SPELL_SOLIDARITY = 870;
const int SPELL_CONGREGATE = 871;
// Function to apply max damage to a creature.
void ApplyMaxDamage( object oCreature)
{ if( !GetIsObjectValid( oCreature) || GetIsDead( oCreature)) return;
effect eDamage = EffectDamage( GetMaxHitPoints( oCreature), DAMAGE_TYPE_MAGICAL, DAMAGE_POWER_ENERGY);
ApplyEffectToObject( DURATION_TYPE_INSTANT, eDamage, oCreature);
}
// Function to create a pile of bones with the TAG “BodyBag”.at a specified location
void MakeBones( location lSpawnAt)
{ object oBonePile = CreateObject( OBJECT_TYPE_PLACEABLE, “plc_bones”, lSpawnAt, FALSE, “BodyBag”);
if( GetIsObjectValid( oBonePile))
{ // Make the bone pile go away after a turn.
AssignCommand( oBonePile, DelayCommand( TurnsToSeconds( 1), DestroyObject( oBonePile)));
}
}
// Function to make a creature take a few steps forward then turn around and attack his host.
void WalkForwardAndTurnAround( object oCreature)
{ if( !GetIsObjectValid( oCreature)) return;
// Get the creature’s host.
object oHost = GetLocalObject( oCreature, “HostCreature”);
// Find a location a few steps ahead.
float fAFewStepsDistance = 1.0f;
float fFacing = GetFacing( oCreature);
vector vAhead = VectorNormalize( AngleToVector( fFacing)) *fAFewStepsDistance;
vector vNew = GetPosition( oCreature) +vAhead;
float fNewFacing = fFacing +180.0f;
while( fNewFacing >= 360.0f) fNewFacing -= 360.0f;
while( fNewFacing < 0.0f) fNewFacing += 360.0f;
location lAhead = Location( GetArea( oCreature), vNew, fFacing);
// Move to the location then turn around and attack.
AssignCommand( oCreature, ActionWait( 1.5f));
AssignCommand( oCreature, ActionMoveToLocation( lAhead, FALSE));
AssignCommand( oCreature, ActionWait( 1.5f));
AssignCommand( oCreature, ActionDoCommand( SetCommandable( TRUE, oCreature)));
AssignCommand( oCreature, ActionDoCommand( SetFacing( fNewFacing)));
AssignCommand( oCreature, ActionDoCommand( SetIsEnemy( oHost)));
// Make sure the actions are not interrupted
AssignCommand( oCreature, SetCommandable( FALSE, oCreature));
}
// Function to determine if a location is valid.
int GetIsLocationValid( location lTest)
{ return GetIsObjectValid( GetAreaFromLocation( lTest)); }
// Function to trim spaces from thef front of a string.
string TrimLeading( string sString )
{ if( sString == “” ) return “”;
int i = 0;
while( GetSubString( sString, i, 1 ) == " ") ++i;
if( i > 0 ) sString = GetSubString( sString, i, GetStringLength( sString ) -i );
return sString;
}
// Function to trim spaces from the end of a string.
string TrimTrailing( string sString )
{ if( sString == “” ) return “”;
int i = GetStringLength( sString ) -1;
while( GetSubString( sString, i, 1 ) == " ") --i;
if( i < (GetStringLength( sString ) -1) ) sString = GetSubString( sString, 0, i +1 );
return sString;
}
// Function to trim multiple spaces from a string. It converts all sequences of
// more than one space in the string to a single space.
string TrimMultipleSpaces( string sString )
{ if( sString == “” ) return “”;
string sTrimmed = “”;
string sLast = “”;
while( sString != “” )
{ string sLetter = GetSubString( sString, 0, 1 );
sString = GetStringRight( sString, GetStringLength( sString ) -1 );
if( (sLetter != " ") || (sLast != " ")) { sLast = sLetter; sTrimmed += sLetter; }
}
return sTrimmed;
}
// Function to trim spaces from a string. It removes all leading and trailing
// spaces and converts all sequences of more than one space in the interior of
// the string to a single space, then returns the result.
string TrimString( string sString )
{ if( sString == “” ) return “”;
sString = TrimLeading( sString ); if( sString == “” ) return “”;
sString = TrimTrailing( sString ); if( sString == “” ) return “”;
return TrimMultipleSpaces( sString );
}
// Function to determine if two creatures are members of the same party.
int GetIsAPartyMember( object oCreature, object oPartyMember )
{ if( !GetIsObjectValid( oCreature ) || (GetObjectType( oCreature ) != OBJECT_TYPE_CREATURE) ||
!GetIsObjectValid( oPartyMember ) || (GetObjectType( oPartyMember ) != OBJECT_TYPE_CREATURE) ) return FALSE;
object oLeader1 = GetFactionLeader( oCreature );
object oLeader2 = GetFactionLeader( oPartyMember );
return (GetIsObjectValid( oLeader1 ) && (oLeader1 == oLeader2));
}
// Function that, given a name and a creature, will return the first member of
// that creature’s party whose name matches the one supplied. The name check
// is case insensitive. bPCOnly determines if only PCs in the party are checked.
object GetPartyMemberByName( string sName, object oCreature, int bPCOnly = TRUE )
{ sName = GetStringLowerCase( TrimString( sName ));
if( (sName == “”) || !GetIsObjectValid( oCreature ) || (GetObjectType( oCreature ) != OBJECT_TYPE_CREATURE) ) return OBJECT_INVALID;
object oParty = GetFirstFactionMember( oCreature, bPCOnly );
while( GetIsObjectValid( oParty ))
{ string sPartyName = GetStringLowerCase( TrimString( GetName( oParty )));
if( sPartyName == sName ) return oParty;
oParty = GetNextFactionMember( oCreature, bPCOnly );
}
return OBJECT_INVALID;
}
// Function to determine if an object is a valid enemy humanoid creature and spell target.
int GetIsAHumanoidEnemy( object oCreature, object oCaster = OBJECT_SELF)
{ if( !GetIsObjectValid( oCreature) ||
(GetObjectType( oCreature) != OBJECT_TYPE_CREATURE) ||
!spellsIsTarget( oCreature, SPELL_TARGET_STANDARDHOSTILE, oCaster) ||
(oCreature == oCaster))
return FALSE;
switch( GetRacialType( oCreature))
{ case RACIAL_TYPE_DWARF: case RACIAL_TYPE_ELF:
case RACIAL_TYPE_GNOME: case RACIAL_TYPE_HALFELF:
case RACIAL_TYPE_HALFLING: case RACIAL_TYPE_HALFORC:
case RACIAL_TYPE_HUMAN: case RACIAL_TYPE_HUMANOID_GOBLINOID:
case RACIAL_TYPE_HUMANOID_ORC: case RACIAL_TYPE_HUMANOID_REPTILIAN:
return TRUE;
}
return FALSE;
}
// Function to Clone the Bones of an enemy.
void CloneBones( object oEnemy, float fDuration, object oCaster = OBJECT_SELF)
{ // Freeze the enemy for 1 round and give him the cutscene ghost effect.
AssignCommand( oEnemy, ClearAllActions( TRUE));
AssignCommand( oEnemy, ActionWait( 4.0f));
AssignCommand( oEnemy, ActionDoCommand( SetCommandable( TRUE, oEnemy)));
ApplyEffectToObject( DURATION_TYPE_TEMPORARY, EffectCutsceneGhost(), oEnemy, 4.0f);
// Spawn in the clone and mark the enemy as it’s host creature and the caster as it’s creator.
object oClone = CreateObject( OBJECT_TYPE_CREATURE, “bone_clone”, GetLocation( oEnemy));
SetLocalObject( oClone, “HostCreature”, oEnemy);
SetLocalObject( oClone, “MyCreator”, oCaster);
// I couldn’t get this part look good so I commented it out.
// Make a small visual effect to signify the bones getting cloned.
// effect eCloneEffect = EffectVisualEffect( VFX_IMP_WILL_SAVING_THROW_USE);
// effect eCloneEffect = EffectVisualEffect( VFX_IMP_RAISE_DEAD);
// ApplyEffectToObject( DURATION_TYPE_INSTANT, eCloneEffect, oEnemy);
// effect eCloneEffect = EffectVisualEffect( VFX_DUR_GHOSTLY_PULSE);
// ApplyEffectToObject( DURATION_TYPE_TEMPORARY, eCloneEffect, oEnemy, 6.0f);
// Apply a visual effect to the clone that will display when the spell wears off.
effect eSpellExpires = EffectVisualEffect( VFX_DUR_CESSATE_NEGATIVE);
ApplyEffectToObject( DURATION_TYPE_TEMPORARY, eSpellExpires, oClone, fDuration);
// Make the clone kill itself when the spell wears off.
AssignCommand( oClone, DelayCommand( fDuration, ApplyMaxDamage( oClone)));
// Lock the enemy’s action queue.
AssignCommand( oEnemy, SetCommandable( FALSE, oEnemy));
}
// Function to determine the saving throw type from caster’s class for
// the Bone Clone spell.
int GetSavingThrowTypeByClass( int iClassType)
{ switch( iClassType)
{ case CLASS_TYPE_CLERIC: return SAVING_THROW_TYPE_DIVINE;
case CLASS_TYPE_DRUID: return SAVING_THROW_TYPE_DIVINE;
case CLASS_TYPE_DRAGONDISCIPLE: return SAVING_THROW_TYPE_FEAR;
case CLASS_TYPE_HARPER: return SAVING_THROW_TYPE_MIND_SPELLS;
case CLASS_TYPE_SORCERER: return SAVING_THROW_TYPE_SPELL;
case CLASS_TYPE_WIZARD: return SAVING_THROW_TYPE_SPELL;
case CLASS_TYPE_PALEMASTER: return SAVING_THROW_TYPE_DEATH;
case CLASS_TYPE_UNDEAD: return SAVING_THROW_TYPE_DEATH;
// Paladins and Rangers probably shouldn't get this spell.
case CLASS_TYPE_PALADIN: return SAVING_THROW_TYPE_DIVINE;
case CLASS_TYPE_RANGER: return SAVING_THROW_TYPE_SPELL;
}
return SAVING_THROW_TYPE_SPELL;
}
//::///////////////////////////////////////////////////////////
// float GetAngleToTargetLocation( location lSource, location lTarget)
// Computes the facing angle from a source location to a target
// location as a floating point number in the 0.0 to 359.999999
// range.
//::///////////////////////////////////////////////////////////
// Parameters: location lSource - the source location.
// location lTarget - the target location.
//
// Returns: the facing angle required to face the target location
// if standing on the source location. Returns -1.0 if
// either location is invalid.
//::///////////////////////////////////////////////////////////
float GetAngleToTargetLocation( location lSource, location lTarget);
float GetAngleToTargetLocation( location lSource, location lTarget)
{ if( !GetIsObjectValid( GetAreaFromLocation( lSource)) || !GetIsObjectValid( GetAreaFromLocation( lTarget))) return -1.0;
return VectorToAngle( GetPositionFromLocation( lTarget) -GetPositionFromLocation( lSource));
}
//::///////////////////////////////////////////////////////////
// void TransportPartyToCreature( object oPartyMember, object oCreature )
// Causes all members of oPartyMember’s party to be transported to the
// location where oCreature is at. If oCreature’s location is not valid
// the function waits 1 second then tries again. It will repeat this
// ten times before giving up.
//::///////////////////////////////////////////////////////////
// Parameters: object oPartyMember - a member of the party to be transported
// object oCreature - the target creature where member will be sent.
//
// Returns: None.
//::///////////////////////////////////////////////////////////
void TransportPartyToCreature( object oPartyMember, object oCreature );
void TransportPartyToCreature( object oPartyMember, object oCreature )
{ if( !GetIsObjectValid( oPartyMember ) || (GetObjectType( oPartyMember ) != OBJECT_TYPE_CREATURE) ||
!GetIsObjectValid( oCreature ) || (GetObjectType( oCreature ) != OBJECT_TYPE_CREATURE) ) return;
// Wait for target creature to arrive if he is transitioning. Only try 10 times.
if( !GetIsLocationValid( GetLocation( oCreature )))
{ int iCount = GetLocalInt( oPartyMember, “TransportPartyToCreatureCounter” ) +1;
if( iCount <= 10 )
{ SetLocalInt( oPartyMember, “TransportPartyToCreatureCounter”, iCount );
AssignCommand( oPartyMember, DelayCommand( 1.0, TransportPartyToCreature( oPartyMember, oCreature )));
}
// otherwise something is wrong with the party leader. He hasn't had a
// valid location in over ten seconds. So give up on the transport.
else DeleteLocalInt( oPartyMember, "TransportPartyToCreatureCounter" );
return;
}
DeleteLocalInt( oPartyMember, “TransportPartyToCreatureCounter” );
// Leader’s location is valid, send the party to him.
object oParty = GetFirstFactionMember( oPartyMember, FALSE );
while( GetIsObjectValid( oParty ))
{ // Transport the member if it isn’t the leader, to the leader, along with
// some visuals at the old and new spot.
if( oParty != oCreature )
{ ApplyEffectAtLocation( DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_FNF_SMOKE_PUFF ), GetLocation( oParty ));
AssignCommand( oParty, JumpToObject( oCreature ));
AssignCommand( oParty, ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_IMP_POLYMORPH ), oParty ));
}
oParty = GetNextFactionMember( oPartyMember, FALSE );
}
}
//::///////////////////////////////////////////////////////////
// void ClearDeathBindLocation( object oRespawner = OBJECT_SELF )
// Ensures the specified PC will no longer be “Death Bound” to any location.
//::///////////////////////////////////////////////////////////
// Parameters: object oRespawner - the PC to clear the bound location of.
//
// Returns: If the player is not bound to any location, or if oRespawner is not
// a valid PC nothing happens. Otherwise the player’s Death Bind spot
// is deleted so he will start spawning at the default death temple
// from now on.
//::///////////////////////////////////////////////////////////
void ClearDeathBindLocation( object oRespawner = OBJECT_SELF );
void ClearDeathBindLocation( object oRespawner = OBJECT_SELF )
{ if( !GetIsPC( oRespawner )) return;
DeleteCampaignVariable( “DeathBindDB”, “BoundArea”, oRespawner );
DeleteCampaignVariable( “DeathBindDB”, “BoundX”, oRespawner );
DeleteCampaignVariable( “DeathBindDB”, “BoundY”, oRespawner );
DeleteCampaignVariable( “DeathBindDB”, “BoundZ”, oRespawner );
DeleteCampaignVariable( “DeathBindDB”, “BoundF”, oRespawner );
}
//::///////////////////////////////////////////////////////////
// void SetDeathBindLocation( location lBindSpot, object oRespawner = OBJECT_SELF )
// Stores the specified location as the given player’s Death Bind spot so
// he will respawn there from now on.
//::///////////////////////////////////////////////////////////
// Parameters: location lBindSpot - the location to Death Bind the player to.
// object oRespawner - the PC to be bound to the new location.
//
// Returns: None. If oRespawner is not a valid PC, or if the location given
// is invalid nothing happens. If the player had a Death Bind spot
// already, it will be replaced and forever forgotton by the new one.
//::///////////////////////////////////////////////////////////
void SetDeathBindLocation( location lBindSpot, object oRespawner = OBJECT_SELF );
void SetDeathBindLocation( location lBindSpot, object oRespawner = OBJECT_SELF )
{ if( !GetIsPC( oRespawner ) || !GetIsLocationValid( lBindSpot )) return;
string sArea = GetTag( GetAreaFromLocation( lBindSpot ));
vector vPos = GetPositionFromLocation( lBindSpot );
float fFace = GetFacingFromLocation( lBindSpot );
SetCampaignString( “DeathBindDB”, “BoundArea”, sArea, oRespawner );
SetCampaignFloat( “DeathBindDB”, “BoundX”, vPos.x, oRespawner );
SetCampaignFloat( “DeathBindDB”, “BoundY”, vPos.y, oRespawner );
SetCampaignFloat( “DeathBindDB”, “BoundZ”, vPos.z, oRespawner );
SetCampaignFloat( “DeathBindDB”, “BoundF”, fFace, oRespawner );
}
//::///////////////////////////////////////////////////////////
// void StoreDeathBindLocation( object oRespawner = OBJECT_SELF )
// Stores the specified player’s current location as his Death Bind spot so
// he will respawn there from now on.
//::///////////////////////////////////////////////////////////
// Parameters: object oRespawner - the PC to be bound to his current location.
//
// Returns: None. If oRespawner is not a valid PC, or if his current location
// is invalid (e.g. transitioning between areas) nothing happens. If
// the player had a Death Bind spot already, it will be replaced and
// forever forgotton by the new location.
//::///////////////////////////////////////////////////////////
void StoreDeathBindLocation( object oRespawner = OBJECT_SELF );
void StoreDeathBindLocation( object oRespawner = OBJECT_SELF )
{ SetDeathBindLocation( GetLocation( oRespawner ), oRespawner );
}
//::///////////////////////////////////////////////////////////
// location RetrieveDeathBindLocation( object oRespawner = OBJECT_SELF )
// Retrieves the location a player was bound to using the Death Bind spell.
//::///////////////////////////////////////////////////////////
// Parameters: object oRespawner - the PC to return the bound location of.
//
// Returns: Returns an invalid location if the player is not bound to any
// location, or if oRespawner is not a valid PC. Otherwise returns
// the player’s most recently set Death Bind spot.
//::///////////////////////////////////////////////////////////
location RetrieveDeathBindLocation( object oRespawner = OBJECT_SELF );
location RetrieveDeathBindLocation( object oRespawner = OBJECT_SELF )
{ if( !GetIsPC( oRespawner )) return GetLocation( OBJECT_INVALID );
object oArea = GetObjectByTag( GetCampaignString( “DeathBindDB”, “BoundArea”, oRespawner ));
if( !GetIsObjectValid( oArea )) return GetLocation( OBJECT_INVALID );
vector vPos = Vector( GetCampaignFloat( “DeathBindDB”, “BoundX”, oRespawner ),
GetCampaignFloat( “DeathBindDB”, “BoundY”, oRespawner ),
GetCampaignFloat( “DeathBindDB”, “BoundZ”, oRespawner ));
float fFace = GetCampaignFloat( “DeathBindDB”, “BoundF”, oRespawner );
return Location ( oArea, vPos, fFace );
}
//::///////////////////////////////////////////////////////////
// int AS_DeathBind( object oRespawner )
// Sends the specified PC to his bound location set by the Death Bind spell
// if he has one. Otherwise does nothing.
// Note: It is assumed the PC has already been ressurrected, healed, bad
// effects removed, GP/XP penalties applied, etc. prior to this
// function being called. It will rez oRespawner (no healing) if
// he is found to be dead, but does nothing else along those lines,
// all it does is get the PC to his Death Bind spot if he has one.
//::///////////////////////////////////////////////////////////
// Parameters: object oRespawner - the PC to be jumped to his Death Bind spot.
//
// Returns: TRUE if the player’s bound location was found and he was sent there.
// FALSE if the player specified has no bound location or is not a
// valid PC.
//::///////////////////////////////////////////////////////////
int AS_DeathBind( object oRespawner );
int AS_DeathBind( object oRespawner )
{ // Get the bound location. If one isn’t found return FALSE.
location lBindSpot = RetrieveDeathBindLocation( oRespawner );
if( !GetIsLocationValid( lBindSpot )) return FALSE;
// If he’s dead, rez him, but no heal and no removing of bad effects, GP, or XP.
if( GetIsDead( oRespawner )) ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectResurrection(), oRespawner );
// Jump the player to his bind spot, and return TRUE to indicate we did.
AssignCommand( oRespawner, JumpToLocation( lBindSpot ));
return TRUE;
}
//::///////////////////////////////////////////////////////////
// int AS_OnPlayerChat()
//
//::///////////////////////////////////////////////////////////
// Parameters: None.
//
// Returns: TRUE if the player chat was determined to relate to an AxeSpells
// spell and was handled. It indicates to the OnPlayerChat script
// that the script should end without doing anything as it has already
// been handled.
// FALSE if the player chat was determined to not relate to an AxeSpells
// spell and nothing was therefore done. It indicates to the OnPlayerChat
// script that it should continue to try to process the chat for other
// chat systems that are in place.
//::///////////////////////////////////////////////////////////
int AS_OnPlayerChat();
int AS_OnPlayerChat()
{ object oPC = GetPCChatSpeaker();
if( !GetIsPC( oPC )) return FALSE;
// Get the chat spoken, trim it of spaces, and convert it to all lower case.
string sSpoken = GetStringLowerCase( TrimString( GetPCChatMessage() ));
// Check for camaraderie spell on the speaker
if( GetLocalInt( oPC, “AS_Camaraderie_WAIT_FOR_NAME” ))
{ // Expecting a player name from this player for the Camaraderie spell.
// See if what he said is the name of a member of his party.
object oPartyMember = GetPartyMemberByName( sSpoken, oPC );
if( GetIsObjectValid( oPartyMember ))
{ // He spoke the name of a party member.
if( oPartyMember == oPC )
{ // He spoke his own name. So send him an error.
SetPCChatMessage( “Camaraderie does not work on yourself. Nothing happens.” );
SetPCChatVolume( TALKVOLUME_TELL );
}
else
{ // He identified another party member. So teleport that member to the
// caster’s location along with some visuals at his old and new spots.
ApplyEffectAtLocation( DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_FNF_SMOKE_PUFF ), GetLocation( oPartyMember ));
AssignCommand( oPartyMember, JumpToLocation( GetLocation( oPC )));
AssignCommand( oPartyMember, ApplyEffectToObject( DURATION_TYPE_INSTANT, EffectVisualEffect( VFX_IMP_POLYMORPH ), oPartyMember ));
}
// Camaraderie spell was handled. So cancel the spell and notify the
// OnPlayerChat script that the chat was recognized and handled.
DeleteLocalInt( oPC, "AS_Camaraderie_WAIT_FOR_NAME" );
return TRUE;
}
}
// The chat did not identify a party member or the speaker was not expected
// to give a party member name by the camaraderie spell, so signal
// OnPlayerChat that the chat was not recognized and nothing was handled.
return FALSE;
}
//void main() {}