#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include	<stdio.h>
#include	"config.h"
#include	"utils.h"
#include	"structs.h"
#include	"skills.h"
#include "db.h"
#include "functions.h"

/******************************************************************************
	Declaration of global variables.
******************************************************************************/
struct player_index_element *player_table=0;
FILE *home_fp, *bin_fp;			   /* for saving character data */
FILE *player_fp = 0;    			/* file desc of player file  */
int top_of_p_table = -1;    		/* ref to top of table       */
int top_of_p_file = -1;     		/* ref of size of p file     */
int top_idnum = 0;          		/* highest idnum in use      */

struct npc_index_element *mob_proto_index=0;
struct char_data *mob_proto_list=0, *mob_list=0;
int    top_of_mob_protos=0, top_of_mobs=0, top_mob_num=0;

struct object_index_element *obj_proto_index=0;
struct object_data *obj_proto_list=0, *object_list=0;
int    top_of_obj_protos=0, top_obj_num=0;

struct grid_index_element *grid_list=0;
struct zone_data *zone_list=0;
int    top_of_z_table = -1;
 
struct none_room_element *none_rooms=0;
struct room_data *tmp_room;
int    top_of_v_rooms = -1;
int	 top_of_rooms=0;

struct cook_data  *cook_list=0;
int    items_cooking=0;

struct regen_index_element *regen_index=0;
struct regen_data *timed_regen=0;
int    top_of_regen=0, active_regen=0;

/* Global variables for the co-ordinates system */
int  axis_limit,mask,minus,shift;

extern   long  levels[];
extern	char  buf[SMALL_BUFSIZE];
extern	char  buf1[SMALL_BUFSIZE];
extern	char  buf2[SMALL_BUFSIZE];
extern	char  buf3[SMALL_BUFSIZE];
extern   char  loadmobs;
extern   char  loadobjects;
extern   char  initworld;

extern	long  seed;
char *greetings=0;
char *motd=0;

void boot_world(void)
{
	char	*msg;
	int	c=0;

	shift   = (sizeof(short)*8);    /* (was short) How many bits to shift and axis across by */
	axis_limit = _power(shift-1)-1;     /* The maximum number our axis can be */
	printf("Zone co-ordinates must be between %d and %d\n",(axis_limit*-1),axis_limit);

	printf("Loading text files\n");
	file_2_string(GREETINGS, &greetings);
	file_2_string(MOTD, &motd);

	/* Load the objects into memory */
	if(loadobjects == YES) 
		load_objects();

	/* Load the mobiles into memory */
	if(loadmobs == YES) 
		load_mobiles();

	/* Load and check the player files */
	if((msg = load_player_file()) != '\0') {
		printf("Error: %s\n",msg);
		exit(1);
	}

	/* Load the zones into memory */
	for(c=0;!load_zones(c);c++);
	index_zones();

	if(initworld == YES) 
		for(c=0;!init_world(c);c++);

	printf("Auto regen mobs/objects: %d (%d active)\n",top_of_regen,active_regen);

	printf("World initialised\n");
}

/*******************************************************************************
	Load the mobiles.
*******************************************************************************/
void load_mobiles(void)
{
   FILE *fp;
   int size,i;
   char c,*err_msg;
   struct char_data *mob;

   if(!(fp=fopen(MOBILES,"r"))) {
      perror(MOBILES);
      exit(1);
   } else {
      printf("Loading Mobiles: ");
   }

   while((read_string(buf,fp)>0) && *buf != 'F') {
      CREATE(mob,struct char_data,1);
      if((err_msg = file_2_mob(mob,fp))) {
          if(*err_msg == '?') {
              printf("Undefined error\n");
          } else {
              printf("%s\n",err_msg);
          }
          exit(1);
      } else if(err_msg = check_mob(mob)) {
          printf("ERROR: \'%s\' %s (MOB ignored)\n",mob->l_name,err_msg);
          free(mob);
      } else {
          BSET(mob->SPECIALS,NPC);
          mob->next_in_list=mob_proto_list;
          mob_proto_list=mob;
          top_of_mob_protos++;
      }
   }

   close(fp);
   if(!top_of_mob_protos) {
      printf("No MOBILES in the database!\n");
   } else {
      printf("%d mobiles in database\nIndexing mobiles: ",top_of_mob_protos);
      CREATE(mob_proto_index,struct npc_index_element,top_of_mob_protos);
      mob = mob_proto_list;
      for(i=top_of_mob_protos-1;i>=0;i--) {
         mob_proto_index[i].mob = mob;
         mob = mob->next_in_list;
      }
      printf("Done\n");
   }
}

