This the code to make the trailto command, an idea I got from a coffeemud command
but this code was written from scratch using the track code. What it does is take
a start and and end point, and return a string of directions between them. You can
specify either a room vnum or a character/mob for either of the arguments. This can
be a handy command for immortals trying to give directions to a lost newbie, or can
even be used to compile a list of walking directions to popular places etc.

Example output: Path: 3w, 2n, u, 5n, e.

Installation:

In Track.cpp:
Find the function find_first_step and change the function header from
	int find_first_step (CRoomIndexData *src, CRoomIndexData *target, int maxdist)
to
	int find_first_step (CRoomIndexData *src, CRoomIndexData *target, int maxdist, BOOL bBetweenAreas = FALSE)

This adds another argument to find_first_step that defaults to FALSE, but if you call it
with the argument set to TRUE the function will work between areas. You won't have to
make any changes to do_track this way because it'll default to FALSE for the calls
from that function. You could easily make it so that do_track will work between areas
as well though, or make a more advanced tracking skill even...

down in the function itself change
   if (src->GetArea () != target->GetArea ())
      return BFS_NO_PATH;
to
   if (!bBetweenAreas && src->GetArea () != target->GetArea ())
      return BFS_NO_PATH;





//Add this to mud.h/smaug.h by the dir_name definition
extern char*	const	short_dir_name	[];


Add this to act_move.cpp or wherever dir_name is defined :
//Make sure these are kept in sync with dir_name above, used to construct
//whole path strings and make them a lot shorter character-wise - Kurgan
char *	const	short_dir_name	[]		=
{
    "n", "e", "s", "w", "u", "d",
    "ne", "nw", "se", "sw", "somewhere"
};


Add the following two functions to track.cpp:

//Function will return a string containing the full path from start to dest 
// (providing the way to get there isn't a mptransfer or somesuch) - Kurgan
char *get_path_string(CRoomIndexData *start, CRoomIndexData *dest)
{
	static char buf[MAX_STRING_LENGTH*2];

	char temp [MAX_STRING_LENGTH];

	int  last_dir = -1;
	int  dircount = 0;   //used to count how many times we move in the same direction
	CRoomIndexData *curr_room = NULL; //room we're currently checking from
	CExitData      *pExit = NULL; //used to get curr_room
	int dir = -1; //What direction we're moving next

	buf[0] = '\0';

	//Couple of checks
	if (!start) {
		bug ("Function get_path_string: NULL start room.");
		return NULL;
	}
	if (!dest) {
		bug ("Function get_path_string: NULL dest room.");
		return NULL;
	}

	curr_room = start;

	BOOL done = FALSE;

	
	//Start our big loop here
	while (!done) {
		
		//If there's ever a problem with finding a path to a place
                //that can be walked to on the mud, can increase the distance
                //here I suppose
		dir = find_first_step (curr_room, dest, 1000000, TRUE);

		switch (dir) {

			case BFS_ERROR:
				bug ("Function get_path_string: Error finding path.");
				return NULL;
			case BFS_ALREADY_THERE:
				done = TRUE;
				break;
			case BFS_NO_PATH:
				return NULL;
			default:
				break;
		}
		
		if (!done) {

			pExit = get_exit (curr_room, dir);
			curr_room = pExit->GetToRoom ();


			//Keep track of how many times we go the same direction so we can do output
			//like 3E, 2SE, N, NE, 5E etc.
			if (last_dir == -1) {
				dircount = 1;
				last_dir = dir;
			}
			else {
				if (dir != last_dir) {
					if (dircount > 1)
						sprintf (temp, "%d%s, ", dircount, short_dir_name[last_dir]);
					else
						sprintf (temp, "%s, ", short_dir_name[last_dir]);

					strcat (buf, temp);
					last_dir = dir;
					dircount = 1;
				}
				else
					++dircount;
			}


			if (curr_room->vnum == dest->vnum) {
				if (dircount > 1)
					sprintf (temp, "%d%s.", dircount, short_dir_name[dir]);
				else
					sprintf (temp, "%s.", short_dir_name[dir]);
				strcat (buf, temp);
			}
		}

	}

	return buf;
}


