FANDOM


Homing Device Edit

What it does Edit

An OnActivateItem script that allows an item to work as a homing device. If the object being searched for is in the same area as the PC, the PC will face in the direction of the object and be given the distance and compass direction. If the object is not in the same area, the PC will be pointed toward the area transition that begins the shortest path to the object.

Notes Edit

Basically, this script does a recursive search through the areas of the module to find an object. Since recursion can be stack intensive, the depth of the search can be controlled by the constant int RECURSION_DEPTH, defined at the beginning of the script. This limits how many areas distant the object can be and still be found. I have tested to a depth of twenty without a problem.

To use, you will need to change three strings in the main function. Change "Compass" to the tag of the item the PC will use as a direction finder. This item should have Unique Power Self Only. Also change the two occurences of "homingstone" to the tag of the item that the player will be guided toward.

In order for this script to work properly, each area should have a unique tag. Also it will not detect if an area is impassable to the PC, for instance if it contains a wall the PC cannot pass through.

The code in the main function first looks to see if the searched for object is in the possession of someone or something. If it is, the PC will be guided toward that object. This could allow for a rather intriguing PvP mod, with everyone searching (and killing each other) for some important object. Or you could modify the code in main to get the searched for object using GetLocalObject on the PC. Then the PCs could have unique searchable items, to be used as a personal homing device, etc.

The Script Edit