char *file_2_mob(struct char_data *mob, FILE *fp)
{
	struct say_data *tmp_say;
	int	c,b,size;

	if(!_numeric(&mob->IDNUM,buf+1))
		return("Non numeric MOB number");
	if(mob->IDNUM > top_mob_num)
		top_mob_num = mob->IDNUM;

	if((read_string(mob->name,fp)) > MAX_NAM_LEN)
		return("Short name too long");

	if(!read_string(buf,fp))
		return("Reading long name");
	else
		mob->l_name=str_dup(buf);

	if((read_string(mob->title,fp)) > 40)
		return("Title too long");

	if((read_string(buf, fp)) < 0)
		return("Reading description");
	else
		mob->description = str_dup(buf);

	while((read_string(buf,fp)) && *buf == 'E') {
		CREATE(tmp_say,struct say_data,1);
		tmp_say->sentence = str_dup(buf+2);
		tmp_say->next = mob->says;
		mob->says = tmp_say;
	}

	while(*buf == 'F') {
		for(c=1;get_arg(buf1,buf,c);c++) {
         if((b = get_sex_flag(buf1)) > -1) {
            BSET(mob->ANTI_SEX,anti_sex_flag[b].setbit);
         } else if((b = get_align_flag(buf1)) > -1) {
            BSET(mob->ANTI_ALIGN,anti_align_flag[b].setbit);
         } else if((b = get_class_flag(buf1)) > -1) {
            BSET(mob->ANTI_CLASS,anti_class_flag[b].setbit);
			} else if((b = get_mob_flag(buf1)) > -1) {
				BSET(NPC_FLAGS(mob),mob_flag[b].setbit);
         } else if((b = get_mob_class(buf1)) > -1) {
            mob->CLASS = b;
         } else if((b = get_mob_sex(buf1)) > -1) {
				mob->SEX = b;
         } else if((b = get_mob_char(buf1)) > -1) {
				if((!get_arg(buf2,buf,++c)) || !_numeric(&size,buf2))
					printf("Invalid parameter %s for %s\n",buf2,buf1);
				else
					mob->data[b] = size;
			} else if((b = get_mob_long(buf1)) > -1) {
				if((!get_arg(buf2,buf,++c)) || !_numeric(&size,buf2))
               printf("Invalid parameter %s for %s\n",buf2,buf1);
            else
               mob->l_data[b] = size;
			} else {
				printf("Invalid flag: %s\n",buf1);
			}
		}
		read_string(buf,fp);
	}
	return(0);
}
/*******************************************************************************
	Player file, handling routines.
*******************************************************************************/
char *load_player_file(void)
{
	int size,recs,c;
	int nr = -1, i;
	struct char_save_data dummy;
	struct object_data *tmp_object;

	if(!(player_fp=fopen(PLAYER_FILE,"r+b"))) {
		if(!(player_fp=fopen(PLAYER_FILE, "w+b"))) {
			perror("Error opening player file");
			exit(1);
		}
	} else {
		printf("Loading player file: ");
	}

	fseek(player_fp, 0L, SEEK_END);
	size=ftell(player_fp);
	rewind(player_fp);
	if(size % sizeof(struct char_save_data))
		printf("WARNING: player file is probably corrupted!\n");
	recs=size / sizeof(struct char_save_data);
	if(recs) {
		printf("%d player(s) in database.\n",recs);
		CREATE(player_table, struct player_index_element, recs);
	} else {
		printf("Zero entries in player file!\n");
		top_of_p_file = top_of_p_table = -1;
		return('\0');
	}

   for (; !feof(player_fp); ) {
      fread(&dummy, sizeof(struct char_save_data), 1, player_fp);
      if (!feof(player_fp)) {
     		nr++;
			player_table[nr].name=str_dup(dummy.name);
			player_table[nr].level=dummy.LEVEL;
      	top_idnum = MAX(top_idnum, dummy.IDNUM);

			/* We need to check the objects carried and worn by the character
				to make sure that an object isn't in excess of it's max allowed */
   		if(!(home_fp=fopen(dummy.home,"r"))) {
      		perror(dummy.home);
		   } else {
				/* Seek for any objects */
				while(read_string(buf, home_fp) > 0) {
					/* Do we have an object? */
					if(*buf != '\0') {
						if((!get_arg(buf1, buf, 1)) || !_numeric(&size, buf1)) {
							sprintf(buf,"Invalid file format in %s\n", dummy.home);
							return(buf);
						}

						/* We have an object number */
						if(tmp_object=get_proto_object(size))
							tmp_object->OBJ_COUNT++;
					}
				}
			}
      }
   }
   top_of_p_file = top_of_p_table = nr;
	return('\0');
}

void kill_char(struct char_data *ch, char *msg)
{
	extern struct char_data *mob_data;
	extern struct descriptor_data *desc_list;
	struct descriptor_data *desc, *desc_next;
	struct object_data *obj, *obj_next;
	struct char_data *mob, *mob_next;
	int c;

	/* check if we are a mob or a player */
	if(!ch->desc) {
		/* Remove mobile from the player table */
		if(ch == mob_list) {
			mob_list=ch->next_in_list;
		} else {
			for(mob=mob_list;(mob->next_in_list != ch) && mob;mob=mob->next_in_list);
			mob->next_in_list=ch->next_in_list;
		}

		/* Remove mob from list of types */
		if(ch == ch->proto_mob->next_of_type) {
			ch->proto_mob->next_of_type = ch->next_of_type;
		} else {
			for(mob=ch->proto_mob->next_of_type;(mob->next_of_type != ch) && mob;mob=mob->next_of_type);
			mob->next_of_type=ch->next_of_type;
		}
	} else {
		if (desc_list == ch->desc) {
			desc_list=ch->desc->next_in_list;
		} else {
			for(desc=desc_list;(desc->next_in_list != ch->desc) && desc;desc=desc->next_in_list);
			desc->next_in_list=ch->desc->next_in_list;
		}
		if(ch->desc->output_buf) {
			free(ch->desc->output_buf);
			ch->desc->output_buf = 0;
		}
		free(ch->desc);
		ch->desc = 0;
	}

	/* Exit the room */
	char_from_room(ch,msg);

	/* Remove the objects (carried and worn) */
	for(c=0;c<MAX_WEAR;c++)
		delete_object(ch->wearing[c], FROM_GAME);

	for(obj=ch->carrying;obj;obj=obj_next) {
		obj_next=obj->next_carried;
		delete_object(obj, FROM_GAME);
	}

	/* Check if I was being followed or following */
	if(ch->leader)
		dump_leader(ch);

	if(ch->followers)
		disband_followers(ch);

	free(ch);
}

void store_2_char(struct char_save_data *st, struct char_data *ch)
{
	struct object_data *tmp_obj,*parent;
	int	c,where,idnum=NOWHERE;

	/* Copy all the characters attributes and items from disk */
	strcpy(ch->name, st->name);
	strcpy(ch->home, st->home);
	strcpy(ch->bin, st->bin);
	strcpy(ch->pwd, st->pwd);
	strcpy(ch->title,st->title);

	for(c=0;c<MAX_DATA;c++)
		ch->data[c] = st->data[c];

	for(c=0;c<MAX_L_DATA;c++)
		ch->l_data[c] = st->l_data[c];
// CRTH - Load skills.
	for(c=0;c < st->NO_SKILLS;c++)
	{
		ch->skills[c] = st->skills[c];
		ch->skills[c+1].useby = LAST_SKL; 
	}


//	IN_ROOM(ch) = 0x0;


	/* Open the data files */
	home_fp = fopen(ch->home,"r");
	bin_fp = fopen(ch->bin,"r");

	/* Load the objects I was wearing */
	while((read_string(buf,home_fp)) > 0) {
		if(*buf == 'O') {
			/* Get the objects ID number */
			if((!get_arg(buf1,buf,1)) || !_numeric(&idnum,buf1))
				break;
			/* Get where the object was being carried/worn/etc */
			if(!get_arg(buf2,buf,2))
				break;
			/* Get the where worn / carried by flag */
			if((!get_arg(buf3,buf,3)) || !_numeric(&where,buf3))
				break;
			/* Try and create an object from the information */
			if(!(tmp_obj = clone_object(idnum,OLD_OBJECT)))
				break;
			if(!(fread(&tmp_obj->data,sizeof(int),MAX_DATA,bin_fp)))
				break;
			/* See if this object has an expiry date */
			if(tmp_obj->LIFE) {
				slow_cook_item((union any)tmp_obj, IS_OBJECT);
			}
			/* Sort out where to put it */
			switch(*buf2) {
				case 'C':
					tmp_obj->carried_by = ch;
					tmp_obj->next_carried = ch->carrying;
					ch->carrying = tmp_obj;
					parent=tmp_obj;
					break;
				case 'V':
					tmp_obj->contained_in = parent;
					tmp_obj->next_contained = parent->contains;
					parent->contains = tmp_obj;
					parent=tmp_obj;
					break;
				case 'I':
					tmp_obj->contained_in = parent;
					tmp_obj->next_contained = parent->contains;
					parent->contains = tmp_obj;
					break;
				case 'W':
					ch->wearing[where] = tmp_obj;
					tmp_obj->carried_by = ch;
					parent=tmp_obj;
					break;
				default :
					break;
			};
		}
		idnum = NOWHERE;
	}
	
	/* Report any errors (in typical DOS style?) */
	if(idnum != NOWHERE) {
		printf("Error: %s has a corrupted save file (will be fixed automatically)\n",ch->name);
	}

	/* Close the files */
	close(home_fp);
	close(bin_fp);

}

