/*------------------------------------------------------------------------------
This snippet is a simple addition to the goto, at, and transfer commands, which
enables you to create and maintain a list of symbolic location names.  For 
instance, on End of Time (the MUD for which I originally wrote this), you could
type 'goto termina' to reach a room on the world map just south of Termina.
--------------------------------------------------------------------------------

Add this to your other structure typedefs in merc.h...

typedef struct location_data LOCATION;


Add this wherever you'd like in merc.h...

struct location_data
{
	bool		valid;
	LOCATION *	next;
	char *		name;
	long		vnum;
	int			level;
};


Add this with your other function prototypes in merc.h...

ROOM_INDEX_DATA *	location_lookup		(CHAR_DATA * ch, char * argument);
LOCATION *			new_loc				(void);
void				free_loc			(LOCATION * loc);
void				save_locations		(void);
void				load_locations		(void);


Add this to merc.h along with similar things, such as char_list...

extern LOCATION * location_list;


Add this to interp.h, and add the corresponding command entry to interp.c.
To get decent use out of it, I recommend making it LEVEL_IMMORTAL.

DECLARE_DO_FUN(	do_locations	);


Now, in act_wiz.c, add this to the top of find_location():

	ROOM_INDEX_DATA * pRoom;


In the same function, add this, below the is_number check:

	if ((pRoom = location_lookup (ch, arg)) != NULL)
        return pRoom;


Add this to db.c, with load_bans() and such:

		load_locations ();


Now, add the rest of this file (location.c) to your src directory, and if
needed, to your makefile.  Do a clean compile, and you should be good to go.

To generate a locations.txt file, just make a location and use the save option.
--------------------------------------------------------------------Cut Here--*/
/***************************************************************************
 * This code may be used freely within any non-commercial MUD, all I ask   *
 * is that these comments remain in tact and that you give me any feedback *
 * or bug reports you come up with.                                        *
 *                                  -- Midboss (eclipsing.souls@gmail.com) *
 ***************************************************************************/
#if defined(macintosh)
#include <types.h>
#else
#include <sys/types.h>
#include <sys/time.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "merc.h"

LOCATION * location_list;

/*
 * Syntax: location
 * Syntax: location add <name> <vnum>
 * Syntax: location delete <name> [level]
 * Syntax: location save
 */
void do_locations (CHAR_DATA * ch, char * argument)
{
	char arg[MIL], arg2[MIL], arg3[MIL];
	LOCATION * pLoc;

	argument = one_argument (argument, arg);
	argument = one_argument (argument, arg2);
	argument = one_argument (argument, arg3);

	//Are we looking for a list?
	if (arg[0] == '\0' || !str_prefix (arg, "list"))
	{
		bool found = FALSE;
		char buf[MSL];

		//Initialize the buffer...
		buf[0] = '\0';

		//Header.
		sprintf (buf + strlen (buf), 
					"{DYou may goto the following pre-defined points:{x\n\r");

		//Let's build a list.
		for (pLoc = location_list; pLoc != NULL; pLoc = pLoc->next)
		{
			//We can use this one, right?
			if (pLoc->level > get_trust (ch)) continue;

			//Set it to true.
			if (!found) found = TRUE;

			//Add it to the buffer.
			sprintf (buf + strlen (buf), 
						"{W%-20s {r[{Droom %ld{r, {Dlevel %d{r]{x\n\r",
						pLoc->name, pLoc->vnum, pLoc->level);
		}

		//None found?
		if (!found)
			send_to_char (
				"{DYou may not use any pre-defined goto points.{x\n\r", ch);
		//Send the buffer.
		else
			send_to_char (buf, ch);

		return;
	}

	else if (!str_prefix (arg, "save"))
		save_locations ();

	//Creating one?
	else if (!str_prefix (arg, "add") || !str_prefix (arg, "create"))
	{
		ROOM_INDEX_DATA * pRoom;
		long vnum;
		int level;

		//Not just anyone can do this.
		if (get_trust (ch) < SUPREME)
		{
			send_to_char ("{DYou're not authorized to do that.{x\n\r", ch);
			return;
		}

		//We have a name, right?
		if (arg2[0] == '\0')
		{
			send_to_char ("{DYou need to provide a name.{x\n\r", ch);
			return;
		}

		//Idiot-proofing.
		if (strlen (arg2) > 20 || strlen (arg2) < 3)
		{
			send_to_char (
				"{DThat name is either too long or too short.{x\n\r", ch);
			return;
		}

		//Make sure we have a vnum.
		if (arg3[0] == '\0' || !is_number (arg3))
		{
			send_to_char ("{DYou need to provide a room vnum.{x\n\r", ch);
			return;
		}

		//Find out if the room exists.
		vnum = atoi (arg3);

		if ((pRoom = get_room_index (vnum)) == NULL)
		{
			send_to_char ("{DThat room doesn't exist.{x\n\r", ch);
			return;
		}

		//Optional level setting.
		if (argument[0] == '\0')
			level = LEVEL_IMMORTAL;
		else
		{
			//Not a number?
			if (!is_number (argument))
			{
				send_to_char ("{DLevels must be numerical.{x\n\r", ch);
				return;
			}

			//Make sure it's a legal level.
			level = atoi (argument);

			if (level > get_trust (ch) || level < LEVEL_IMMORTAL)
			{
				send_to_char ("{DYou can't do that.{x\n\r", ch);
				return;
			}
		}

		//Create the location and stick it in the list.
		pLoc = new_loc ();

		free_string (pLoc->name);
		pLoc->name = str_dup (arg2);
		pLoc->vnum = vnum;
		pLoc->level = level;

		pLoc->next = location_list;
		location_list = pLoc;
		return;
	}

	else if (!str_prefix (arg, "delete"))
	{
		LOCATION * list;
		LOCATION * prev;

		//Can we do it?
		if (get_trust (ch) < SUPREME)
		{
			send_to_char ("{DYou're not authorized to do that.{x\n\r", ch);
			return;
		}

		//No arg?
		if (arg2[0] == '\0')
		{
			send_to_char ("{DWhich goto point do you want to delete?{x\n\r", ch);
			return;
		}

		//Make sure it's NULL, then try to find one.
		pLoc = NULL;

		for (list = location_list; list != NULL; list = list->next)
		{
			if (list->level > get_trust (ch)) continue;

			if (!str_prefix (arg2, list->name))
			{
				pLoc = list;
				break;
			}
		}

	
		//No location?
		if (pLoc == NULL)
		{
			send_to_char ("{DNo such goto point.{x\n\r", ch);
			return;
		}

		//Now remove it.
		if (pLoc == location_list)
		{
			location_list = pLoc->next;
			free_loc (pLoc);
		}
		else
		{
			for (prev = location_list; prev != NULL; prev = prev->next)
			{
				if (prev->next == pLoc)
				{
					prev->next = pLoc->next;
					free_loc (pLoc);
				}
			}
		}

		//Done.
		send_to_char ("{DGoto point deleted.{x\n\r", ch);
		return;
	}

	//Get a list.
	else do_locations (ch, "");
}