//Function to find and return a string of directions to and from:
// 1) character calling to specified character
// 2) character specified to another character specified.
// 3) character calling to a specified vnum
// 4) specified vnum to another specified vnum
// or any combination of the above: eg. specified vnum to character specified
void do_trailto (CCharacter *ch, char *argument)
{
	char arg1[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];

	CRoomIndexData *start = NULL;
	CRoomIndexData *dest  = NULL;
	CCharacter *start_ch = NULL;
	CCharacter *dest_ch = NULL;

	int svnum = 0;
	int dvnum = 0;

	char *buf = NULL;
	
	argument = one_argument (argument, arg1);
	argument = one_argument (argument, arg2);

	//Can't just trailto nothing....
	if (arg1[0] == '\0') {
		ch->SendText ("Usage: trailto <character/roomvnum> [character/roomvnum]\n\r");
		return;
	}

	
	//Now we need to get which arguments are which and get out starting 
	//and destination points.
	if (arg2[0] == '\0')  {
		//If only one argument specified we make the starting point
		//the calling character's room
		if ((start = ch->GetInRoom ()) == NULL) {
			bug ("Function do_trailto: NULL ch->GetinRoom ()");
			return;
		}
		//Now see if the argument given is in the form of a vnum or a
		//character/mob name
		if (is_number (arg1)) {
			dvnum = atoi(arg1);
			if (dvnum < 1 || dvnum > 2097152000) {
				bug ("Function do_trailto: destination vnum out of range.");
				return;
			}

			if ((dest = RoomTable.GetRoom (dvnum)) == NULL) {
				bug ("Function do_trailto: NULL destination room (%d).", dvnum);
				return;
			}

		}
		else {

			if ((dest_ch = get_char_world (ch, arg1)) == NULL) {
				ch->SendText ("Target character/mob not found.\n\r");
				return;
			}

			if ((dest = dest_ch->GetInRoom ()) == NULL) {
				bug ("Function do_trailto: NULL dest_ch->GetInRoom ().");
				return;
			}
		}
	}
	//Two arguments specified so let's see what we got
	else {
		//see if the arg1 is in the form of a vnum or a
		//character/mob name
		if (is_number (arg1)) {
			svnum = atoi(arg1);
			if (svnum < 1 || svnum > 2097152000) {
				bug ("Function do_trailto: start vnum out of range.");
				return;
			}

			if ((start = RoomTable.GetRoom (svnum)) == NULL) {
				bug ("Function do_trailto: NULL start room (%d).", dvnum);
				return;
			}

		}
		else {

			if ((start_ch = get_char_world (ch, arg1)) == NULL) {
				ch->SendText ("Start character/mob not found.\n\r");
				return;
			}

			if ((start = start_ch->GetInRoom ()) == NULL) {
				bug ("Function do_trailto: NULL start_ch->GetInRoom ().");
				return;
			}
		}
		
		//see if the arg2 is in the form of a vnum or a
		//character/mob name
		if (is_number (arg2)) {
			dvnum = atoi(arg2);
			if (dvnum < 1 || dvnum > 2097152000) {
				bug ("Function do_trailto: destination vnum out of range.");
				return;
			}

			if ((dest = RoomTable.GetRoom (dvnum)) == NULL) {
				bug ("Function do_trailto: NULL destination room (%d).", dvnum);
				return;
			}

		}
		else {

			if ((dest_ch = get_char_world (ch, arg2)) == NULL) {
				ch->SendText ("Destination character/mob not found.\n\r");
				return;
			}

			if ((dest = dest_ch->GetInRoom ()) == NULL) {
				bug ("Function do_trailto: NULL dest_ch->GetInRoom ().");
				return;
			}
		}
	}

	//Okay, at this point we presumably have our start and dest rooms.

	if (start->vnum == dest->vnum) {
		ch->SendText ("No need to find a path...they're already in the same spot...\n\r");
		return;
	}

	//This next little bit is actually the guts of the thing...
	if ((buf = get_path_string (start, dest)) == NULL) {
		ch->SendText ("Unable to find a path for some reason...\n\r");
		return;
	}

	set_char_color (AT_GREEN, ch);
	ch->SendTextf ("Path: %s\n\r", buf);


	return;

}

Add table entries in Skills.cpp for do_trailto like any other command.
Also add a DO_FUN entry in smaug.h with the rest of the commands.
Clean and compile, and make the trailto command online with cedit.
cedit trailto create do_trailto
This command is meant to be for immortals so the level should be
set to 51 or higher.

Note: My code compiles without having to pre-declare local functions headers
so if you have a problem you might need to add the function prototype for
get_path_string near the beginning of track.cpp

And that's all there is to it.

This code was developed on a highly modified SmaugWiz 2.02 codebase, but it should
work fine with the stock code (I hadn't made any changes to the tracking code prior
to writing this). Use it, abuse it. Change it to be better if you like. Enjoy. I've
ported over -lots- of smaug code to SmaugWiz, so I don't think it would be very hard
to port this over to regular smaug code, but that's up to anyone who wants to
do it.