/* create a new entry in the in-memory index table for the player file */
int create_char_entry(char *name)
{
   if (top_of_p_table == -1) {
		CREATE(player_table,struct player_index_element,1);
      top_of_p_table = 0;
   } else {
		RESIZE(player_table,struct player_index_element,(++top_of_p_table +1));
	}
	player_table[top_of_p_table].name=str_dup(name);
   return (top_of_p_table);
}

struct room_data *create_void_room(int x,int y,int z,int room)
{
	int i,c,t;
	struct room_data *new_room;

	/* Create the room, and set the flags */
	top_of_v_rooms++;
	RESIZE(none_rooms,struct none_room_element,(top_of_v_rooms + 1));
	CREATE(new_room,struct room_data,1);
	BSET(new_room->flags,VOID_ROOM);

	none_rooms[top_of_v_rooms].room = new_room;
	NONE_X(top_of_v_rooms) = x;
	NONE_Y(top_of_v_rooms) = y;
	NONE_Z(top_of_v_rooms) = z;
	NONE_R(top_of_v_rooms) = room;
	NON_ZZ(top_of_v_rooms) = -1;
	//none_rooms[top_of_v_rooms].room->IDNUM = top_of_v_rooms;

	/******************************************************************************
		Index into ascending order the none existant rooms:-
		Order of precedence: X, Y, Z, ROOM
		Description of how index works...
		NONE_X(i) != NONE_X(c) then index on X else,
		NONE_Y(i) != NONE_Y(c) then index on Y else, etc ...
	******************************************************************************/

	/**************************************************************************
		Search for the first item in the list that is greater than the new one
	**************************************************************************/
	for(c=0,i=top_of_v_rooms;c<top_of_v_rooms;c++) {
		if(((NONE_X(i) != NONE_X(c)) ? (NONE_X(i) - NONE_X(c)) :
         ((NONE_Y(i) != NONE_Y(c)) ? (NONE_Y(i) - NONE_Y(c)) :
         ((NONE_Z(i) != NONE_Z(c)) ? (NONE_Z(i) - NONE_Z(c)) :
         ((NONE_R(i) != NONE_R(c)) ? (NONE_R(i) - NONE_R(c)) : 0 )))) < 0)
			break;
	}

	/**************************************************
		Shuffle the list to squeeze a new room in...
	**************************************************/
	while(i > c) {
		none_rooms[i].room = none_rooms[i-1].room;
		none_rooms[i].room->IDNUM = i;
		i--;
	}

	/*******************************
		Insert the new room...
	*******************************/
	none_rooms[c].room = new_room;
	none_rooms[c].room->IDNUM = c;

	/**************************
		Return the new room...
	**************************/
	return(new_room);
}

int delete_void_room(struct room_data *room)
{
	int c,i;

	/* Is it flagged as a void room? */
	if(!BTST(room->flags,VOID_ROOM))
		return(0);

	for(c=room->IDNUM;c<top_of_v_rooms;c++) {
		none_rooms[c].room = none_rooms[c+1].room;
		none_rooms[c].room->IDNUM = c;
	}

	free(room);
	top_of_v_rooms--;
}

int create_zone_entry(void)
{
   if(top_of_z_table == -1) {
		CREATE(zone_list,struct zone_data,1);
      top_of_z_table = 0;
   } else {
		RESIZE(zone_list,struct zone_data,(++top_of_z_table +1));
	}

   return (top_of_z_table);
}

/* initialize a new character only if class is set */
int init_char(struct char_data *ch)
{
	/* Set the home and save file paths */
// Matt, I have moved these lines to con_reroll in interpreter.c as they
// were causing me a few problems when i was implementing the 
// 'are yo happy with your stats?' sequence.
//	sprintf(ch->home,"%s%c/%s",PLAYER_PATH,(*ch->name | 32),ch->name);
//	sprintf(ch->bin,"%s.bin",ch->home);

	ch->LOAD_X = START_X;
	ch->LOAD_Y = START_Y;
	ch->LOAD_Z = START_Z;

	ch->STR_ADD = 0;
	ch->ARMOR = 100;
	ch->GOLD = 0;
	ch->BANK = 0;
	ch->EXP = 0;

	/* We start off as good */
	ch->ALIGN = 1000;

	ch->AGE = 17;

	/* Do abilities now */
	roll_char(ch);

	ch->MAX_MANA = ch->INT + ch->WIS;
	ch->MAX_HIT = ch->STR + ch->CON;
	ch ->MAX_MOVE = ch->STR + ch->CON + ch->DEX;
	ch->MANA = ch->MAX_MANA;
	ch->HIT = ch->MAX_HIT;
	ch->MOVE = ch->MAX_MOVE;

	ch->WEIGHT=(_random(((ch->CLASS+ch->SEX)*4),5)+100);
	ch->HEIGHT=(_random((ch->CLASS*5),ch->SEX)+_random((ch->WEIGHT/30),ch->SEX)+148);

	ch->TOHIT = 0;
	ch->TODAM = 0;

	ch->POSITION  = POS_STANDING;		// CRTH
	ch->STATE = STAT_OK;					// CRTH
	ch->DRUNK = 0;							// CRTH
	ch->POISEN = 0;						// CRTH
	ch->DISEASE = 0;						// CRTH
	ch->HUNGER = 0;						// CRTH
	ch->THIRST = 0;						// CRTH	
	IN_ROOM(ch) = 0x0;

   /* *** if this is our first player --- he be God *** */
	if(top_of_p_table < 0) {
		ch->LOAD_R = 16;
		BSET(ch->PREFS,SAVE_LOC);
		ch->LEVEL = IMP;
		ch->EXP = levels[IMP];
		ch->INVIS = IMP;
		sprintf(ch->title,"The Implementor"); // CRTH
	} else {
		ch->LOAD_R = START_ROOM;
		ch->LEVEL = 1;
		ch->INVIS = 0;
		// CRTH - same as the home path stuff.
//		obj_2_char(clone_object(10,NEW_OBJECT),ch);
//		obj_2_char(clone_object(11,NEW_OBJECT),ch);
		sprintf(ch->title,"the newbie"); // CRTH
	}

	update_char(ch);

	if(DEBUG_CHAR)
		printf("%s Has just been born.\n",ch->name);
	return(0);
}