/*
 * Look up a 'goto point' room.
 */
ROOM_INDEX_DATA * location_lookup (CHAR_DATA * ch, char * argument)
{
	ROOM_INDEX_DATA * pRoom;
	LOCATION * list;

	for (list = location_list; list != NULL; list = list->next)
	{
		if (list->level > get_trust (ch)) continue;

		if (!str_prefix (argument, list->name)
			&& (pRoom = get_room_index (list->vnum)) != NULL)
			return pRoom;
	}

	return NULL;
}

//Save all locations.
void save_locations (void)
{
	LOCATION * pLoc;
	FILE * fp;

	if ((fp = fopen ("locations.txt", "w")) == NULL)
	{
		bug ("Location list not found.", 0);
		return;
	}

	for (pLoc = location_list; pLoc != NULL; pLoc = pLoc->next)
		fprintf (fp, "Loc %s~ %ld %d\n", pLoc->name, pLoc->vnum, pLoc->level);

	fprintf (fp, "End\n");
	fclose (fp);
	return;
}

//Load all locations.
void load_locations (void)
{
	LOCATION * pLoc;
	FILE * fp;
	char * word;
	bool fMatch;

	//Get the file.
	if ((fp = fopen ("locations.txt", "r")) == NULL)
	{
		bug ("Location list not found.", 0);
		return;
	}

    for (;;)
    {
        word = feof (fp) ? "End" : fread_word (fp);
        fMatch = FALSE;

		switch (UPPER(word[0]))
		{
			//Comment.
            case '*':
                fMatch = TRUE;
                fread_to_eol (fp);
                break;

			//Load a location...
            case 'L':
				if (!str_cmp (word, "Loc"))
				{
					pLoc = new_loc ();
					free_string (pLoc->name);
					pLoc->name = fread_string (fp);
					pLoc->vnum = fread_number (fp);
					pLoc->level = fread_number (fp);

					pLoc->next = location_list;
					location_list = pLoc;
					fMatch = TRUE;
					break;
				}
				break;

			//End of list?
            case 'E':
				if (!str_cmp (word, "End"))
                    return;
				break;
		}
	}
	fclose (fp);
}


/*
 * Recycling stuff.
 */
LOCATION * loc_free;

LOCATION * new_loc (void)
{
    static LOCATION loc_zero;
    LOCATION *loc;

    if (loc_free == NULL)
        loc = alloc_perm (sizeof (*loc));
    else
    {
        loc = loc_free;
        loc_free = loc_free->next;
    }

    *loc = loc_zero;
    VALIDATE (loc);
    loc->name = &str_empty[0];
    return loc;
}

void free_loc (LOCATION * loc)
{
    if (!IS_VALID (loc))
        return;

    free_string (loc->name);
    INVALIDATE (loc);

    loc->next = loc_free;
    loc_free = loc;
}