Blink Spell scripts

I have two blink spell scripts I took off the vault. Both work (they blink the PC around each with its own unique VFX and script, BUT…the PC just keeps on blinking non-stop. Can someone help me with a script that just makes the PC blink say 2d6 times then it stops. I wouldn’t want one that does say 1d4+1 (per level) as that would blink a PC way too many times at 40th lv casting. I think just 2d6 times would be sufficient. Below are the two scripts and one of the scripts one of the spells call on.

I cannot see a time limit or # of blinks on either.

as_blink.nss

// Blink spell script.
//:://////////////////////////
// as_blink.nss
//:://////////////////////////
// Innate Level: 3th
// School: Enchantment
// Descriptor(s):
// Components: Somatic
// Range: Short
// Area of Effect: Caster
// Duration: Instant
// Metamagic: None
// Counter Spells:
// Save: None
// Spell Resistance: No
// Description: Blink causes the caster to vanish and reappear at
// a random nearby location 8-15 meters away. The
// caster has no control over where he blinks to.
//:://////////////////////////
#include “_axe_spells_inc”

//::////////////////////////////////
// Blink spell script main function.
//::////////////////////////////////
void main()
{ object oCaster = OBJECT_SELF;

// Spellcast Hook Code
if( !X2PreSpellCastCode())
{ // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
return;
}

// Find the target position for the blink.
float fAngle = IntToFloat( Random( 3600 )) /10.0; // direction of blink.
float fDist = GetRandomDelay( 0.0, 7.0 ) +8.0; // distance to blink.
vector vPos = VectorNormalize( AngleToVector( fAngle ));
vPos *= fDist;
vPos += GetPosition( oCaster );

// Compute target location.
object oArea = GetArea( oCaster );
location lTarget = Location( oArea, vPos, 0.0 );

// Face back towards where he blinked from.
lTarget = Location( oArea, vPos, GetAngleToTargetLocation( lTarget, GetLocation( oCaster ) ));

// Show the spell impact visual effect.
effect eImpact = EffectVisualEffect( VFX_FNF_SMOKE_PUFF );
ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eImpact, GetLocation( oCaster ));

// Blink the caster to the target.
JumpToLocation( lTarget );
}

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

cs_s1_blink.nss

void main()
{

      object oArea = GetArea(OBJECT_SELF);
      float fDelay = 2.0f;
      int i;
      int pn;
      int iFace;
      vector vOM1;
        while (i < GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF))
    {
      pn = d4();
      vOM1 = GetPosition(OBJECT_SELF);
      switch (pn)
        {
        case 1:
         vOM1.x += IntToFloat(d8());
         vOM1.y += IntToFloat(d8());
         break;
        case 2:
         vOM1.x -= IntToFloat(d8());
         vOM1.y += IntToFloat(d8());
         break;
        case 3:
         vOM1.x += IntToFloat(d8());
         vOM1.y -= IntToFloat(d8());
         break;
        case 4:
         vOM1.x -= IntToFloat(d8());
         vOM1.y -= IntToFloat(d8());
         break;
         }
      iFace = d100();
      location lPC1 = Location(oArea, vOM1, IntToFloat(iFace));

      DelayCommand(fDelay, AssignCommand(OBJECT_SELF, ActionJumpToLocation(lPC1)));
      fDelay = (IntToFloat(i)*6.0f)+2.0f;
     i++;
    }

}

I cannot see a time limit or # of blinks on either.

I don’t see this either. Actually the PC should jump just once.Could only be a sideeffect of something else.

Replace the core script with the following

#include "x0_i0_spells"
#include "x2_inc_spellhook"

float GetAngleToTargetLocation( location lSource, location lTarget)
{ if( !GetIsObjectValid( GetAreaFromLocation( lSource)) || !GetIsObjectValid( GetAreaFromLocation( lTarget))) return -1.0;

return VectorToAngle( GetPositionFromLocation( lTarget) -GetPositionFromLocation( lSource));
}

void main()
{ object oCaster = OBJECT_SELF;

// Spellcast Hook Code
if( !X2PreSpellCastCode())
{ // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
return;
}

// Find the target position for the blink.
float fAngle = IntToFloat( Random( 3600 )) /10.0; // direction of blink.
float fDist = GetRandomDelay( 0.0, 7.0 ) +8.0; // distance to blink.
vector vPos = VectorNormalize( AngleToVector( fAngle ));
vPos *= fDist;
vPos += GetPosition( oCaster );

// Compute target location.
object oArea = GetArea( oCaster );
location lTarget = Location( oArea, vPos, 0.0 );

// Face back towards where he blinked from.
lTarget = Location( oArea, vPos, GetAngleToTargetLocation( lTarget, GetLocation( oCaster ) ));

// Show the spell impact visual effect.
effect eImpact = EffectVisualEffect( VFX_FNF_SMOKE_PUFF );
ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eImpact, GetLocation( oCaster ));

// Blink the caster to the target.
JumpToLocation( lTarget );
}

Still hopping around like a madman??

Hi Mmat,

Thanks for responding and for the help. I tried the changes you gave me above. This is my script where I incorporated your part. I had to delete the #include “_axe_spells_inc” in the original script because it was conflicting with the ```
#include “x0_i0_spells”

So it compiled after I took out that inc.

#include "x0_i0_spells"
#include "x2_inc_spellhook"

float GetAngleToTargetLocation( location lSource, location lTarget)
{ if( !GetIsObjectValid( GetAreaFromLocation( lSource)) || !GetIsObjectValid( GetAreaFromLocation( lTarget))) return -1.0;

return VectorToAngle( GetPositionFromLocation( lTarget) -GetPositionFromLocation( lSource));
}