/*******************************************************
	Routines related to saving and loading characters.
*******************************************************/
/* Load a char, TRUE if loaded, FALSE if not */
int     load_char(char *name, struct char_save_data *char_element)
{
   int  player_i;
   int  find_name(char *name);

   if ((player_i = find_name(name)) >= 0) {
      fseek(player_fp, (player_i * sizeof(struct char_save_data)),SEEK_SET);
      fread(char_element, sizeof(struct char_save_data), 1, player_fp);
		return(player_i);
   } else
      return(-1);
}


/* locate entry in p_table with entry->name == name. -1 mrks failed search */
int     find_name(char *name)
{
   int  i;

   for(i=0;i<=top_of_p_table;i++) {
      if (!str_cmp(player_table[i].name,name))
         return(i);
   }
   return(-1);
}

void save_char(struct char_data *ch)
{
	struct char_save_data sd;
	struct object_data *obj, *next_obj;
	int c,i;

	strcpy(sd.name, ch->name);
	strcpy(sd.home, ch->home);
	strcpy(sd.bin, ch->bin);
	strcpy(sd.pwd, ch->pwd);
	strcpy(sd.host,ch->host);
	strcpy(sd.title,ch->title);


	/* Save the characters stats */
	for(c=0;c<MAX_DATA;c++)
		sd.data[c] = ch->data[c];

	for(c=0;c<MAX_L_DATA;c++)
		sd.l_data[c] = ch->l_data[c];
	
	// CRTH - Save skills.
	for(c = 0;c<ch->NO_SKILLS;c++)
		sd.skills[c] = ch->skills[c];

	/* Open and zero the save files */
	if(!(home_fp = fopen(ch->home,"w+")) || !(bin_fp = fopen(ch->bin,"w+"))) {
		printf("%s -- %s\n",ch->home, ch->bin);
		sprintf(buf,"[%s] Opening home files for write access",ch->name);
		perror(buf);
		return;
	}

	/* Save the items I am wearing */
	for(c=0;c<MAX_WEAR;c++) {
		if(ch->wearing[c]) {
			save_object(ch->wearing[c],'W',c);
		}
	}

	/* Save the objects I was carrying < MAX_CARRY */
	if(ch->carrying)
		save_object(ch->carrying,'C',NOWHERE);

	/* Close the files */
	close(home_fp);
	close(bin_fp);

	fseek(player_fp, ch->IDNUM * sizeof(struct char_save_data), SEEK_SET);
	fwrite(&sd, sizeof(struct char_save_data), 1, player_fp);

	// CRTH - If they have SAVE_LOC_NUM switched on, then save there location.
	if(BTST(ch->PREFS,SAVE_LOC))
	{
		ch->LOAD_X = GET_X(ch); 
		ch->LOAD_Y = GET_Y(ch);
		ch->LOAD_Z = GET_Z(ch);
		ch->LOAD_R = ROOM_NUM(ch);
	}

	if(DEBUG_CHAR)
		printf("%s has just saved.\n",ch->name);
}

void	save_object(struct object_data *obj, char CWI, int where)
{

	/* printf("Saving: %s %c %d\n",obj->name,CWI,where); */

	/* First of all save this object */
	sprintf(buf,"O %d %c %d~\n",obj->IDNUM,CWI,where);
	fwrite(&buf, strlen(buf), 1, home_fp);

	/* Save the objects vital statistics (to make every object unique) */
	fwrite(&obj->data,sizeof(int),MAX_DATA,bin_fp);

	/* Move on to the beginning of the next field ;-{ */
   fseek(home_fp,0L,SEEK_END);
   fseek(bin_fp,0L,SEEK_END);

	if(obj->next_contained)
		save_object(obj->next_contained,'I',where);
	if(obj->contains)
		save_object(obj->contains,'V',obj->IDNUM);
	if(obj->next_carried)
		save_object(obj->next_carried,'C',NOWHERE);
}

/******************************************************************************/
/******************************************************************************/
/* read contents of a text file, and place in buf */
int     file_2_string(char *name, char **buf)
{
   FILE  *fl;
	char	tmp[100];
	int	size;

	if(*buf)
		free(*buf);
   *buf = '\0';

   if (!(fl = fopen(name, "r"))) {
      sprintf(tmp, "Error reading %s", name);
      perror(tmp);
      return(-1);
   }

	fseek(fl,0L,SEEK_END);
	size=ftell(fl);
	rewind(fl);

	CREATE(*buf,char,size+1);

	fread(*buf,size,1,fl);
	close(fl);
	*(buf+size)='\0';

	return(size);
}
/**************************************************************/
/**************************************************************/
void	load_objects()
{
   FILE *fp;
   struct object_data *tmp_object, *object;
   int size,i;
   char c,*err_msg;

   if(!(fp=fopen(OBJECTS,"r"))) {
      perror(OBJECTS);
      exit(1);
   } else {
      printf("Loading objects: ");
   }

   while((read_string(buf,fp)>0) && *buf != 'F') {
      CREATE(tmp_object,struct object_data,1);
      memset(tmp_object, 0, sizeof(struct object_data));
      if((err_msg = file_2_object(tmp_object,fp))) {
         if(*err_msg == '?') {
            printf("Undefined error\n");
         } else {
            printf("ERROR: %s\n",err_msg);
         }
	 		exit(1);
      } else {
         tmp_object->next_in_list=obj_proto_list;
         obj_proto_list=tmp_object;
         top_of_obj_protos++;
      }
   }

   close(fp);
   if(!top_of_obj_protos) {
      printf("No OBJECTS in the database!\n");
   } else {
      printf("%d objects in database\nIndexing objects: ",top_of_obj_protos);
      CREATE(obj_proto_index,struct object_index_element,top_of_obj_protos);
      object=obj_proto_list;
      for(i=top_of_obj_protos-1;i>=0;i--) {
         obj_proto_index[i].object=object;
         object=object->next_in_list;
      }
      printf("Done\n");
   }
}