//*******************************************************
// OnActivateItem script for a homing device
// Written by John Werner
// In the main function, change "Compass" to the tag of
// the activatable item, and "homingstone" to the tag of
// the object to search for.
//*******************************************************
// Constant to control depth of recursive search
int RECURSION_DEPTH = 20;
//*******************************************************
// RecursiveSearch
// Recursive function to search areas for an object
// Parameters:
// oGrail - object being searched for
// oTransit - area transition target of the door or
//   trigger just traversed
// oDataStore - invisible object used for temporary data
//   storage
// fDist - float containing the distance from the PC to
//   the oTransit object
// iDepth - int to control recursion depth
//*******************************************************
void RecursiveSearch( object oGrail, object oTransit,
  object oDataStore, float fDist, int iDepth )
{
  // If the area containing the object has been found
  if ( GetArea( oGrail ) == GetArea( oTransit ) )
  {
    // Calculate the total path distance from PC to object
    float fPathDist = fDist + GetDistanceBetween( oTransit, oGrail );
    // If the path distance for this branch has not been set
    // or the path distance is the shortest path found so far,
    // store the path distance on the data store object
    if ( GetLocalFloat( oDataStore, "fDistToGrail" ) == -1.0
      || fPathDist < GetLocalFloat( oDataStore, "fDistToGrail" ) )
      SetLocalFloat( oDataStore, "fDistToGrail", fPathDist );
    return;
  }
  // If this area has been visited before, return
  if ( GetLocalInt( oDataStore, GetTag( GetArea( oTransit ) ) ) == 1 )
    return;
  // If the recursion depth has been exceded, return
  if ( iDepth < 0 )
    return;
  // Create a variable in the data store object to indicate
  // the current area has been visited
  SetLocalInt( oDataStore, GetTag( GetArea( oTransit ) ), 1 );
  // Create an index variable
  int nNth = 1;
  // Get the nearest door
  object oExit = GetNearestObject( OBJECT_TYPE_DOOR, oTransit, nNth );
  // Loop through all doors in the area, get the transition
  // target and call the recursive search function on that target
  while ( GetIsObjectValid( oExit )
    && GetArea( oExit ) == GetArea( oTransit ) )
  {
    object oNewTarget = GetTransitionTarget( oExit );
    if ( GetIsObjectValid( oNewTarget ) )
    {
      RecursiveSearch( oGrail, oNewTarget, oDataStore,
        fDist + GetDistanceBetween( oTransit, oExit ), iDepth - 1 );
    }
    nNth++;
    oExit = GetNearestObject( OBJECT_TYPE_DOOR, oTransit, nNth );
  }
  // Reset the index variable
  nNth = 1;
  // Get the nearest trigger
  oExit = GetNearestObject( OBJECT_TYPE_TRIGGER, oTransit, nNth );
  // Loop through all triggers in the area, get the transition
  // target and call the recursive search function on that target
  while ( GetIsObjectValid( oExit )
    && GetArea( oExit ) == GetArea( oTransit ) )
  {
    object oNewTarget = GetTransitionTarget( oExit );
    if ( GetIsObjectValid( oNewTarget ) )
    {
      RecursiveSearch( oGrail, oNewTarget, oDataStore,
        fDist + GetDistanceBetween( oTransit, oExit ), iDepth - 1 );
    }
    nNth++;
    oExit = GetNearestObject( OBJECT_TYPE_TRIGGER, oTransit, nNth );
  }
}
//*****************************************************
// SearchAreas
// Function to search surrounding areas for an object
// The object this function returns will be the area
// transition that begins the shortest path to the
// object, or OBJECT_INVALID if the object is not found
// oPC - PC searching for the object
// oGrail - the object of the search
//*****************************************************
object SearchAreas( object oPC, object oGrail )
{
  // Initialize a float to track the shortest
  // path to the oGrail object
  float fNearest = 999999999.9;
  // Initialize an object to track the door or trigger
  // that begins the shortest path
  object oNearestExit = OBJECT_INVALID;
  // Initialize an index
  int nNth = 1;
  // Get nearest door
  object oExit = GetNearestObject( OBJECT_TYPE_DOOR, oPC, nNth );
  // Loop through all doors in the same area as the PC
  while ( GetIsObjectValid( oExit )
    && GetArea( oExit ) == GetArea( oPC ) )
  {
    // Get the transition target of the door
    object oTransit = GetTransitionTarget( oExit );
    if ( GetIsObjectValid( oTransit ) )
    {
      // Create an invisible object to use as a data store
      object oDataStore = CreateObject( OBJECT_TYPE_CREATURE,
        "plc_invisobj", GetLocation( oPC ) );
      // Create a variable on the data store to indicate
      // this area has been visited
      SetLocalInt( oDataStore, GetTag( GetArea( oPC ) ), 1 );
      // Create a float on the data store to record
      // the shortest path via the current door
      SetLocalFloat( oDataStore, "fDistToGrail", -1.0 );
      // Call the recursive search on the transition target
      // of the door
      RecursiveSearch( oGrail, oTransit, oDataStore,
        GetDistanceBetween( oPC, oExit ), RECURSION_DEPTH );
      // Get the length of the shortest path through the current door
      float fDist = GetLocalFloat( oDataStore, "fDistToGrail" );
      // If a path was found and it is shorter than previous paths,
      // store the distance and the door that begins the path
      if ( fDist != -1.0 && fDist < fNearest )
      {
        fNearest = fDist;
        oNearestExit = oExit;
      }
      // Destroy the data store
      DestroyObject( oDataStore );
    }
    nNth++;
    oExit = GetNearestObject( OBJECT_TYPE_DOOR, oPC, nNth );
  }
  // Reset the index variable
  nNth = 1;
  // Get the nearest trigger
  oExit = GetNearestObject( OBJECT_TYPE_TRIGGER, oPC, nNth );
  // Loop through all triggers in the same area as the PC
  while ( GetIsObjectValid( oExit )
    && GetArea( oExit ) == GetArea( oPC ) )
  {
    // Get the transition target of the trigger
    object oTransit = GetTransitionTarget( oExit );
    if ( GetIsObjectValid( oTransit ) )
    {
      // Create an invisible object to use as a data store
      object oDataStore = CreateObject( OBJECT_TYPE_CREATURE,
        "plc_invisobj", GetLocation( oPC ) );
      // Create a variable on the data store to indicate
      // this area has been visited
      SetLocalInt( oDataStore, GetTag( GetArea( oPC ) ), 1 );
      // Create a float on the data store to record
      // the shortest path via the current door
      SetLocalFloat( oDataStore, "fDistToGrail", -1.0 );
      // Call the recursive search on the transition target
      // of the door
      RecursiveSearch( oGrail, oTransit, oDataStore,
        GetDistanceBetween( oPC, oExit ), RECURSION_DEPTH );
      // Get the length of the shortest path through the current door
      float fDist = GetLocalFloat( oDataStore, "fDistToGrail" );
      // If a path was found and it is shorter than previous paths,
      // store the distance and the door that begins the path
      if ( fDist != -1.0 && fDist < fNearest )
      {
        fNearest = fDist;
        oNearestExit = oExit;
      }
      // Destroy the data store
      DestroyObject( oDataStore );
    }
    nNth++;
    oExit = GetNearestObject( OBJECT_TYPE_TRIGGER, oPC, nNth );
  }
  // Return the door or trigger that begins the shortest path
  return oNearestExit;
}
void main()
{
  object oItem = GetItemActivated();
  if ( GetIsObjectValid( oItem ) )
  {
    string sTag = GetTag( oItem );
    if ( sTag == "Compass" )
    {
      object oPC = GetItemActivator();
      // Attempt to get the possessor of the object of the search
      object oGrail = GetItemPossessor( GetObjectByTag( "homingstone" ) );
      // If the object of the search is not possessed by anyone,
      // then get the object
      if ( !GetIsObjectValid( oGrail ) )
        oGrail = GetObjectByTag( "homingstone" );
      // If we have a valid PC
      if ( GetIsObjectValid( oPC ) )
      {
        // Create the effect on the PC
        ApplyEffectToObject( DURATION_TYPE_INSTANT,
          EffectVisualEffect( VFX_IMP_HEAD_MIND ), oPC );
        // Create a boolean to indicate if the Grail
        // is in the same area
        int nInArea = TRUE;
        // Check to be sure the PC is in the same area
        // as the Grail object.  If not, find the Grail
        if ( GetArea( oPC ) != GetArea( oGrail ) )
        {
          oGrail = SearchAreas( oPC, oGrail );
          nInArea = FALSE;
        }
        if ( GetIsObjectValid( oGrail ) )
        {
          // Get the position of the PC
          vector vPCpos = GetPosition( oPC );
          // Get the position of the Grail object
          vector vGrailPos = GetPosition( oGrail );
          // Get the difference in y positions
          float fRise = vGrailPos.y - vPCpos.y;
          // Get the difference in x positions
          float fRun = vGrailPos.x - vPCpos.x;
          // Create strings to hold the heading
          string sHeading, sDir1, sDir2;
          // Get the distance to the Grail object
          string sDist = FloatToString( GetDistanceBetween( oPC, oGrail ) );
          // Trim the whitespace from the distance
          while ( GetSubString( sDist, 0, 1 ) == " " )
            sDist = GetStringRight( sDist, GetStringLength( sDist ) - 1 );
          // Truncate the distance to tenths
          sDist = GetSubString( sDist, 0, FindSubString( sDist, "." ) + 2 );
          // Get the correct direction names for the relative quadrant
          if ( fRise > 0.0 )
            sDir1 = "North";
          else
            sDir1 = "South";
          if ( fRun > 0.0 )
            sDir2 = "East";
          else
            sDir2 = "West";
          // Check to prevent a divide-by-zero error
          if ( fRun != 0.0 )
          {
            // Calculate the slope of the line
            float fSlope = fabs( fRise / fRun );
            // Translate the slope to direction
            if ( fSlope < 0.25 )
              sHeading = sDir2;
            else if ( fSlope < 0.75 )
              sHeading = sDir2 + " by " + sDir1 + GetStringLowerCase( sDir2 );
            else if ( fSlope < 1.5 )
              sHeading = sDir1 + GetStringLowerCase( sDir2 );
            else if ( fSlope < 3.0 )
              sHeading = sDir1 + " by " + sDir1 + GetStringLowerCase( sDir2 );
            else
              sHeading = sDir1;
          }
          else
            sHeading = sDir1;
          // Turn the PC toward the target
          AssignCommand( oPC, SetFacingPoint( GetPosition( oGrail ) ) );
          // Give the PC directions
          // If the object is in the same area,
          // give the direction and distance
          if ( nInArea )
          {
            FloatingTextStringOnCreature( sDist + "m " + sHeading, oPC );
          }
          // If the object is in another area,
          // give the direction and distance to the door
          // or trigger that begins the shortest path
          else
          {
            FloatingTextStringOnCreature( "The object you seek is very distant", oPC );
            DelayCommand( 2.0, FloatingTextStringOnCreature( "Take the passage " + sDist + "m " + sHeading, oPC ) );
          }
        }
        // The object could not be found
        else
          FloatingTextStringOnCreature( "Alas, the path is too dim to follow", oPC );
      }
    }
  }
}

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.