void main()
{ object oCaster = OBJECT_SELF;

// Spellcast Hook Code
if( !X2PreSpellCastCode())
{ // If code within the PreSpellCastHook (i.e. UMD) reports FALSE, do not run this spell
return;
}

// Find the target position for the blink.
float fAngle = IntToFloat( Random( 3600 )) /10.0; // direction of blink.
float fDist = GetRandomDelay( 0.0, 7.0 ) +8.0; // distance to blink.
vector vPos = VectorNormalize( AngleToVector( fAngle ));
vPos *= fDist;
vPos += GetPosition( oCaster );

// Compute target location.
object oArea = GetArea( oCaster );
location lTarget = Location( oArea, vPos, 0.0 );

// Face back towards where he blinked from.
lTarget = Location( oArea, vPos, GetAngleToTargetLocation( lTarget, GetLocation( oCaster ) ));

// Show the spell impact visual effect.
effect eImpact = EffectVisualEffect( VFX_FNF_SMOKE_PUFF );
ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eImpact, GetLocation( oCaster ));

// Blink the caster to the target.
JumpToLocation( lTarget );
}

I’m still blinking around…ugh.

What about the other blink script I also posted? Oh by the way…in your changes I still don’t see the amount of times to blink. Is it possible to make it blink say 2d6 times only?

Probably the second script is the one that is running. It’s scheduling a number of blinks equal to the caster’s wizard levels. The other one and the one mmat posted should really only do 1 blink unless called repeatedly.

It won’t compile with the “_axe_spells_inc”. Which would need to be called upon. Which I posted that script above. It does not blink just once. It keeps blinking non-stop.

I had to delete the “_axe_spells_inc” and replace it with "#include “x0_i0_spells” for it to compile.

Anyone have a working “Blink Spell” script? Or can just make one that blinks say 2d6 times?

I meant this one is the one that is probably running. The other versions may not be running since they should only be jumping once.

Both scripts make me blink non-stop. I looked at the script “cs_s1_blink” I guess after looking at it you are right…that part:

while (i < GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF))

Would do by level. It is weird that it is scripted that way.

What does “pn = d4()” do?..is it suppose to do something? Is there a way I could rewrite this script to make it only blink 2d6 times?

Hi meaglyn,

I tested the one script again. The cs_s1_blink

You are correct. It blinks a PC as per number of Wizard levels. I guess casting it as a 30th level caster made me think it was blinking non-stop. I had blinked 30 times. good grief…too many times.

Can this be fixed to a specific number of blinks?

Maybe add a VFX effect and sound too would be nice

Hi meaglyn and Mmat,

I tried to blend the two scripts into the one below (I want to add the effect of the one to the other). I tried my best but it says the end compound is an error. Also, I really have no clue on how to switch it from one blink per level to a 2d6 number of blinks.

void main()
{

      object oArea = GetArea(OBJECT_SELF);
      float fDelay = 2.0f;
      int i;
      int pn;
      int iFace;
      vector vOM1;
        while (i < GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF))
    {
      pn = d4();
      vOM1 = GetPosition(OBJECT_SELF);
      switch (pn)
        {
        case 1:
         vOM1.x += IntToFloat(d8());
         vOM1.y += IntToFloat(d8());
         break;
        case 2:
         vOM1.x -= IntToFloat(d8());
         vOM1.y += IntToFloat(d8());
         break;
        case 3:
         vOM1.x += IntToFloat(d8());
         vOM1.y -= IntToFloat(d8());
         break;
        case 4:
         vOM1.x -= IntToFloat(d8());
         vOM1.y -= IntToFloat(d8());
         break;
         }
     // Show the spell impact visual effect.
        effect eImpact = EffectVisualEffect( VFX_FNF_SMOKE_PUFF );
        ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eImpact, GetLocation( oCaster ));
       {
      iFace = d100();
      location lPC1 = Location(oArea, vOM1, IntToFloat(iFace));

      DelayCommand(fDelay, AssignCommand(OBJECT_SELF, ActionJumpToLocation(lPC1)));
      fDelay = (IntToFloat(i)*6.0f)+2.0f;
     i++;
    }

}

that was the objective. Back to vanilla since there is no reason to use an axe (or even a hatchet) in that script.

But obviously it wasn’t the script which was executed by the spell.

The compound error appeared for me too and was gone after I corrected the spelling of the #include statements (all lower case)

1 Like

Any idea how I would put an VFX effect and sound and number of blinks?

Use a sorcerer with this script. he will not blink. Or replace “GetLevelByClass(CLASS_TYPE_WIZARD, OBJECT_SELF)” with “d6(2)” which will be an average of 7 blinks for anybody.

What does “pn = d4()” do?

it varies the direction of the blink, This could be done easier, but will work.

ApplyEffectAtLocation( DURATION_TYPE_INSTANT, eImpact, GetLocation( oCaster ));

This will apply the effect at the location of the caster. Replace “GetLocation( oCaster )” with “lPC1” to apply it at the target.

The way that script is scheduling the blinks, using delaycommand, adding the effect like that will make all
~7 blink locations have the impact effect all at once and the pc will then blink to each location that is marked by an impact later on.

You’d want to create a function that does the jump and the impact effect and then delaycommand that.

Hi Mmat, :slight_smile:

Ok I did the changes as you suggested. I still got an error message as shown below related to the effect:

Tested the spell without the effect. Works as it should. Awesome - thanks!!

Just need the effect and its all good :slight_smile:

Can you recommend a better effect that would look better than a puff of smoke?

Thanks meaglyn for your input :slight_smile:

Do you think people would like this spell to be released to the vault? I can do that. Of course I would give credit to those who helped.