char	*file_2_object(struct object_data *object, FILE *fp)
{
	struct affect_data *tmp_affect;
	int size,c,i,f;

	object->in_room = 0;
	
	/* Read the object number */
	if(!_numeric(&object->IDNUM,buf+1))
		return("Non numeric room number");
	if(object->IDNUM > top_obj_num)
		top_obj_num = object->IDNUM;
	if(object->IDNUM < top_obj_num)
		return("Non ascending order in object file!");

	/* Read the object name */
	if((size = read_string(buf,fp))<1)
		return("?");
	object->name=str_dup(buf);

	/* Read the still description */
	if((size = read_string(buf,fp))<0)
		return("?");
	object->still_desc=str_dup(buf);

	/* Read the examine description */
	if((size = read_string(buf,fp))<0)
		return("?");
	object->exam_desc=str_dup(buf);

	/* Read in the aliases */
	if((size = read_string(buf,fp))<0)
		return("?");
	object->aliases=str_dup(buf);

	/* Read in the weapon stats, weight, value and max allowed */
	read_string(buf,fp);
	for(c=0,i=DIE_NUM;c<6;c++,i++) {
		get_arg(buf1,buf,c);
		if(!_numeric(&object->data[i],buf1))
			return("Non numeric stats");
	}
	for(;(read_string(buf,fp)) && !shrt_cmp(buf, "F ") && !feof(fp);) {
		for(c=1;get_arg(buf1,buf,c);c++) {
			if((size = chk_obj_type(object,c))) {
				if(size < 0)
					return(buf);
				c = size;			/* Was an object type */
			} else if((size = chk_obj_worn(object,c))) {
				if(size < 0)
					return(buf);
				c = size;			/* Was wearable */
			} else if((size = get_sex_flag(buf1)) > -1) {
				BSET(object->SEX,anti_sex_flag[size].setbit);
			} else if((size = get_align_flag(buf1)) > -1) {
				BSET(object->ALIGN,anti_align_flag[size].setbit);
			} else if((size = get_class_flag(buf1)) > -1) {
				BSET(object->CLASS,anti_class_flag[size].setbit);
			} else {
					printf("ERROR: [%s] invalid flag:- %s\n",object->name,buf1);
			}
		}
	}
	/* Read in the affect modifiers (if any) */
	while(*buf != 'S') {
		for(c=1;get_arg(buf1,buf,c);c+=2) {
			if((get_arg(buf2,buf,c+1)) && _numeric(&size,buf2)) {
				for(i=0;*data_list[i].name != '\n';i++) {
					if(!shrt_cmp(buf1,data_list[i].name)) {
						CREATE(tmp_affect,struct affect_data,1);
						tmp_affect->affect=data_list[i].number;
						tmp_affect->value=size;
						tmp_affect->next=object->affect;
						object->affect=tmp_affect;
						break;
					}
				}
				if(*data_list[i].name == '\n')
					printf("Invalid affect name: %s\n",buf1);
			} else {
				printf("Invalid affect format: %s %s\n",buf1,buf2);
			}
		}
		read_string(buf,fp);
	}
	return(0);
}
/**************************************************************/
/**************************************************************/
int	load_zones(int zone)
{
	FILE *fp;
	int c,room;
	char *err_msg;

	sprintf(buf,"%s%d/rooms",PATH,zone);
	if(!(fp=fopen(buf,"r"))) {
		return(1);
	}
	printf("Loading: zone %3d\tco-ordinates: ",zone);
   create_zone_entry();

   /* Read the co-ordinates of this zone */
	read_string(buf,fp);
	if(!get_arg(buf1,buf,1) || !get_arg(buf2,buf,2) || !get_arg(buf3,buf,3)) {
		printf("Invalid number of co-ordinates!\n");
 		exit(1);
	} else if(!_numeric(&ZONE_X(zone),buf1) || !_numeric(&ZONE_Y(zone),buf2) || !_numeric(&ZONE_Z(zone),buf3)) {
		printf("Not numeric!\n");
		exit(1);
	} else {
		for(c=0;c<top_of_z_table;c++) {
			if((ZONE_X(zone) == ZONE_X(c)) && (ZONE_Y(zone) == ZONE_Y(c)) && (ZONE_Z(zone) == ZONE_Z(c))) {
				printf("Duplicated!!\n");
				exit(1);
			}
		}
		printf("%d, %d, %d",ZONE_X(zone),ZONE_Y(zone),ZONE_Z(zone));
	}

	/* Read in the reset time for this zone */
	read_string(buf,fp);
	if((!get_arg(buf1,buf,1)) || !_numeric(&zone_list[zone].reset,buf1)) {
		printf("Invalid reset time!\n");
		exit(1);
	}

	/* Read in the defined rooms from disk */
	while((read_string(buf,fp) > 0) && *buf != 'F') {
		if(!_numeric(&room,buf+1)) {
			printf("Non numeric room number\n");
			exit(1);
		} else if(room > (ZONE_SIZE-1) || room < 0) {
			printf("Invalid room number: %d\n",room);
			exit(1);
		} else {
			CREATE(ZONE_ROOM(zone,room),struct room_data,1);
			if((err_msg = file_2_room(ZONE_ROOM(zone,room),zone,fp))) {
				printf("ERROR: %s (current line: %s)\n",err_msg,buf);
				exit(1);
			}
			ZONE_ROOM(zone,room)->ROOM = room;
			zone_list[zone].num_rooms++;
			top_of_rooms++;
		}
	}
	printf("\t[%d rooms]\n",zone_list[zone].num_rooms);
	close(fp);
	return(0);
}

char	*file_2_room(struct room_data *room,int zone,FILE *fp)
{
	struct extra_desc *tmp_extra;
	char  *err_msg;
	int   size,c,i,found;

	room->ZONE = zone;

	/* Read the room name */
	if((size = read_string(buf, fp)) < 1)
		return("?");
	room->name=str_dup(buf);

	/* Read the room description */
	if((size = read_string(buf, fp)) < 1)
		return("?");
	room->description=str_dup(buf);
	
	/* Set the room x,y,z,n,zone */
	for(c=0;c<3;c++)
		room->xyz[c] = zone_list[zone].xyz[c];
	room->ZONE = zone;

	/* Set the room level */
	room->MIN_LEVEL=1;				/* All rooms are set to this unless */
	room->MAX_LEVEL=IMP;				/* specified in the flags */

	/* Read in the room flags */
	if(!(err_msg=get_room_flags(room,fp)))
		return(err_msg);

	/* Read in the extra descriptions (if any) */
	while(*buf == '$') {
		get_arg(buf1,buf,-1);		/* get all after leading E */
		CREATE(tmp_extra,struct extra_desc,1);
		tmp_extra->name=str_dup(buf1);
		read_string(buf,fp);
		tmp_extra->desc=str_dup(buf);
		tmp_extra->next=room->extra;
		room->extra=tmp_extra;
		read_string(buf,fp);
	}	

	/* Read in the exits */
	while(*buf == 'E') {
		file_2_exit_flags(room,fp);
	}
	return(0);
}

char *get_room_flags(struct room_data *room, FILE *fp)
{
	int c,i,found,size;

	/* Read the flags in (if any) */
	while((read_string(buf,fp)) && *buf == 'F') {
		for(c=1;get_arg(buf1,buf,c);c++) {
      	if((size = get_sex_flag(buf1)) > -1) {
         	BSET(room->SEX,anti_sex_flag[size].setbit);
      	} else if((size = get_align_flag(buf1)) > -1) {
         	BSET(room->ALIGN,anti_align_flag[size].setbit);
      	} else if((size = get_class_flag(buf1)) > -1) {
         	BSET(room->CLASS,anti_class_flag[size].setbit);
      	} else {
				/* Check the room flags IS_DARK, etc */
      		for(i=0,found=0;*room_flag[i].name != '\n' && !found;i++) {
					if(!str_cmp(buf1,room_flag[i].name)) {
            		if((room_flag[i].setbit & 3) == 3) {
            			if(!get_arg(buf1,buf,++c)) {
               			return(buf1);
            			} else if(!_numeric(&size,buf1)) {
               			return(buf1);
            			}
							found++;
							switch(room_flag[i].setbit) {
							case 0x03:
							case 0x07:
								if(size < room->MIN_LEVEL || size > room->MAX_LEVEL) {
              		 			return(buf1);
            				} else if(room_flag[i].setbit == 3) {
               				room->MIN_LEVEL=size;
            				} else {
               				room->MAX_LEVEL=size;
								}
								break;
							case 0x0b:
								room->OBJ_SPECIAL=size;
								break;
							default:
								room->MOVE_COST=size;
								break;
							}
						} else {
            			BSET(room->flags,room_flag[i].setbit);
							found++;
						}
					}
				}
				if(!found)
					printf("Invalid room flag: %s\n",buf1);
			}
		}
	}
}

/* Exit data is strored in buf */
int	file_2_exit_flags(struct room_data *room,FILE *fp)
{
	int c,i,size;
	struct exit_data *tmp_exit;

	CREATE(tmp_exit,struct exit_data,1);

	for(c=1;get_arg(buf1,buf,c);c++) {	
		for(i=0;*exit_flag[i].flag != '\n';i++) {
			if(!str_cmp(buf1,exit_flag[i].flag)) {
				if(BTST(exit_flag[i].setbit,(LOCKED+OPEN+CLOSED))) {
					if(!BTST(tmp_exit->status,(LOCKED+OPEN+CLOSED))) {
						BSET(tmp_exit->status,exit_flag[i].setbit);
						if(exit_flag[i].setbit == LOCKED) {  /* We need a key number */
							if((!get_arg(buf1,buf,++c)) || !_numeric(&tmp_exit->key,buf1)) {
								printf("LOCKED flag has an invalid arguement\n");
							}
						}
					} else {
						printf("Exit status multiply defined (%s ignored)\n",buf1);
					}
				} else if(exit_flag[i].setbit > -1) {
					BSET(tmp_exit->status,exit_flag[i].setbit);
				} else {
					printf("Invalid exit flag: %s\n",buf1);
				}
				break;
			}
		}
		if(*exit_flag[i].flag == '\n') {
			for(i=0;i<10;i++) {
				if(!shrt_cmp(buf1,exit_list[i].name)) {
					if(room->exits[i]) {
						printf("Duplicated exit %s (Ignored)\n",exit_list[i].name);
						free(tmp_exit);
						return(0);
					} else {
						room->exits[i]=tmp_exit;
					}
					break;
				}
			}
			if(i == 10) {
				printf("Invalid exit parameter: %s\n",buf1);
			}
		}
	}

	/* Read the 'look at' description (if there) */
	read_string(buf,fp);
	if(*buf == 'D') {
		tmp_exit->desc=str_dup(buf+2);
		read_string(buf,fp);
	}

	/* Read the aliases in (if any) */
	if(*buf == 'A') {
		tmp_exit->alias=str_dup(buf+2);
		read_string(buf,fp);
	}
}

/***********************************************************/
/* Initialise a zone...                                    */
/* This means loading all the objects and mobiles for that */
/* zone as specified in the 'init' file.                   */
/*                                                         */ 
/* NOTE:                                                   */
/* If an object has reached its maximum count within the   */
/* world it will not be created!                           */
/***********************************************************/
int	init_world(int zone)
{
	FILE	 *fp;
	int	 room_num,object_num,reg_num,reg_time,ok,c;
	struct object_data *object=0;
	struct char_data *mob=0;
	struct room_data *room=0;

	sprintf(buf,"%s%d/init",PATH,zone);
	if(!(fp=fopen(buf,"r"))) {
		return(1);
	}
	printf("Initialising zone %d\n",zone);

	while((read_string(buf,fp)) && *buf != 'F') {
		if(!get_arg(buf1,buf,1) || !get_arg(buf2,buf,2) || !get_arg(buf3,buf,3)) {
			printf("ERROR: Invalid arguements in init file!\n");
			exit(1);
		}

		/* See if we have valid arguements */
		if(!_numeric(&object_num,buf1) || !_numeric(&room_num,buf2) || !_numeric(&reg_time,buf3)) {
			printf("ERROR: numeric arguement expected in init file\n");
			exit(1);
		} else if((room=get_room(ZONE_X(zone),ZONE_Y(zone),ZONE_Z(zone),room_num)) == 0) {
			printf("ERROR: room does not exist in init file\n");
			exit(1);
		}

		/* Flag that no object was created */
		ok = FALSE;

		/* Move the mob or object to the room */
		switch(*buf) {
		case 'O':
  			if(object=clone_object(object_num,NEW_OBJECT)) {
				ok = TRUE;
				obj_2_room(object,room);
			}
			break;
		case 'M':
         if(mob=clone_mob(object_num)) {
				ok = TRUE;
				/* Put it in the room */
           	mob->in_room = room;
           	mob->next_in_room=room->people;
           	room->people=mob;
			}
			break;
		default:
			/* Perhaps an error message? */
			exit(1);
		}

		/* Allocate space for our regeneration information */
		if(reg_time) {
			reg_num = top_of_regen++;
			RESIZE(regen_index,struct regen_index_element,top_of_regen);
			CREATE(regen_index[reg_num].item,struct regen_data,1);

			regen_index[reg_num].item->reg_num = reg_num;
			regen_index[reg_num].item->time = reg_time;
			regen_index[reg_num].item->X = ZONE_X(zone);
			regen_index[reg_num].item->Y = ZONE_Y(zone);
			regen_index[reg_num].item->Z = ZONE_Z(zone);
			regen_index[reg_num].item->ROOM = room_num;
			regen_index[reg_num].item->data.ro = 0;

			switch(*buf) {
			case 'O':
				if(ok == TRUE) {
					regen_index[reg_num].item->data.ob = object;
					object->REGEN = reg_num;
				}
				regen_index[reg_num].item->type = IS_OBJECT;
				regen_index[reg_num].item->proto.ob = get_proto_object(object_num);
				break;
			case 'M':
				if(ok == TRUE) {
					regen_index[reg_num].item->data.ch = mob;
					mob->REGEN=reg_num;
				}
				regen_index[reg_num].item->type = IS_CHAR;
				regen_index[reg_num].item->proto.ch = get_proto_mob(object_num);
// CRTH - With out the next line the REGEN mob would only REGEN once.
				regen_index[reg_num].item->proto.ch->REGEN = reg_num;	
			}

			if(ok == TRUE) {
				active_regen++;
			} else {
				/* Put it on the regen list */
				activate_regen(reg_num);
			}

		}

		for(c=0;get_arg(buf1,buf,c+4) && c < MAX_OBJ_REGEN;c++) {
			if(!_numeric(&object_num,buf1)) {
				printf("Non numeric value found in init file: %s\n",buf);
			} else if(ok == TRUE) {
				switch(*buf) {
				case 'O':
					obj_2_obj(clone_object(object_num,NEW_OBJECT),object);
					break;
				case 'M':
					obj_2_char(clone_object(object_num,NEW_OBJECT),mob);
				}
			}
			if(reg_time)
				regen_index[reg_num].item->obj_regen[c] = object_num;
		}
		if(mob)
			do_mob_wield(mob);
	}
	close(fp);
	return(0);
}

struct char_data *clone_mob(int mob_num)
{
	struct char_data *mob, *source;
	int c;

	if(!(source = get_proto_mob(mob_num)))
		return(0);
	CREATE(mob, struct char_data, 1);
	memset(mob, 0, sizeof(struct char_data));

	strcpy(mob->name,source->name);
	strcpy(mob->title,source->title);
	mob->description = source->description;
	mob->l_name = source->l_name;
	mob->says = source->says;
	mob->desc='\0';
	*mob->pwd='\0';
	*mob->host='\0';

	for(c=0;c<MAX_DATA;c++)
		mob->data[c] = source->data[c];

	for(c=0;c<MAX_L_DATA;c++)
		mob->l_data[c] = source->l_data[c];

	init_mob(mob);

	/* Put the mob on the mob_list */
	mob->next_in_list=mob_list;
	mob_list=mob;
	/* Get the proto-type to point to this mob */
	mob->next_of_type=source->next_of_type;
	source->next_of_type=mob;
	/* The mob needs a pointer to the proto */
	mob->proto_mob=source;

	return(mob);
}

/* Read a '~' terminated string from a file */
int	read_string(char *target, FILE *fp)
{
	int i=0;
	char c;

	/* Get rid of leading carriage returns (spaces are ok) */
	while((c=fgetc(fp)) == '\n');
	if(feof(fp))
		return(-1);

	*target='\0';
	while(c != '~') {
		*target++ = c;

		/* Move on and check our buffer for overflow */
		if(i++ > SMALL_BUFSIZE) {
			printf("String too big!\n");
			return(-1);
		}

		/* Get the next character */
		if((c=fgetc(fp)) == EOF)
			return(-1);
	}
	/* Terminate the string */
	*target='\0';

	/* return the number of characters */
	return(i+1);
}

char	*check_mob(struct char_data *mob)
{
   /* Check for the basics in creating a mob */
   if(!mob->LEVEL)
      return("No LEVEL defined");
   else if(!mob->SEX)
      return("No SEX defined");
   else if(!mob->CLASS)
      return("No CLASS defined");
	return(0);
}

int	init_mob(struct char_data *mob)
{
	/* Build A mob */
	if(!mob->MAX_MANA)
		mob->MANA=mob->MAX_MANA=_random(mob->LEVEL,20)+10;
	if(!mob->MAX_HIT)
		mob->HIT=mob->MAX_HIT=_random(mob->LEVEL,(20+mob->CLASS))+10;
	if(!mob->MAX_MOVE)
		mob->MOVE=mob->MAX_MOVE=_random(mob->LEVEL,(25+(5-mob->CLASS)))+10;
	if(!mob->WEIGHT)
   	mob->WEIGHT=(_random(((mob->CLASS+mob->SEX)*4),5)+100);
	if(!mob->HEIGHT)
  		mob->HEIGHT=(_random((mob->CLASS*5),mob->SEX)+_random((mob->WEIGHT/30),mob->SEX)+148);

	/* Check the mobs attributes */
	if(!mob->STR)
//		mob->STR = (mob->LEVEL/3)+13;
		mob->STR = ((rand() % 18)+1) + mob->LEVEL / 18;
	if(!mob->DEX)
//		mob->DEX = (mob->LEVEL/3)+13;
		mob->DEX = ((rand() % 18)+1) + mob->LEVEL / 18;
	if(!mob->CON)
//		mob->CON = (mob->LEVEL/3)+13;
		mob->CON = ((rand() % 18)+1) + mob->LEVEL / 18;
	if(!mob->INT)
//		mob->INT = (mob->LEVEL/3)+13;
		mob->INT = ((rand() % 18)+1) + mob->LEVEL / 18;
	if(!mob->WIS)
//		mob->WIS = (mob->LEVEL/3)+13;
		mob->WIS = ((rand() % 18)+1) + mob->LEVEL / 18;
	if(!mob->POSITION)
		mob->POSITION=POS_STANDING;
	if(!mob->STATE)
		mob->STATE = STAT_OK;

	/* Some other stuff */
	if(!mob->GOLD)
		mob->GOLD = _random(mob->LEVEL,(20+mob->LEVEL));
	if(!mob->EXP)
		mob->EXP = _random(mob->LEVEL,(mob->LEVEL*25));
	if(!mob->AGE)
		mob->AGE = _random(2,mob->LEVEL+1)+14;
	if(!mob->ARMOR)
		if((mob->ARMOR = (10 - (mob->LEVEL/3)< -10)))
			mob->ARMOR = -10;
	update_char(mob);
	return(0);
}

int   chk_obj_worn(struct object_data *object,int c)
{
   int i;

   /* See if flag is a WORN identifier */
   for(i=0;*obj_worn_flag[i].name != '\n';i++) {
      if(!str_cmp(buf1,obj_worn_flag[i].name)) {
         if(obj_worn_flag[i].args == -2) {
            if((!get_arg(buf2,buf,++c)) || !_numeric(&object->OBJ_SPECIAL,buf2))
               return(-1);
         } else {
            BSET(object->WORN,obj_worn_flag[i].setbit);
         }
         return(c);
      }
   }
   return(0);
}

int   get_align_flag(char *name)
{
   int i;
   /* Check !ALIGN flag */
   for(i=0;*anti_align_flag[i].name != '\n';i++)
      if(!str_cmp(name,anti_align_flag[i].name))
         return(i);
   return(-1);
}

int   get_sex_flag(char *name)
{
   int i;
   /* Check !SEX flag */
   for(i=0;*anti_sex_flag[i].name != '\n';i++)
      if(!str_cmp(name,anti_sex_flag[i].name))
         return(i);
   return(-1);
}
int   get_class_flag(char *name)
{
   int i;
   /* Check !CLASS flag */
   for(i=0;*anti_class_flag[i].name != '\n';i++)
      if(!str_cmp(name,anti_class_flag[i].name))
         return(i);
   return(-1);
}
int   get_mob_sex(char *name)
{
   int i;
   /* Check SEX */
   for(i=0;*sex_list[i].name != '\n';i++)
      if(!str_cmp(name,sex_list[i].name))
         return(sex_list[i].number);
   return(-1);
}
int   get_mob_class(char *name)
{
   int i;
   /* Check CLASS */
   for(i=0;*class_list[i].name != '\n';i++)
      if(!str_cmp(name,class_list[i].name))
         return(class_list[i].number);
   return(-1);
}
int   get_mob_flag(char *name)
{
   int i;
   /* Check MOB flag */
   for(i=0;*mob_flag[i].name != '\n';i++)
      if(!str_cmp(name,mob_flag[i].name))
         return(i);
   return(-1);
}
int   get_mob_char(char *name)
{
   int i;
   /* Check STAT flag */
   for(i=0;*data_list[i].name != '\n';i++)
      if(!str_cmp(name,data_list[i].name))
         return(data_list[i].number);
   return(-1);
}
int   get_mob_long(char *name)
{
   int i;
   /* Check STAT flag */
   for(i=0;*l_data_list[i].name != '\n';i++)
      if(!str_cmp(name,l_data_list[i].name))
         return(i);
   return(-1);
}

/* The name to comaper with is in 'buf1' */
int   chk_obj_type(struct object_data *object,int c)
{
   int i,f,size;

   /* See if flag is an OBJECT identifier */
   for(i=0;*obj_type_flag[i].name != '\n';i++) {
      if(!str_cmp(buf1,obj_type_flag[i].name)) {
         BSET(object->TYPE,obj_type_flag[i].setbit);
         for(f=0;f<obj_type_flag[i].args;f++) {
            if(!get_arg(buf1,buf,++c))
               return(-1);
            else if(!_numeric(&size,buf1))
               return(-1);
            else if(obj_type_flag[i].setbit == OBJ_EXPL || obj_type_flag[i].setbit == OBJ_LIGHT)
               object->data[f+10]=size;
            else
               object->data[f+6]=size;
         }
         return(c);
      }
   }
   return(0);
}

/*******************************************************************
	This routine should only be called after adding a new zone or
	at the program beginning after all zones have been loaded.
*******************************************************************/
void   index_zones(void)
{
	int   c,i,t,tmp_store;

	/* Make space for the indexed list */
	if(grid_list) {
		RESIZE(grid_list,struct grid_index_element,(top_of_z_table+1));

		GRID_X(top_of_z_table) = ZONE_X(top_of_z_table);
		GRID_Y(top_of_z_table) = ZONE_Y(top_of_z_table);
		GRID_Z(top_of_z_table) = ZONE_Z(top_of_z_table);
		grid_list[top_of_z_table].zone = top_of_z_table;
	} else {
		CREATE(grid_list,struct grid_index_element,(top_of_z_table+1));

		/* Copy the relevant information across */
		for(c=0;c<=top_of_z_table;c++) {
			GRID_X(c) = ZONE_X(c);
			GRID_Y(c) = ZONE_Y(c);
			GRID_Z(c) = ZONE_Z(c);
			grid_list[c].zone = c;
		}
	}

	/* Sort the zone 'idnum's into ascending order */
	for(c=0;c<top_of_z_table;c++) {
		for(i=c+1;i<=top_of_z_table;i++) {
			if(((GRID_X(i) != GRID_X(c)) ? (GRID_X(i) - GRID_X(c)) :
            ((GRID_Y(i) != GRID_Y(c)) ? (GRID_Y(i) - GRID_Y(c)) :
            ((GRID_Z(i) != GRID_Z(c)) ? (GRID_Z(i) - GRID_Z(c)) : 0 ))) < 0) {
				for(t=0;t<3;t++) {
					tmp_store = grid_list[c].xyz[t];
					grid_list[c].xyz[t] = grid_list[i].xyz[t];
					grid_list[i].xyz[t] = tmp_store;
				}
				tmp_store = grid_list[c].zone;
				grid_list[c].zone = grid_list[i].zone;
				grid_list[i].zone = tmp_store;
			}
		}
	}
}
// CRTH - Level advancement.
char	ChkExp(struct char_data *ch)
{
	char	ret = 0;

	if(ch->EXP >= levels[ch->LEVEL] && ch->LEVEL < IMP)
	{
		ch->LEVEL++;
		ch->MAX_HIT += ((rand() % (ch->STR + ch->CON))+5);
		ch->MAX_MANA += ((rand() % (ch->INT + ch->WIS)) + 5);
		ch->MAX_MOVE += ((rand() % (ch->DEX)) + 5);

		if(ch->LEVEL % 3)
		{
			ch->STR += (rand() % 3);
			ch->DEX += (rand() % 3);
			ch->CON += (rand() % 3);
			ch->INT += (rand() % 3);	
			ch->WIS += (rand() % 3);
		}
		// Award general skillpoints.
		ch->GEN_SKILL += 3;
		ch->SP_SKILL += 1;

		sprintf(buf,"You are now level \033[34m%d\033[0m!!\n\r",ch->LEVEL);
		send_2_output(buf,ch->desc);
		if(DEBUG_CHAR)
			printf("\033[1m\033[33m%s\033[0m is now level \033[34m%d\033[0m\n",ch->name,ch->LEVEL);
		ret = 1;
	}

	return(ret);

}

