Lyonesse/bin/
Lyonesse/doc/eng/
Lyonesse/doc/ita/
Lyonesse/lib/
Lyonesse/lib/buildings/
Lyonesse/lib/clans/
Lyonesse/lib/data/
Lyonesse/lib/etc/
Lyonesse/lib/house/
Lyonesse/lib/misc/
Lyonesse/lib/plralias/A-E/
Lyonesse/lib/plralias/F-J/
Lyonesse/lib/plralias/K-O/
Lyonesse/lib/plralias/P-T/
Lyonesse/lib/plralias/U-Z/
Lyonesse/lib/plralias/ZZZ/
Lyonesse/lib/plrobjs/A-E/
Lyonesse/lib/plrobjs/F-J/
Lyonesse/lib/plrobjs/K-O/
Lyonesse/lib/plrobjs/P-T/
Lyonesse/lib/plrobjs/U-Z/
Lyonesse/lib/plrobjs/ZZZ/
Lyonesse/lib/plrsave/A-E/
Lyonesse/lib/plrsave/F-J/
Lyonesse/lib/plrsave/K-O/
Lyonesse/lib/plrsave/P-T/
Lyonesse/lib/plrsave/U-Z/
Lyonesse/lib/plrsave/ZZZ/
Lyonesse/lib/ships/
Lyonesse/lib/stables/
Lyonesse/lib/text/help/
Lyonesse/lib/world/
Lyonesse/lib/world/bld/
Lyonesse/lib/world/ship/
Lyonesse/lib/world/shp/
Lyonesse/lib/world/wls/
Lyonesse/lib/world/wls/Life/
Lyonesse/lib/world/wls/Map/
Lyonesse/log/
/**************************************************************************
 * #   #   #   ##   #  #  ###   ##   ##  ###       http://www.lyonesse.it *
 * #    # #   #  #  ## #  #    #    #    #                                *
 * #     #    #  #  # ##  ##    #    #   ##   ## ##  #  #  ##             *
 * #     #    #  #  # ##  #      #    #  #    # # #  #  #  # #            *
 * ###   #     ##   #  #  ###  ##   ##   ###  #   #  ####  ##    Ver. 1.0 *
 *                                                                        *
 * -Based on CircleMud & Smaug-     Copyright (c) 2001-2002 by Mithrandir *
 *                                                                        *
 * ********************************************************************** */
/* ************************************************************************
*   File: act.wizard.c                                  Part of CircleMUD *
*  Usage: Player-level god commands and other goodies                     *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "spells.h"
#include "house.h"
#include "screen.h"
#include "constants.h"

/*   external vars  */
extern SPELL_INFO_DATA	spell_info[];
extern ATTACK_HIT_TYPE	attack_hit_text[];
extern char				*class_abbrevs[];
extern char				*race_abbrevs[];
extern time_t			boot_time;
extern int				circle_shutdown;
extern int				circle_reboot;
extern int				circle_restrict;
extern int				load_into_inventory;
extern int				buf_switches;
extern int				buf_largecount;
extern int				buf_overflows;
extern int				loaded_rooms;
extern const char		*pc_class_types[];
extern const char		*pc_race_types[];

/* extern functions */
ROOM_DATA *create_wild_room(COORD_DATA *coord, bool Static);
BUILDING_DATA *find_building(int vnum);
BUILDING_DATA *find_building_in_room_by_name(ROOM_DATA *pRoom, char *arg);
SHIP_DATA *find_ship( sh_int vnum );
TRADING_POST *find_trading_post(int vnum);
GOOD_DATA *get_good(int gnum);
OBJ_DATA *create_good_obj(GOOD_DATA *pGood, int amount);
int		get_sect( COORD_DATA *coord );
int		tot_exp_to_level(int level);
int		parse_class(char arg);
int		parse_race(char arg);
void	show_shops(CHAR_DATA *ch, char *value);
void	hcontrol_list_houses(CHAR_DATA *ch);
void	do_start(CHAR_DATA *ch);
void	appear(CHAR_DATA *ch);
void	reset_zone(zone_rnum zone);
void	roll_real_abils(CHAR_DATA *ch);
void	list_obj_to_char(OBJ_DATA *list, CHAR_DATA *ch, int mode, int show);
void	do_stat_wild(CHAR_DATA *ch);
void	ListSpellsInBook(CHAR_DATA *ch, SPELLBOOK *book, bool dampage);
void	list_spec_procs(CHAR_DATA *ch);
void	extract_vehicle(VEHICLE_DATA *vehicle, int mode);
void	do_stat_building(CHAR_DATA *ch, BUILDING_DATA *bld);
void	stat_vehicle(CHAR_DATA *ch, VEHICLE_DATA *vehicle);
void	char_to_building(CHAR_DATA *ch, BUILDING_DATA *bld, int room);
void	char_from_building(CHAR_DATA *ch);
void	char_from_ship( CHAR_DATA *ch );
void	char_to_ship( CHAR_DATA *ch, SHIP_DATA *ship, int room );

/* ----------------------------------------------------- */

ACMD(do_echo)
{
	skip_spaces(&argument);
	
	if (!*argument)
		send_to_char("Yes.. but what?\r\n", ch);
	else
	{
		if (subcmd == SCMD_EMOTE)
			sprintf(buf, "$n %s", argument);
		else
			strcpy(buf, argument);
		act(buf, FALSE, ch, 0, 0, TO_ROOM);
		if (PRF_FLAGGED(ch, PRF_NOREPEAT))
			send_to_char(OK, ch);
		else
			act(buf, FALSE, ch, 0, 0, TO_CHAR);
	}
}


ACMD(do_send)
{
	CHAR_DATA *vict;
	
	half_chop(argument, arg, buf);
	
	if (!*arg)
	{
		send_to_char("Send what to who?\r\n", ch);
		return;
	}
	if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
	{
		send_to_char(NOPERSON, ch);
		return;
	}
	send_to_char(buf, vict);
	send_to_char("\r\n", vict);
	if (PRF_FLAGGED(ch, PRF_NOREPEAT))
		send_to_char("Sent.\r\n", ch);
	else
	{
		sprintf(buf2, "You send '%s' to %s.\r\n", buf, GET_NAME(vict));
		send_to_char(buf2, ch);
	}
}



/* take a string, and return an rnum.. used for goto, at, etc.  -je 4/6/93 */
ROOM_DATA *find_target_room(CHAR_DATA *ch, char *rawroomstr)
{
	ROOM_DATA *location = NULL;
	char roomstr[MAX_INPUT_LENGTH];
	
	one_argument(rawroomstr, roomstr);
	
	if (!*roomstr)
	{
		send_to_char("You must supply a room number or name.\r\n", ch);
		return (NULL);
	}
	
	if (isdigit(*roomstr) && !strchr(roomstr, '.'))
	{
		if ((location = get_room((room_vnum)atoi(roomstr))) == NULL)
		{
			send_to_char("No room exists with that number.\r\n", ch);
			return (NULL);
		}
	}
	else
	{
		CHAR_DATA *target_mob;
		OBJ_DATA *target_obj;
		char *mobobjstr = roomstr;
		int num;
		
		num = get_number(&mobobjstr);
		if ((target_mob = get_char_vis(ch, mobobjstr, &num, FIND_CHAR_WORLD)) != NULL)
		{
			if ((location = target_mob->in_room) == NULL)
			{
				send_to_char("That character is currently lost.\r\n", ch);
				return (NULL);
			}
		}
		else if ((target_obj = get_obj_vis(ch, mobobjstr, &num)) != NULL)
		{
			if (target_obj->in_room != NULL)
				location = target_obj->in_room;
			else if (target_obj->carried_by && target_obj->carried_by->in_room != NULL)
				location = target_obj->carried_by->in_room;
			else if (target_obj->worn_by && target_obj->worn_by->in_room != NULL)
				location = target_obj->worn_by->in_room;
			
			if (location == NULL)
			{
				send_to_char("That object is currently not in a room.\r\n", ch);
				return (NULL);
			}
		}
		
		if (location == NULL)
		{
			send_to_char("Nothing exists by that name.\r\n", ch);
			return (NULL);
		}
	}
	
	/* a location has been found -- if you're >= GRGOD, no restrictions. */
	if (GET_LEVEL(ch) >= LVL_GRGOD)
		return (location);
	
	if (ROOM_FLAGGED(location, ROOM_GODROOM))
		send_to_char("You are not godly enough to use that room!\r\n", ch);
	else if (ROOM_FLAGGED(location, ROOM_PRIVATE) && location->people && location->people->next_in_room)
		send_to_char("There's a private conversation going on in that room.\r\n", ch);
	else if (ROOM_FLAGGED(location, ROOM_HOUSE) && !House_can_enter(ch, location->number))
		send_to_char("That's private property -- no trespassing!\r\n", ch);
	else
		return (location);
	
	return (NULL);
}



ACMD(do_at)
{
	ROOM_DATA *location, *original_loc;
	char command[MAX_INPUT_LENGTH];
	
	half_chop(argument, buf, command);
	if (!*buf)
	{
		send_to_char("You must supply a room number or a name.\r\n", ch);
		return;
	}
	
	if (!*command)
	{
		send_to_char("What do you want to do there?\r\n", ch);
		return;
	}
	
	if ((location = find_target_room(ch, buf)) < 0)
		return;
	
	/* a location has been found. */
	original_loc = ch->in_room;
	char_from_room(ch);
	char_to_room(ch, location);
	command_interpreter(ch, command);
	
	/* check if the char is still there */
	if (ch->in_room == location)
	{
		char_from_room(ch);
		char_to_room(ch, original_loc);
	}
}


ACMD(do_goto)
{
	ROOM_DATA *location;
	COORD_DATA coord;
	BUILDING_DATA *bld = NULL;
	SHIP_DATA *ship = NULL;
	char arg1[MAX_STRING_LENGTH], arg2[MAX_STRING_LENGTH];

	argument = two_arguments( argument, arg1, arg2 );

	if ( !*arg2)
	{
		if ((location = find_target_room(ch, arg1)) == NULL)
			return;
	}
	else if (isname(arg1, "building bld") && is_number(arg2))
	{
		int vnum = atoi(arg2);

		if (!(bld = find_building(vnum)))
		{
			send_to_char("There is not a building with that number.\r\n", ch);
			return;
		}
	}
	else if (isname(arg1, "ship") && is_number(arg2))
	{
		int vnum = atoi(arg2);

		if (!(ship = find_ship(vnum)))
		{
			send_to_char("There is not a ship with that number.\r\n", ch);
			return;
		}
	}
	else if (!str_cmp(arg1, "tp") && is_number(arg2))
	{
		TRADING_POST *pTp;
		int vnum = atoi(arg2);

		if (!(pTp = find_trading_post(vnum)))
		{
			send_to_char("There is not a trading post with that number.\r\n", ch);
			return;
		}

		if (!pTp->in_room)
		{
			send_to_char("That is a virtual TP -- is not assigned to any building.\r\n", ch);
			return;
		}

		if (!(bld = find_building(pTp->in_room->extra_data->vnum)))
		{
			send_to_char("ERROR: cannot find the tp's building.\r\n", ch);
			return;
		}
	}
	else if (is_number(arg1) && is_number(arg2))
	{
		coord.y = atoi(arg1);
		coord.x = atoi(arg2);

		if (!check_coord( coord.y, coord.x ))
		{
			send_to_char("Invalid coordinates.\r\n", ch );
			return;
		}

		if ( invalid_sect( &coord ) || get_sect( &coord ) == SECT_WILD_EXIT )
		{
			send_to_char( "Invalid destination sector.\r\n", ch );
			return;
		}

		location = create_wild_room( &coord, FALSE );
	}
	else
	{
		send_to_char("Goto where/what?\r\n", ch);
		return;
	}

	if (POOFOUT(ch))
		sprintf(buf, "$n %s", POOFOUT(ch));
	else
		strcpy(buf, "$n disappears in a puff of smoke.");
	
	act(buf, TRUE, ch, 0, 0, TO_ROOM);

	if (ch->in_building)
		char_from_building(ch);
	else if (ch->in_ship)
		char_from_ship(ch);
	else if (ch->in_vehicle)
		char_from_vehicle(ch);

	char_from_room(ch);
	
	if (bld)
		char_to_building(ch, bld, bld->type->entrance);
	else if (ship)
		char_to_ship(ch, ship, 0);
	else
	{
		if (location->extra_data)
		{
			if (IS_SHIP(location))
			{
				ship = find_ship(location->extra_data->vnum);
				char_to_ship(ch, ship, location->number);
			}
			else if (IS_BUILDING(location))
			{
				bld = find_building(location->extra_data->vnum);
				char_to_building(ch, bld, location->number);
			}
			else
				char_to_room(ch, location);
		}
		else
			char_to_room(ch, location);
	}

	if (POOFIN(ch))
		sprintf(buf, "$n %s", POOFIN(ch));
	else
		strcpy(buf, "$n appears with an ear-splitting bang.");
	
	act(buf, TRUE, ch, 0, 0, TO_ROOM);
	look_at_room(ch, 0);
}



ACMD(do_trans)
{
	DESCRIPTOR_DATA *i;
	CHAR_DATA *victim;
	
	one_argument(argument, buf);
	if (!*buf)
		send_to_char("Whom do you wish to transfer?\r\n", ch);
	else if (str_cmp("all", buf))
	{
		if (!(victim = get_char_vis(ch, buf, NULL, FIND_CHAR_WORLD)))
			send_to_char(NOPERSON, ch);
		else if (victim == ch)
			send_to_char("That doesn't make much sense, does it?\r\n", ch);
		else if (victim->in_obj)
			send_to_char("You are in a furniture and cannot trans people.\r\n", ch);
		else
		{
			if ((GET_LEVEL(ch) < GET_LEVEL(victim)) && !IS_NPC(victim))
			{
				send_to_char("Go transfer someone your own size.\r\n", ch);
				return;
			}
			act("$n disappears in a mushroom cloud.", FALSE, victim, 0, 0, TO_ROOM);

			if (victim->in_building)
				char_from_building(victim);
			else if (victim->in_ship)
				char_from_ship(victim);
			else if (victim->in_vehicle)
				char_from_vehicle(victim);

			char_from_room(victim);

			if (ch->in_building)
				char_to_building(victim, ch->in_building, ch->in_room->number);
			else if (ch->in_ship)
				char_to_ship(victim, ch->in_ship, ch->in_room->number);
			else if (ch->in_vehicle)
				char_to_vehicle(victim, ch->in_vehicle);
			else
				char_to_room(victim, ch->in_room);

			act("$n arrives from a puff of smoke.", FALSE, victim, 0, 0, TO_ROOM);
			act("$n has transferred you!", FALSE, ch, 0, victim, TO_VICT);
			look_at_room(victim, 0);
		}
	}
	else			/* Trans All */
	{
		if (GET_LEVEL(ch) < LVL_GRGOD)
		{
			send_to_char("I think not.\r\n", ch);
			return;
		}
		
		for (i = descriptor_list; i; i = i->next)
		{
			if (STATE(i) == CON_PLAYING && i->character && i->character != ch)
			{
				victim = i->character;
				if (GET_LEVEL(victim) >= GET_LEVEL(ch))
					continue;
				act("$n disappears in a mushroom cloud.", FALSE, victim, 0, 0, TO_ROOM);

				if (victim->in_building)
					char_from_building(victim);
				else if (victim->in_ship)
					char_from_ship(victim);
				else if (victim->in_vehicle)
					char_from_vehicle(victim);

				char_from_room(victim);

				if (ch->in_building)
					char_to_building(victim, ch->in_building, ch->in_room->number);
				else if (ch->in_ship)
					char_to_ship(victim, ch->in_ship, ch->in_room->number);
				else if (ch->in_vehicle)
					char_to_vehicle(victim, ch->in_vehicle);
				else
					char_to_room(victim, ch->in_room);

				act("$n arrives from a puff of smoke.", FALSE, victim, 0, 0, TO_ROOM);
				act("$n has transferred you!", FALSE, ch, 0, victim, TO_VICT);
				look_at_room(victim, 0);
			}
		}
		send_to_char(OK, ch);
	}
}



ACMD(do_teleport)
{
	CHAR_DATA *victim;
	ROOM_DATA *target;
	
	two_arguments(argument, buf, buf2);
	
	if (!*buf)
		send_to_char("Whom do you wish to teleport?\r\n", ch);
	else if (!(victim = get_char_vis(ch, buf, NULL, FIND_CHAR_WORLD)))
		send_to_char(NOPERSON, ch);
	else if (victim == ch)
		send_to_char("Use 'goto' to teleport yourself.\r\n", ch);
	else if (GET_LEVEL(victim) >= GET_LEVEL(ch))
		send_to_char("Maybe you shouldn't do that.\r\n", ch);
	else if (!*buf2)
		send_to_char("Where do you wish to send this person?\r\n", ch);
	else if ((target = find_target_room(ch, buf2)) >= 0)
	{
		send_to_char(OK, ch);
		act("$n disappears in a puff of smoke.", FALSE, victim, 0, 0, TO_ROOM);

		if (victim->in_building)
			char_from_building(victim);
		else if (victim->in_ship)
			char_from_ship(victim);
		else if (victim->in_vehicle)
			char_from_vehicle(victim);

		char_from_room(victim);

		char_to_room(victim, target);
		act("$n arrives from a puff of smoke.", FALSE, victim, 0, 0, TO_ROOM);
		act("$n has teleported you!", FALSE, ch, 0, (char *) victim, TO_VICT);
		look_at_room(victim, 0);
	}
}


int find_attack_type(char *arg)
{
	int nr;
	
	for (nr = 0; nr < NUM_ATTACK_TYPES; nr++)
	{
		if (is_abbrev(arg, attack_hit_text[nr].singular))
			break;
	}
	
	return (nr);
}


ACMD(do_vnum)
{
	half_chop(argument, buf, buf2);
	
	if (!*buf || !*buf2 || (!is_abbrev(buf, "mob") && !is_abbrev(buf, "obj") && !is_abbrev(buf, "weapon")))
	{
		send_to_char("Usage: vnum { obj | mob | weapon } <name | attack-type>\r\n", ch);
		return;
	}

	if (is_abbrev(buf, "mob"))
		if (!vnum_mobile(buf2, ch))
			send_to_char("No mobiles by that name.\r\n", ch);
		
	if (is_abbrev(buf, "obj"))
		if (!vnum_object(buf2, ch))
			send_to_char("No objects by that name.\r\n", ch);
  
	if (is_abbrev(buf, "weapon"))
		if (!vnum_weapon(find_attack_type(buf2), ch))
			send_to_char("No weapons with that attack-type.\r\n", ch);
}



void do_stat_room(CHAR_DATA * ch)
{
	EXTRA_DESCR *desc;
	ROOM_DATA *rm = ch->in_room;
	EXIT_DATA *pexit;
	CHAR_DATA *k;
	ROOM_AFFECT *ra;
	TRIGGER_DATA *trig;
	int found;
	
	sprintf(buf, "Room name: %s%s%s\r\n", CCCYN(ch, C_NRM), ROOM_RNAME(rm),
		CCNRM(ch, C_NRM));
	send_to_char(buf, ch);

	strcpy(buf2, terrain_type[rm->sector_type]->name);

	if ( IS_WILD(rm) )
	{
		sprintf(buf, "Zone: [Wilderness], Coord: [%s%5d %5d%s], Type: %s\r\n",
			CCGRN(ch, C_NRM), GET_RY(rm), GET_RX(rm),
			CCNRM(ch, C_NRM), buf2);
	}
	else if ( IS_SHIP(rm) )
	{
		sprintf(buf, "Zone: [Ship], VNum: [%s%5d%s], Type: %s\r\n",
			CCGRN(ch, C_NRM), rm->number, CCNRM(ch, C_NRM),
			buf2);
	}
	else if ( IS_BUILDING(rm) )
	{
		sprintf(buf, "Zone: [Building], VNum: [%s%5d%s], Type: %s\r\n",
			CCGRN(ch, C_NRM), rm->number, CCNRM(ch, C_NRM),
			buf2);
	}
	else
	{
		sprintf(buf, "Zone: [%3d], VNum: [%s%5d%s], Type: %s\r\n",
			zone_table[rm->zone].number,
			CCGRN(ch, C_NRM), rm->number, CCNRM(ch, C_NRM),
			buf2);
	}
	send_to_char(buf, ch);
	
	sprintbit(rm->room_flags, room_bits, buf2);
	sprintf(buf, "SpecProc: %s, Flags: %s\r\n",
		(rm->func == NULL) ? "None" : "Exists", buf2);
	send_to_char(buf, ch);
	
	send_to_char("Description:\r\n", ch);
	send_to_char( ROOM_RDESC(rm), ch );
	
	if (rm->ex_description)
	{
		sprintf(buf, "Extra descs:%s", CCCYN(ch, C_NRM));
		for (desc = rm->ex_description; desc; desc = desc->next)
		{
			strcat(buf, " ");
			strcat(buf, desc->keyword);
		}
		strcat(buf, CCNRM(ch, C_NRM));
		send_to_char(strcat(buf, "\r\n"), ch);
	}
	sprintf(buf, "Chars present:%s", CCYEL(ch, C_NRM));
	for (found = 0, k = rm->people; k; k = k->next_in_room)
	{
		if (!CAN_SEE(ch, k))
			continue;
		sprintf(buf2, "%s %s(%s)", found++ ? "," : "", GET_NAME(k),
			(!IS_NPC(k) ? "PC" : (!IS_MOB(k) ? "NPC" : "MOB")));
		strcat(buf, buf2);
		if (strlen(buf) >= 62)
		{
			if (k->next_in_room)
				send_to_char(strcat(buf, ",\r\n"), ch);
			else
				send_to_char(strcat(buf, "\r\n"), ch);
			*buf = found = 0;
		}
	}
	
	if (*buf)
		send_to_char(strcat(buf, "\r\n"), ch);
	send_to_char(CCNRM(ch, C_NRM), ch);
	
	if (rm->first_content)
	{
		send_to_char( "Contents:&2\r\n", ch );
		list_obj_to_char(rm->first_content, ch, 0, TRUE);
		send_to_char( "&0", ch );
	}

	for ( pexit = rm->first_exit; pexit; pexit = pexit->next )
	{
		if ( pexit->to_room == NULL )
			sprintf(buf1, " %sNONE%s", CCCYN(ch, C_NRM), CCNRM(ch, C_NRM));
		else
		{
			if ( IS_WILD( pexit->to_room ) )
				sprintf(buf1, "%s%5d %5d%s", CCCYN(ch, C_NRM), GET_RY(pexit->to_room), GET_RX(pexit->to_room), CCNRM(ch, C_NRM));
			else
				sprintf(buf1, "%s%5d%s", CCCYN(ch, C_NRM), pexit->to_room->number, CCNRM(ch, C_NRM));
		}
		
		sprintbit( pexit->exit_info, exit_bits, buf2);
		sprintf(buf, "Exit %s%-5s%s:  To: [%s], Key: [%5d], Keywrd: %s, Type: %s\r\n ",
			CCCYN(ch, C_NRM), dirs[pexit->vdir], CCNRM(ch, C_NRM), buf1,
			pexit->key, pexit->keyword ? pexit->keyword : "None",
			buf2);
		send_to_char(buf, ch);
		
		if (pexit->description)
			strcpy(buf, pexit->description);
		else
			strcpy(buf, "  No exit description.\r\n");
		send_to_char(buf, ch);
	}

	for ( ra = rm->affections; ra; ra = ra->next_in_room )
	{
		sprintbit( ra->bitvector, room_affections, buf2);
		sprintf( buf, "RAFF: %s [LEV: %d] Timer: [%d] Bit: [%s] Text: %s\r\n",
			spell_info[ra->spell].name, ra->level, ra->timer, buf2,
			ra->text ? ra->text : "<nothing>");
		send_to_char( buf, ch );
	}

	for ( trig = rm->trigger; trig; trig = trig->next )
	{
		sprintbit(trig->action, trig_act_descr, buf2);
		sprintf( buf, "TRIGGER: '%s', Timer: [%d], Action: %s, WhoTrig: %s\r\n",
			trig_type_descr[trig->type], trig->timer, buf2,
			trig_who_descr[trig->whotrig]);
		send_to_char( buf, ch );
	}
}



void do_stat_object(CHAR_DATA *ch, OBJ_DATA *j)
{
	OBJ_DATA *j2;
	EXTRA_DESCR *desc;
	int i, found;
	obj_vnum vnum;
	
	vnum = GET_OBJ_VNUM(j);
	sprintf(buf, "Name: '%s%s%s', Aliases: %s\r\n", CCYEL(ch, C_NRM),
		((j->short_description) ? j->short_description : "<None>"),
		CCNRM(ch, C_NRM), j->name);
	send_to_char(buf, ch);
	sprinttype(GET_OBJ_TYPE(j), item_types, buf1);
	if (GET_OBJ_RNUM(j) >= 0)
		strcpy(buf2, (obj_index[GET_OBJ_RNUM(j)].func ? "Exists" : "None"));
	else
		strcpy(buf2, "None");
	sprintf(buf, "VNum: [%s%5d%s], RNum: [%5d], Type: %s, SpecProc: %s\r\n",
		CCGRN(ch, C_NRM), vnum, CCNRM(ch, C_NRM), GET_OBJ_RNUM(j), buf1, buf2);
	send_to_char(buf, ch);
	sprintf(buf, "L-Des: %s\r\n", ((j->description) ? j->description : "None"));
	send_to_char(buf, ch);
	
	if (j->ex_description)
	{
		sprintf(buf, "Extra descs:%s", CCCYN(ch, C_NRM));
		for (desc = j->ex_description; desc; desc = desc->next)
		{
			strcat(buf, " ");
			strcat(buf, desc->keyword);
		}
		strcat(buf, CCNRM(ch, C_NRM));
		send_to_char(strcat(buf, "\r\n"), ch);
	}

	send_to_char("Can be worn on: ", ch);
	sprintbit(j->obj_flags.wear_flags, wear_bits, buf);
	strcat(buf, "\r\n");
	send_to_char(buf, ch);
	
	send_to_char("Set char bits : ", ch);
	sprintbit(j->obj_flags.bitvector, affected_bits, buf);
	strcat(buf, "\r\n");
	send_to_char(buf, ch);
	
	send_to_char("Extra flags   : ", ch);
	sprintbit(GET_OBJ_EXTRA(j), extra_bits, buf);
	strcat(buf, "\r\n");
	send_to_char(buf, ch);

	send_to_char("Anti flags    : ", ch);
	sprintbit(GET_OBJ_ANTI(j), anti_bits, buf);
	strcat(buf, "\r\n");
	send_to_char(buf, ch);
	
	sprintf(buf, "Item count: %d\r\n", j->count);
	send_to_char(buf, ch);
	
	if ( j->count > 1 )
		sprintf(buf, "Single Item Weight: %d, Real Weight: %d\r\n",
			GET_OBJ_WEIGHT(j), get_real_obj_weight(j));
	else
		sprintf(buf, "Weight: %d, ", GET_OBJ_WEIGHT(j));
	send_to_char(buf, ch);
	
	sprintf(buf, "Value: %d, Cost/day: %d, Timer: %d, Min Level: %d\r\n",
		GET_OBJ_COST(j), GET_OBJ_RENT(j), GET_OBJ_TIMER(j), GET_OBJ_LEVEL(j));
	send_to_char(buf, ch);
	
	sprintf(buf, "Quality: %d   Item Condition: [%d/%d] %s.\r\n",
		GET_OBJ_QUALITY(j),
		GET_OBJ_COND(j), GET_OBJ_MAXCOND(j),
		obj_cond_table[find_objcond_index(j)]);
	send_to_char(buf, ch);

	strcpy(buf, "In room: ");
	if (j->in_room == NULL)
		strcat(buf, "Nowhere");
	else
	{
		if ( IN_WILD(j) )
			sprintf(buf2, "[%d %d]", GET_Y(j), GET_X(j) );
		else
			sprintf(buf2, "%d", j->in_room->number );
		strcat(buf, buf2);
	}
	
	/*
	 * NOTE: In order to make it this far, we must already be able to see the
	 *       character holding the object. Therefore, we do not need CAN_SEE().
	 */
	strcat(buf, ", In object: ");
	strcat(buf, j->in_obj ? j->in_obj->short_description : "None");
	strcat(buf, ", Carried by: ");
	strcat(buf, j->carried_by ? GET_NAME(j->carried_by) : "Nobody");
	strcat(buf, ", Worn by: ");
	strcat(buf, j->worn_by ? GET_NAME(j->worn_by) : "Nobody");
	strcat(buf, "\r\n");
	send_to_char(buf, ch);
	
	switch (GET_OBJ_TYPE(j))
	{
	case ITEM_LIGHT:
		if (GET_OBJ_VAL(j, 2) == -1)
			strcpy(buf, "Hours left: Infinite");
		else
			sprintf(buf, "Hours left: [%d]", GET_OBJ_VAL(j, 2));
		break;
	case ITEM_SCROLL:
	case ITEM_POTION:
		sprintf(buf, "Spells: (Level %d) %s, %s, %s", GET_OBJ_VAL(j, 0),
			skill_name(GET_OBJ_VAL(j, 1)), skill_name(GET_OBJ_VAL(j, 2)),
			skill_name(GET_OBJ_VAL(j, 3)));
		break;
	case ITEM_WAND:
	case ITEM_STAFF:
		sprintf(buf, "Spell: %s at level %d, %d (of %d) charges remaining",
			skill_name(GET_OBJ_VAL(j, 3)), GET_OBJ_VAL(j, 0),
			GET_OBJ_VAL(j, 2), GET_OBJ_VAL(j, 1));
		break;
	case ITEM_WEAPON:
		sprintf(buf, "Todam: %dd%d, Message type: %d",
			GET_OBJ_VAL(j, 1), GET_OBJ_VAL(j, 2), GET_OBJ_VAL(j, 3));
		break;
	case ITEM_ARMOR:
		sprintf(buf, "AC-apply: [%d]", GET_OBJ_VAL(j, 0));
		break;
	case ITEM_TRAP:
		sprintf(buf, "Spell: %d, - Hitpoints: %d",
			GET_OBJ_VAL(j, 0), GET_OBJ_VAL(j, 1));
		break;
	case ITEM_CONTAINER:
		sprintbit(GET_OBJ_VAL(j, 1), container_bits, buf2);
		sprintf(buf, "Weight capacity: %d, Lock Type: %s, Key Num: %d, Corpse: %s",
			GET_OBJ_VAL(j, 0), buf2, GET_OBJ_VAL(j, 2),
			YESNO(GET_OBJ_VAL(j, 3)));
		break;
	case ITEM_DRINKCON:
	case ITEM_FOUNTAIN:
		sprinttype(GET_OBJ_VAL(j, 2), drinks, buf2);
		sprintf(buf, "Capacity: %d, Contains: %d, Poisoned: %s, Liquid: %s",
			GET_OBJ_VAL(j, 0), GET_OBJ_VAL(j, 1), YESNO(GET_OBJ_VAL(j, 3)),
			buf2);
		break;
	case ITEM_NOTE:
		sprintf(buf, "Tongue: %d", GET_OBJ_VAL(j, 0));
		break;
	case ITEM_KEY:
		strcpy(buf, "");
		break;
	case ITEM_FOOD:
		sprintf(buf, "Makes full: %d, Poisoned: %s", GET_OBJ_VAL(j, 0),
			YESNO(GET_OBJ_VAL(j, 3)));
		break;
	case ITEM_MONEY:
		sprintf(buf, "Coins: %d", GET_OBJ_VAL(j, 0));
		break;
	case ITEM_FURNITURE:
		sprintf(buf, "Furniture type: %s, Max position: %s, People: %d, Capacity: %d",
			furn_types[GET_OBJ_VAL(j, 3)],
			position_types[GET_OBJ_VAL(j, 2)],
			GET_OBJ_VAL(j, 1), GET_OBJ_VAL(j, 0));
		break;
	case ITEM_FIREWEAPON:
		sprintf(buf, "Missiles: %s, Range: %d", 
			missile_cont_table[GET_OBJ_VAL(j, 0)], GET_OBJ_VAL(j, 1));
		break;
	case ITEM_MISSILE:
		sprintf(buf, "Missile: %s, Num dam dice: %d, Size dam dice: %d, Extra dam %d",
			missile_cont_table[GET_OBJ_VAL(j, 0)], 
			GET_OBJ_VAL(j, 1), GET_OBJ_VAL(j, 2), GET_OBJ_VAL(j, 3) );
		break;
	case ITEM_MISSILE_CONT:
		sprintf(buf, "Missile Container: %s, Capacity %d",
			missile_cont_table[GET_OBJ_VAL(j, 2)], GET_OBJ_VAL(j, 0));
		break;

	default:
		sprintf(buf, "Values 0-3: [%d] [%d] [%d] [%d]",
			GET_OBJ_VAL(j, 0), GET_OBJ_VAL(j, 1),
			GET_OBJ_VAL(j, 2), GET_OBJ_VAL(j, 3));
		break;
	}
	send_to_char(strcat(buf, "\r\n"), ch);
	
	/*
	 * I deleted the "equipment status" code from here because it seemed
	 * more or less useless and just takes up valuable screen space.
	 */
	if (j->first_content)
	{
		sprintf(buf, "\r\nContents:%s", CCGRN(ch, C_NRM));
		for (found = 0, j2 = j->first_content; j2; j2 = j2->next_content)
		{
			sprintf(buf2, "%s %s", found++ ? "," : "", j2->short_description);
			strcat(buf, buf2);
			if (strlen(buf) >= 62)
			{
				if (j2->next_content)
					send_to_char(strcat(buf, ",\r\n"), ch);
				else
					send_to_char(strcat(buf, "\r\n"), ch);
				*buf = found = 0;
			}
		}
		
		if (*buf)
			send_to_char(strcat(buf, "\r\n"), ch);
		send_to_char(CCNRM(ch, C_NRM), ch);
	}
	
	/* List the people using this piece of furniture */
	if (j->people)
	{
		CHAR_DATA *jch;

		sprintf(buf, "\r\nChars using Object: %s", CCYEL(ch, C_NRM));
		for (found = 0, jch = j->people; jch; jch = jch->next_in_obj)
		{
			if (!CAN_SEE(ch, jch))
				continue;
			sprintf(buf2, "%s %s (%s)", found++ ? "," : "", PERS(jch, ch),
				(!IS_NPC(jch) ? "PC" : (!IS_MOB(jch) ? "NPC" : "MOB")));
			strcat(buf, buf2);
			if (strlen(buf) >= 62)
			{
				if (jch->next_in_obj)
					send_to_char(strcat(buf, ",\r\n"), ch);
				else
					send_to_char(strcat(buf, "\r\n"), ch);
				*buf = found = 0;
			}
		}
		
		if (*buf)
			send_to_char(strcat(buf, "\r\n"), ch);
		send_to_char(CCNRM(ch, C_NRM), ch);
	}

	found = 0;

	send_to_char("Affections:", ch);
	for (i = 0; i < MAX_OBJ_AFF; i++)
	{
		if (j->affected[i].modifier)
		{
			sprinttype(j->affected[i].location, apply_types, buf2);
			sprintf(buf, "%s %+d to %s", found++ ? "," : "",
				j->affected[i].modifier, buf2);
			send_to_char(buf, ch);
		}
	}
	if (!found)
		send_to_char(" None", ch);
		
	send_to_char("\r\n", ch);

	/* object has special data attached */
	if ( j->special )
	{
		if (OBJ_FLAGGED(j, ITEM_IS_SPELLBOOK))
			ListSpellsInBook(ch, (SPELLBOOK *) j->special, FALSE);
		else if (OBJ_FLAGGED(j, ITEM_HAS_SPELLS))
		{
			OBJ_SPELLS_DATA *spells;

			found = 0;
			send_to_char("Spells:", ch);
			
			for (spells = (OBJ_SPELLS_DATA *) j->special; spells; spells = spells->next)
			{
				if (spells->spellnum)
				{
					sprintf(buf, "%s '%s', level: %d, perc: %d", found++ ? "," : "",
						skill_name(spells->spellnum),
						spells->level, spells->percent);
					send_to_char(buf, ch);
				}
			}
			if (!found)
				send_to_char(" None", ch);
			send_to_char("\r\n", ch);
		}
		else if (OBJ_FLAGGED(j, ITEM_HAS_TRAPS))
		{
			OBJ_TRAP_DATA *trap;
			char dflags[256];

			for (trap = (OBJ_TRAP_DATA *) j->special; trap; trap = trap->next)
			{
				sprintbit(trap->action, trap_act_list, dflags);
				sprintf(buf, "TRAP: Damtype: %s, Trigger: %s, Charges: %d, Everyone: %s\r\n",
					trap_dam_descr[trap->dam_type], dflags, trap->charges,
					( trap->whole_room ? "Yes" : "No" ) );
				send_to_char(buf, ch);
			}
		}
		else
			send_to_char("Item has unknown special data attached.\r\n", ch);
	}
}


void do_stat_character(CHAR_DATA *ch, CHAR_DATA *k)
{
	OBJ_DATA *j;
	FOLLOW_DATA *fol;
	AFFECTED_DATA *aff;
	int i, i2, found = 0;
	
	sprinttype(GET_SEX(k), genders, buf);
	if ( IN_WILD(k) )
		sprintf(buf3, "[%d %d]", GET_Y(k), GET_X(k) );
	else
		sprintf(buf3, "%d", k->in_room->number );
	sprintf(buf2, " %s '%s'  IDNum: [%5ld], In room [%s]\r\n",
		(!IS_NPC(k) ? "PC" : (!IS_MOB(k) ? "NPC" : "MOB")),
		GET_NAME(k), GET_IDNUM(k), buf3);
	send_to_char(strcat(buf, buf2), ch);
	
	if (IS_MOB(k))
	{
		sprintf(buf, "Alias: %s, VNum: [%5d], RNum: [%5d]\r\n",
			GET_PC_NAME(k), GET_MOB_VNUM(k), GET_MOB_RNUM(k));
		send_to_char(buf, ch);
	}
	
	sprintf(buf, "Title: %s\r\n", (GET_TITLE(k) ? GET_TITLE(k) : "<None>"));
	send_to_char(buf, ch);
	
	sprintf(buf, "L-Des: %s", (GET_LDESC(k) ? GET_LDESC(k) : "<None>\r\n"));
	send_to_char(buf, ch);
	
	if (IS_NPC(k))
	{	/* Use GET_CLASS() macro? */
		strcpy(buf, "Monster Class: ");
		sprinttype(k->player.chclass, npc_class_types, buf2);
	}
	else
	{
		strcpy(buf, "Class: ");
		sprinttype(k->player.chclass, pc_class_types, buf2);
	}
	strcat(buf, buf2);

	if (!IS_NPC(k))
	{
		strcat(buf, ", Race: ");
		sprinttype(GET_RACE(k), pc_race_types, buf2);
		strcat(buf, buf2);
	}

	sprintf(buf2, ", Lev: [%s%2d%s], XP: [%s%7d%s], Align: [%4d]\r\n",
		CCYEL(ch, C_NRM), GET_LEVEL(k), CCNRM(ch, C_NRM),
		CCYEL(ch, C_NRM), GET_EXP(k), CCNRM(ch, C_NRM),
		GET_ALIGNMENT(k));
	strcat(buf, buf2);
	send_to_char(buf, ch);
	
	if (!IS_NPC(k))
	{
		if (MULTICLASS(k))
		{
			int c, found = 0;

			sprintf(buf, "Total Level: %d, Completed classes:", GET_TOT_LEVEL(k));
			for (c = 0; c < NUM_CLASSES; c++)
			{
				if (GET_CLASS(k) == c) continue;

				if (MULTICLASSED(k, (1 << c)))
				{
					sprinttype(c, pc_class_types, buf2);
					sprintf(buf+strlen(buf), "%s %s", found++ ? "," : "", buf2);
				}
			}
			strcat(buf, "\r\n");
			send_to_char(buf, ch);
		}
		strcpy(buf1, (char *) asctime(localtime(&(k->player.time.birth))));
		strcpy(buf2, (char *) asctime(localtime(&(k->player.time.logon))));
		buf1[10] = buf2[10] = '\0';
		
		sprintf(buf, "Created: [%s], Last Logon: [%s], Played [%dh %dm], Age [%d]\r\n",
			buf1, buf2, k->player.time.played / 3600,
			((k->player.time.played % 3600) / 60), age(k)->year);
		send_to_char(buf, ch);
		
		sprintf(buf, "Hometown: [%d], Speaks: [%d/%d/%d], (STL[%d]/per[%d]/NSTL[%d])\r\n",
			k->player.hometown, GET_TALK(k, 0), GET_TALK(k, 1), GET_TALK(k, 2),
			GET_PRACTICES(k), int_app[GET_INT(k)].learn,
			wis_app[GET_WIS(k)].bonus);
		send_to_char(buf, ch);
	}

	sprintf(buf, "Str: [%s%d%s]  Int: [%s%d%s]  Wis: [%s%d%s]  "
		"Dex: [%s%d%s]  Con: [%s%d%s]  Cha: [%s%d%s]\r\n",
		CCCYN(ch, C_NRM), GET_STR(k), CCNRM(ch, C_NRM),
		CCCYN(ch, C_NRM), GET_INT(k), CCNRM(ch, C_NRM),
		CCCYN(ch, C_NRM), GET_WIS(k), CCNRM(ch, C_NRM),
		CCCYN(ch, C_NRM), GET_DEX(k), CCNRM(ch, C_NRM),
		CCCYN(ch, C_NRM), GET_CON(k), CCNRM(ch, C_NRM),
		CCCYN(ch, C_NRM), GET_CHA(k), CCNRM(ch, C_NRM));
	send_to_char(buf, ch);
	
	sprintf(buf, "Hit p.:[%s%d/%d+%d%s]  Mana p.:[%s%d/%d+%d%s]  Move p.:[%s%d/%d+%d%s]\r\n",
		CCGRN(ch, C_NRM), GET_HIT(k), GET_MAX_HIT(k), hit_gain(k), CCNRM(ch, C_NRM),
		CCGRN(ch, C_NRM), GET_MANA(k), GET_MAX_MANA(k), mana_gain(k), CCNRM(ch, C_NRM),
		CCGRN(ch, C_NRM), GET_MOVE(k), GET_MAX_MOVE(k), move_gain(k), CCNRM(ch, C_NRM));
	send_to_char(buf, ch);
	
	sprintf(buf, "Coins: [%9d], Bank: [%9d] (Total: %d)\r\n",
		get_gold(k), GET_BANK_GOLD(k), get_gold(k) + GET_BANK_GOLD(k));
	send_to_char(buf, ch);
	
	sprintf(buf, "AC: [%d%+d/10], Hitroll: [%2d], Damroll: [%2d], Saving throws: [%d/%d/%d/%d/%d]\r\n",
		GET_AC(k), dex_app[GET_DEX(k)].defensive, k->points.hitroll,
		k->points.damroll, GET_SAVE(k, 0), GET_SAVE(k, 1), GET_SAVE(k, 2),
		GET_SAVE(k, 3), GET_SAVE(k, 4));
	send_to_char(buf, ch);
	
	sprinttype(GET_POS(k), position_types, buf2);
	sprintf(buf, "Pos: %s, Fighting: %s", buf2,
		(FIGHTING(k) ? GET_NAME(FIGHTING(k)) : "Nobody"));
	
	if (IS_NPC(k))
	{
		strcat(buf, ", Attack type: ");
		strcat(buf, attack_hit_text[GET_ATTACK(k)].singular);
	}
	if (k->desc)
	{
		sprinttype(STATE(k->desc), connected_types, buf2);
		strcat(buf, ", Connected: ");
		strcat(buf, buf2);
	}
	send_to_char(strcat(buf, "\r\n"), ch);
	
	strcpy(buf, "Default position: ");
	sprinttype((k->mob_specials.default_pos), position_types, buf2);
	strcat(buf, buf2);
	
	sprintf(buf2, ", Idle Timer (in tics) [%d]\r\n", k->player.timer);
	strcat(buf, buf2);
	send_to_char(buf, ch);
	
	if (IS_NPC(k))
	{
		sprintbit(MOB_FLAGS(k), action_bits, buf2);
		sprintf(buf, "NPC flags: %s%s%s\r\n", CCCYN(ch, C_NRM), buf2, CCNRM(ch, C_NRM));
		send_to_char(buf, ch);
	}
	else
	{
		sprintbit(PLR_FLAGS(k), player_bits, buf2);
		sprintf(buf, "PLR: %s%s%s\r\n", CCCYN(ch, C_NRM), buf2, CCNRM(ch, C_NRM));
		send_to_char(buf, ch);
		sprintbit(PRF_FLAGS(k), preference_bits, buf2);
		sprintf(buf, "PRF: %s%s%s\r\n", CCGRN(ch, C_NRM), buf2, CCNRM(ch, C_NRM));
		send_to_char(buf, ch);
	}
	
	if (IS_MOB(k))
	{
		sprintf(buf, "Mob Spec-Proc: %s, NPC Bare Hand Dam: %dd%d\r\n",
			(mob_index[GET_MOB_RNUM(k)].func ? "Exists" : "None"),
			GET_NDD(k), GET_SDD(k));
		send_to_char(buf, ch);
	}
	sprintf(buf, "Carried: weight: %d, items: %d; ", IS_CARRYING_W(k), IS_CARRYING_N(k));
	
	for (i = 0, j = k->first_carrying; j; j = j->next_content )
	{
		i += j->count;
	}
	sprintf(buf + strlen(buf), "Items in: inventory: %d, ", i);
	
	for (i = 0, i2 = 0; i < NUM_WEARS; i++)
	{
		if (GET_EQ(k, i))
			i2++;
	}
	sprintf(buf2, "eq: %d\r\n", i2);
	strcat(buf, buf2);
	send_to_char(buf, ch);
	
	if (!IS_NPC(k))
	{
		sprintf(buf, "Hunger: %d, Thirst: %d, Drunk: %d\r\n",
			GET_COND(k, FULL), GET_COND(k, THIRST), GET_COND(k, DRUNK));
		send_to_char(buf, ch);
	}
	
	sprintf(buf, "Master is: %s, Followers are:",
		((k->master) ? GET_NAME(k->master) : "<none>"));
	
	for (fol = k->followers; fol; fol = fol->next)
	{
		sprintf(buf2, "%s %s", found++ ? "," : "", PERS(fol->follower, ch));
		strcat(buf, buf2);
		if (strlen(buf) >= 62)
		{
			if (fol->next)
				send_to_char(strcat(buf, ",\r\n"), ch);
			else
				send_to_char(strcat(buf, "\r\n"), ch);
			*buf = found = 0;
		}
	}
	
	if (*buf)
		send_to_char(strcat(buf, "\r\n"), ch);

	if ( k->in_obj )
	{
		sprintf(buf, "%s on: %s\r\n",
			position_types[GET_POS(k)], k->in_obj->short_description);
		CAP(buf);
		send_to_char(strcat(buf, "\r\n"), ch);
	}
	
	/* Showing the bitvector */
	sprintbit(AFF_FLAGS(k), affected_bits, buf2);
	sprintf(buf, "AFF: %s%s%s\r\n", CCYEL(ch, C_NRM), buf2, CCNRM(ch, C_NRM));
	send_to_char(buf, ch);
	
	/* Routine to show what spells a char is affected by */
	if (k->affected)
	{
		for (aff = k->affected; aff; aff = aff->next)
		{
			*buf2 = '\0';
			sprintf(buf, "SPL: (%3dhr) %s%-21s%s ", aff->duration + 1,
				CCCYN(ch, C_NRM), skill_name(aff->type), CCNRM(ch, C_NRM));
			if (aff->modifier)
			{
				sprintf(buf2, "%+d to %s", aff->modifier, apply_types[(int) aff->location]);
				strcat(buf, buf2);
			}
			if (aff->bitvector)
			{
				if (*buf2)
					strcat(buf, ", sets ");
				else
					strcat(buf, "sets ");
				sprintbit(aff->bitvector, affected_bits, buf2);
				strcat(buf, buf2);
			}
			send_to_char(strcat(buf, "\r\n"), ch);
		}
	}
}


ACMD(do_stat)
{
	CHAR_DATA *victim;
	OBJ_DATA *object;
	
	half_chop(argument, buf1, buf2);
	
	if (!*buf1)
	{
		send_to_char("Stats on who or what?\r\n", ch);
		return;
	}
	else if (is_abbrev(buf1, "room"))
	{
		do_stat_room(ch);
	}
	else if (is_abbrev(buf1, "mob"))
	{
		if (!*buf2)
			send_to_char("Stats on which mobile?\r\n", ch);
		else
		{
			if ((victim = get_char_vis(ch, buf2, NULL, FIND_CHAR_WORLD)) != NULL)
				do_stat_character(ch, victim);
			else
				send_to_char("No such mobile around.\r\n", ch);
		}
	}
	else if (is_abbrev(buf1, "player"))
	{
		if (!*buf2)
		{
			send_to_char("Stats on which player?\r\n", ch);
		}
		else
		{
			if ((victim = get_player_vis(ch, buf2, NULL, FIND_CHAR_WORLD)) != NULL)
				do_stat_character(ch, victim);
			else
				send_to_char("No such player around.\r\n", ch);
		}
	}
	else if (is_abbrev(buf1, "file"))
	{
		if (!*buf2)
			send_to_char("Stats on which player?\r\n", ch);
		else if ((victim = get_player_vis(ch, buf2, NULL, FIND_CHAR_WORLD)) != NULL)
			do_stat_character(ch, victim);
		else
		{
			CREATE(victim, CHAR_DATA, 1);
			clear_char(victim);
			CREATE(victim->player_specials, PLAYER_SPECIAL, 1);
			if (load_char(buf2, victim) >= 0)
			{
				char_to_room(victim, get_room(0));
				if (GET_LEVEL(victim) > GET_LEVEL(ch))
					send_to_char("Sorry, you can't do that.\r\n", ch);
				else
					do_stat_character(ch, victim);
				extract_char_final(victim);
			}
			else
			{
				send_to_char("There is no such player.\r\n", ch);
				free_char(victim);
			}
		}
	}
	else if (is_abbrev(buf1, "object"))
	{
		if (!*buf2)
			send_to_char("Stats on which object?\r\n", ch);
		else
		{
			if ((object = get_obj_vis(ch, buf2, NULL)) != NULL)
				do_stat_object(ch, object);
			else
				send_to_char("No such object around.\r\n", ch);
		}
	}
	else if (is_abbrev(buf1, "wild"))
		do_stat_wild(ch);
	else
	{
		BUILDING_DATA *pBld;
		VEHICLE_DATA *pVeh;
		char *name = buf1;
		int numbr, nnum;
		
		nnum = get_number(&name);
		
		numbr = nnum;
		if ((object = get_obj_in_equip_vis(ch, name, &numbr, ch->equipment)) != NULL)
		{
			do_stat_object(ch, object);
			return;
		}
		
		//numbr = nnum;
		if ((object = get_obj_in_list_vis_rev(ch, name, &numbr, ch->last_carrying)) != NULL)
		{
			do_stat_object(ch, object);
			return;
		}
		
		//numbr = nnum;
		if ((object = get_obj_in_list_vis_rev(ch, name, &numbr, ch->in_room->last_content)) != NULL)
		{
			do_stat_object(ch, object);
			return;
		}
		
		numbr = nnum;
		if ((victim = get_char_vis(ch, name, &numbr, FIND_CHAR_ROOM)) != NULL)
		{
			do_stat_character(ch, victim);
			return;
		}
		
		//numbr = nnum;
		if ((victim = get_char_vis(ch, name, &numbr, FIND_CHAR_WORLD)) != NULL)
		{
			do_stat_character(ch, victim);
			return;
		}
		
		numbr = nnum;
		if ((object = get_obj_vis(ch, name, &numbr)) != NULL)
		{
			do_stat_object(ch, object);
			return;
		}

		// look for vehicles in room
		if ((pVeh = find_vehicle_in_room_by_name(ch, str_dup(name))) != NULL)
		{
			stat_vehicle(ch, pVeh);
			return;
		}

		// look for buildings in room
		if ((pBld = find_building_in_room_by_name(ch->in_room, str_dup(name))) != NULL)
		{
			do_stat_building(ch, pBld);
			return;
		}

		ch_printf(ch, "Nothing around called '%s'.\r\n", name);
	}
}


ACMD(do_shutdown)
{
	if (subcmd != SCMD_SHUTDOWN)
	{
		send_to_char("If you want to shut something down, say so!\r\n", ch);
		return;
	}
	one_argument(argument, arg);
	
	if (!*arg)
	{
		log("(GC) Shutdown by %s.", GET_NAME(ch));
		send_to_all("Shutting down.\r\n");
		circle_shutdown = 1;
	}
	else if (!str_cmp(arg, "reboot"))
	{
		log("(GC) Reboot by %s.", GET_NAME(ch));
		send_to_all("Rebooting.. come back in a minute or two.\r\n");
		touch(FASTBOOT_FILE);
		circle_shutdown = circle_reboot = 1;
	}
	else if (!str_cmp(arg, "die"))
	{
		log("(GC) Shutdown by %s.", GET_NAME(ch));
		send_to_all("Shutting down for maintenance.\r\n");
		touch(KILLSCRIPT_FILE);
		circle_shutdown = 1;
	}
	else if (!str_cmp(arg, "pause"))
	{
		log("(GC) Shutdown by %s.", GET_NAME(ch));
		send_to_all("Shutting down for maintenance.\r\n");
		touch(PAUSE_FILE);
		circle_shutdown = 1;
	}
	else
		send_to_char("Unknown shutdown option.\r\n", ch);
}


void snoop_check(CHAR_DATA *ch)
{
	/*  This short routine is to ensure that characters that happen
	 *  to be snooping (or snooped) and get advanced/demoted will
	 *  not be snooping/snooped someone of a higher/lower level (and
	 *  thus, not entitled to be snooping.
	 */
	if (!ch || !ch->desc)
		return;

	if (ch->desc->snooping &&
		(GET_LEVEL(ch->desc->snooping->character) >= GET_LEVEL(ch)))
	{
		ch->desc->snooping->snoop_by = NULL;
		ch->desc->snooping = NULL;
	}
	
	if (ch->desc->snoop_by &&
		(GET_LEVEL(ch) >= GET_LEVEL(ch->desc->snoop_by->character)))
	{
		ch->desc->snoop_by->snooping = NULL;
		ch->desc->snoop_by = NULL;
	}
}

void stop_snooping(CHAR_DATA *ch)
{
	if (!ch->desc->snooping)
		send_to_char("You aren't snooping anyone.\r\n", ch);
	else
	{
		send_to_char("You stop snooping.\r\n", ch);
		ch->desc->snooping->snoop_by = NULL;
		ch->desc->snooping = NULL;
	}
}


ACMD(do_snoop)
{
	CHAR_DATA *victim, *tch;
	
	if (!ch->desc)
		return;
	
	one_argument(argument, arg);
	
	if (!*arg)
		stop_snooping(ch);
	else if (!(victim = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
		send_to_char("No such person around.\r\n", ch);
	else if (!victim->desc)
		send_to_char("There's no link.. nothing to snoop.\r\n", ch);
	else if (victim == ch)
		stop_snooping(ch);
	else if (victim->desc->snoop_by)
		send_to_char("Busy already. \r\n", ch);
	else if (victim->desc->snooping == ch->desc)
		send_to_char("Don't be stupid.\r\n", ch);
	else
	{
		if (victim->desc->original)
			tch = victim->desc->original;
		else
			tch = victim;
		
		if (GET_LEVEL(tch) >= GET_LEVEL(ch))
		{
			send_to_char("You can't.\r\n", ch);
			return;
		}
		send_to_char(OK, ch);
		
		if (ch->desc->snooping)
			ch->desc->snooping->snoop_by = NULL;
		
		ch->desc->snooping = victim->desc;
		victim->desc->snoop_by = ch->desc;
	}
}



ACMD(do_switch)
{
	CHAR_DATA *victim;
	
	one_argument(argument, arg);
	
	if (ch->desc->original)
		send_to_char("You're already switched.\r\n", ch);
	else if (!*arg)
		send_to_char("Switch with who?\r\n", ch);
	else if (!(victim = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
		send_to_char("No such character.\r\n", ch);
	else if (ch == victim)
		send_to_char("Hee hee... we are jolly funny today, eh?\r\n", ch);
	else if (victim->desc)
		send_to_char("You can't do that, the body is already in use!\r\n", ch);
	else if ((GET_LEVEL(ch) < LVL_IMPL) && !IS_NPC(victim))
		send_to_char("You aren't holy enough to use a mortal's body.\r\n", ch);
	else if (GET_LEVEL(ch) < LVL_GRGOD && ROOM_FLAGGED(IN_ROOM(victim), ROOM_GODROOM))
		send_to_char("You are not godly enough to use that room!\r\n", ch);
	else if (GET_LEVEL(ch) < LVL_GRGOD && ROOM_FLAGGED(IN_ROOM(victim), ROOM_HOUSE)
		&& !House_can_enter(ch, victim->in_room->number))
		send_to_char("That's private property -- no trespassing!\r\n", ch);
	else
	{
		send_to_char(OK, ch);
		
		ch->desc->character = victim;
		ch->desc->original = ch;
		
		victim->desc = ch->desc;
		ch->desc = NULL;
	}
}


ACMD(do_return)
{
	if (ch->desc && ch->desc->original)
	{
		send_to_char("You return to your original body.\r\n", ch);
		
		/*
		 * If someone switched into your original body, disconnect them.
		 *   - JE 2/22/95
		 *
		 * Zmey: here we put someone switched in our body to disconnect state
		 * but we must also NULL his pointer to our character, otherwise
		 * close_socket() will damage our character's pointer to our descriptor
		 * (which is assigned below in this function). 12/17/99
		 */
		if (ch->desc->original->desc)
		{
			ch->desc->original->desc->character = NULL;
			STATE(ch->desc->original->desc) = CON_DISCONNECT;
		}
		
		/* Now our descriptor points to our original body. */
		ch->desc->character = ch->desc->original;
		ch->desc->original = NULL;
		
		/* And our body's pointer to descriptor now points to our descriptor. */
		ch->desc->character->desc = ch->desc;
		ch->desc = NULL;
	}
}



ACMD(do_load)
{
	CHAR_DATA *mob;
	OBJ_DATA *obj;
	GOOD_DATA *pGood;
	mob_vnum number;
	mob_rnum r_num;
	
	two_arguments(argument, buf, buf2);
	
	if (!*buf || !*buf2 || !isdigit(*buf2))
	{
		send_to_char("Usage: load { obj | mob | good } <number>\r\n", ch);
		return;
	}

	if ((number = atoi(buf2)) < 0)
	{
		send_to_char("A NEGATIVE number??\r\n", ch);
		return;
	}

	if (is_abbrev(buf, "mob"))
	{
		if ((r_num = real_mobile(number)) < 0)
		{
			send_to_char("There is no monster with that number.\r\n", ch);
			return;
		}
		mob = read_mobile(r_num, REAL);
		char_to_room(mob, ch->in_room);
		
		act("$n makes a quaint, magical gesture with one hand.", TRUE, ch,
			0, 0, TO_ROOM);
		act("$n has created $N!", FALSE, ch, 0, mob, TO_ROOM);
		act("You create $N.", FALSE, ch, 0, mob, TO_CHAR);
	}
	else if (is_abbrev(buf, "obj"))
	{
		if ((r_num = real_object(number)) < 0)
		{
			send_to_char("There is no object with that number.\r\n", ch);
			return;
		}
		obj = read_object(r_num, REAL);
		if (load_into_inventory)
			obj = obj_to_char(obj, ch);
		else
			obj = obj_to_room(obj, ch->in_room);
		act("$n makes a strange magical gesture.", TRUE, ch, 0, 0, TO_ROOM);
		act("$n has created $p!", FALSE, ch, obj, 0, TO_ROOM);
		act("You create $p.", FALSE, ch, obj, 0, TO_CHAR);
	}
	else if (is_abbrev(buf, "good"))
	{
		if (!(pGood = get_good(number)))
		{
			send_to_char("There is no goods with that number\r\n", ch);
			return;
		}

		obj = create_good_obj(pGood, 1);

		if (load_into_inventory)
			obj = obj_to_char(obj, ch);
		else
			obj = obj_to_room(obj, ch->in_room);
		act("$n makes a strange magical gesture.", TRUE, ch, 0, 0, TO_ROOM);
		act("$n has created $p!", FALSE, ch, obj, 0, TO_ROOM);
		act("You create $p.", FALSE, ch, obj, 0, TO_CHAR);
	}
	else
		send_to_char("That'll have to be either 'obj' or 'mob'.\r\n", ch);
}



ACMD(do_vstat)
{
	CHAR_DATA *mob;
	OBJ_DATA *obj;
	mob_vnum number;	/* or obj_vnum ... */
	mob_rnum r_num;	/* or obj_rnum ... */
	
	two_arguments(argument, buf, buf2);
	
	if (!*buf || !*buf2 || !isdigit(*buf2))
	{
		send_to_char("Usage: vstat { obj | mob } <number>\r\n", ch);
		return;
	}
	if ((number = atoi(buf2)) < 0)
	{
		send_to_char("A NEGATIVE number??\r\n", ch);
		return;
	}
	if (is_abbrev(buf, "mob"))
	{
		if ((r_num = real_mobile(number)) < 0)
		{
			send_to_char("There is no monster with that number.\r\n", ch);
			return;
		}
		mob = read_mobile(r_num, REAL);
		char_to_room(mob, get_room(0));
		do_stat_character(ch, mob);
		extract_char(mob);
	}
	else if (is_abbrev(buf, "obj"))
	{
		if ((r_num = real_object(number)) < 0)
		{
			send_to_char("There is no object with that number.\r\n", ch);
			return;
		}
		obj = read_object(r_num, REAL);
		do_stat_object(ch, obj);
		extract_obj(obj);
	}
	else
		send_to_char("That'll have to be either 'obj' or 'mob'.\r\n", ch);
}


/* clean a room of all mobiles and objects */
ACMD(do_purge)
{
	CHAR_DATA *vict, *next_v;
	OBJ_DATA *obj, *next_o;
	VEHICLE_DATA *vehicle, *next_vehicle;
	
	one_argument(argument, buf);
	
	/* argument supplied. destroy single object or char */
	if (*buf)
	{
		if ((vict = get_char_vis(ch, buf, NULL, FIND_CHAR_ROOM)) != NULL)
		{
			if (!IS_NPC(vict) && (GET_LEVEL(ch) <= GET_LEVEL(vict)))
			{
				send_to_char("Fuuuuuuuuu!\r\n", ch);
				return;
			}
			act("$n disintegrates $N.", FALSE, ch, 0, vict, TO_NOTVICT);
			
			if (!IS_NPC(vict))
			{
				sprintf(buf, "(GC) %s has purged %s.", GET_NAME(ch), GET_NAME(vict));
				mudlog(buf, BRF, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
				if (vict->desc)
				{
					STATE(vict->desc) = CON_CLOSE;
					vict->desc->character = NULL;
					vict->desc = NULL;
				}
			}
			extract_char(vict);
		}
		else if ((obj = get_obj_in_list_vis_rev(ch, buf, NULL, ch->in_room->last_content)) != NULL)
		{
			act("$n destroys $p.", FALSE, ch, obj, 0, TO_ROOM);
			extract_obj(obj);
		}
		else
		{
			send_to_char("Nothing here by that name.\r\n", ch);
			return;
		}
		
		send_to_char(OK, ch);
	}
	else			/* no argument. clean out the room */
	{
		act("$n gestures... You are surrounded by scorching flames!",
			FALSE, ch, 0, 0, TO_ROOM);
		send_to_room("The world seems a little cleaner.\r\n", ch->in_room);
		
		for (vict = ch->in_room->people; vict; vict = next_v)
		{
			next_v = vict->next_in_room;
			if (IS_NPC(vict))
				extract_char(vict);
		}
		
		for (obj = ch->in_room->first_content; obj; obj = next_o)
		{
			next_o = obj->next_content;
			extract_obj(obj);
		}

		for (vehicle = ch->in_room->vehicles; vehicle; next_vehicle)
		{
			next_vehicle = vehicle->next_in_room;
			extract_vehicle(vehicle, 1);
		}
	}
}



const char *logtypes[] =
{
  "off", "brief", "normal", "complete", "\n"
};

ACMD(do_syslog)
{
	int tp;
	
	one_argument(argument, arg);
	
	if (!*arg)
	{
		tp = ((PRF_FLAGGED(ch, PRF_LOG1) ? 1 : 0) +
			(PRF_FLAGGED(ch, PRF_LOG2) ? 2 : 0));
		sprintf(buf, "Your syslog is currently %s.\r\n", logtypes[tp]);
		send_to_char(buf, ch);
		return;
	}
	if (((tp = search_block(arg, logtypes, FALSE)) == -1))
	{
		send_to_char("Usage: syslog { Off | Brief | Normal | Complete }\r\n", ch);
		return;
	}
	REMOVE_BIT(PRF_FLAGS(ch), PRF_LOG1 | PRF_LOG2);
	SET_BIT(PRF_FLAGS(ch), (PRF_LOG1 * (tp & 1)) | (PRF_LOG2 * (tp & 2) >> 1));
	
	sprintf(buf, "Your syslog is now %s.\r\n", logtypes[tp]);
	send_to_char(buf, ch);
}



ACMD(do_advance)
{
	CHAR_DATA *victim;
	char *name = arg, *level = buf2;
	int newlevel, oldlevel;
	
	two_arguments(argument, name, level);
	
	if (*name)
	{
		if (!(victim = get_char_vis(ch, name, NULL, FIND_CHAR_WORLD)))
		{
			send_to_char("That player is not here.\r\n", ch);
			return;
		}
	}
	else
	{
		send_to_char("Advance who?\r\n", ch);
		return;
	}
	
	if (GET_LEVEL(ch) <= GET_LEVEL(victim))
	{
		send_to_char("Maybe that's not such a great idea.\r\n", ch);
		return;
	}
	if (IS_NPC(victim))
	{
		send_to_char("NO!  Not on NPC's.\r\n", ch);
		return;
	}
	if (!*level || (newlevel = atoi(level)) <= 0)
	{
		send_to_char("That's not a level!\r\n", ch);
		return;
	}
	if (newlevel > LVL_IMPL)
	{
		sprintf(buf, "%d is the highest possible level.\r\n", LVL_IMPL);
		send_to_char(buf, ch);
		return;
	}
	if (newlevel > GET_LEVEL(ch))
	{
		send_to_char("Yeah, right.\r\n", ch);
		return;
	}
	if (newlevel == GET_LEVEL(victim))
	{
		send_to_char("They are already at that level.\r\n", ch);
		return;
	}
	oldlevel = GET_LEVEL(victim);
	if (newlevel < GET_LEVEL(victim))
	{
		do_start(victim);
		GET_LEVEL(victim) = newlevel;
		GET_TOT_LEVEL(victim) -= (oldlevel - newlevel);
		send_to_char("You are momentarily enveloped by darkness!\r\n"
			"You feel somewhat diminished.\r\n", victim);
	}
	else
	{
		act("$n makes some strange gestures.\r\n"
			"A strange feeling comes upon you,\r\n"
			"Like a giant hand, light comes down\r\n"
			"from above, grabbing your body, that\r\n"
			"begins to pulse with colored lights\r\n"
			"from inside.\r\n\r\n"
			"Your head seems to be filled with demons\r\n"
			"from another plane as your body dissolves\r\n"
			"to the elements of time and space itself.\r\n"
			"Suddenly a silent explosion of light\r\n"
			"snaps you back to reality.\r\n\r\n"
			"You feel slightly different.", FALSE, ch, 0, victim, TO_VICT);
	}
	
	send_to_char(OK, ch);
	
	if (newlevel < oldlevel)
		log("(GC) %s demoted %s from level %d to %d.",
			GET_NAME(ch), GET_NAME(victim), oldlevel, newlevel);
	else
		log("(GC) %s has advanced %s to level %d (from %d)",
			GET_NAME(ch), GET_NAME(victim), newlevel, oldlevel);
	
	if (newlevel < LVL_IMMORT)
	{
		/*
		 * If they are no longer an immortal, let's remove some of the
		 * nice immortal only flags, shall we?
		 */
		REMOVE_BIT(PRF_FLAGS(victim), PRF_LOG1 | PRF_LOG2);
		REMOVE_BIT(PRF_FLAGS(victim), PRF_NOHASSLE | PRF_HOLYLIGHT);
	}

	GET_EXP(victim) = 0;
	gain_exp_regardless(victim, tot_exp_to_level(newlevel));
	save_char(victim, NULL);
}

ACMD(do_restore)
{
	CHAR_DATA *vict;
	int i;
	
	one_argument(argument, buf);
	if (!*buf)
		send_to_char("Whom do you wish to restore?\r\n", ch);
	else if (!(vict = get_char_vis(ch, buf, NULL, FIND_CHAR_WORLD)))
		send_to_char(NOPERSON, ch);
	else if (!IS_NPC(vict) && ch != vict && GET_LEVEL(vict) >= GET_LEVEL(ch))
		send_to_char("They don't need your help.\r\n", ch);
	else
	{
		GET_HIT(vict)	= GET_MAX_HIT(vict);
		GET_MANA(vict)	= GET_MAX_MANA(vict);
		GET_MOVE(vict)	= GET_MAX_MOVE(vict);
		
		if (!IS_NPC(vict) && GET_LEVEL(ch) >= LVL_GRGOD)
		{
			if (GET_LEVEL(vict) >= LVL_IMMORT)
				for (i = 1; i <= MAX_SKILLS; i++)
					SET_SKILL(vict, i, 100);
			
			if (GET_LEVEL(vict) >= LVL_GRGOD)
			{
				vict->real_abils.intel	= 25;
				vict->real_abils.wis	= 25;
				vict->real_abils.dex	= 25;
				vict->real_abils.str	= 25;
				vict->real_abils.con	= 25;
				vict->real_abils.cha	= 25;
			}
		}
		update_pos(vict);
		affect_total(vict);

		send_to_char(OK, ch);
		act("You have been fully healed by $N!", FALSE, vict, 0, ch, TO_CHAR);
	}
}


void perform_immort_vis(CHAR_DATA *ch)
{
	if (GET_INVIS_LEV(ch) == 0 && !AFF_FLAGGED(ch, AFF_HIDE | AFF_INVISIBLE))
	{
		send_to_char("You are already fully visible.\r\n", ch);
		return;
	}
	
	GET_INVIS_LEV(ch) = 0;
	appear(ch);
	send_to_char("You are now fully visible.\r\n", ch);
}


void perform_immort_invis(CHAR_DATA *ch, int level)
{
	CHAR_DATA *tch;
	
	for (tch = ch->in_room->people; tch; tch = tch->next_in_room)
	{
		if (tch == ch)
			continue;
		if (GET_LEVEL(tch) >= GET_INVIS_LEV(ch) && GET_LEVEL(tch) < level)
			act("You blink and suddenly realize that $n is gone.", FALSE, ch, 0,
			tch, TO_VICT);
		if (GET_LEVEL(tch) < GET_INVIS_LEV(ch) && GET_LEVEL(tch) >= level)
			act("You suddenly realize that $n is standing beside you.", FALSE, ch, 0,
			tch, TO_VICT);
	}
	
	GET_INVIS_LEV(ch) = level;
	sprintf(buf, "Your invisibility level is %d.\r\n", level);
	send_to_char(buf, ch);
}
  

ACMD(do_invis)
{
	int level;
	
	if (IS_NPC(ch))
	{
		send_to_char("You can't do that!\r\n", ch);
		return;
	}
	
	one_argument(argument, arg);
	if (!*arg)
	{
		if (GET_INVIS_LEV(ch) > 0)
			perform_immort_vis(ch);
		else
			perform_immort_invis(ch, GET_LEVEL(ch));
	}
	else
	{
		level = atoi(arg);
		if (level > GET_LEVEL(ch))
			send_to_char("You can't go invisible above your own level.\r\n", ch);
		else if (level < 1)
			perform_immort_vis(ch);
		else
			perform_immort_invis(ch, level);
	}
}


ACMD(do_gecho)
{
	DESCRIPTOR_DATA *pt;
	
	skip_spaces(&argument);
	delete_doubledollar(argument);
	
	if (!*argument)
		send_to_char("That must be a mistake...\r\n", ch);
	else
	{
		sprintf(buf, "%s\r\n", argument);
		for (pt = descriptor_list; pt; pt = pt->next)
		{
			if (STATE(pt) == CON_PLAYING && pt->character && pt->character != ch)
				send_to_char(buf, pt->character);
		}
		if (PRF_FLAGGED(ch, PRF_NOREPEAT))
			send_to_char(OK, ch);
		else
			send_to_char(buf, ch);
	}
}


ACMD(do_poofset)
{
	char **msg;
	
	switch (subcmd)
	{
	case SCMD_POOFIN:    msg = &(POOFIN(ch));    break;
	case SCMD_POOFOUT:   msg = &(POOFOUT(ch));   break;
	default:    return;
	}
	
	skip_spaces(&argument);
	
	if (*msg)
		free(*msg);
	
	if (!*argument)
		*msg = NULL;
	else
		*msg = str_dup(argument);
	
	send_to_char(OK, ch);
}



ACMD(do_dc)
{
	DESCRIPTOR_DATA *d;
	int num_to_dc;
	
	one_argument(argument, arg);
	if (!(num_to_dc = atoi(arg)))
	{
		send_to_char("Usage: DC <user number> (type USERS for a list)\r\n", ch);
		return;
	}
	for (d = descriptor_list; d && d->desc_num != num_to_dc; d = d->next);
	
	if (!d)
	{
		send_to_char("No such connection.\r\n", ch);
		return;
	}
	if (d->character && GET_LEVEL(d->character) >= GET_LEVEL(ch))
	{
		if (!CAN_SEE(ch, d->character))
			send_to_char("No such connection.\r\n", ch);
		else
			send_to_char("Umm.. maybe that's not such a good idea...\r\n", ch);
		return;
	}
	
	/* We used to just close the socket here using close_socket(), but
	 * various people pointed out this could cause a crash if you're
	 * closing the person below you on the descriptor list.  Just setting
	 * to CON_CLOSE leaves things in a massively inconsistent state so I
	 * had to add this new flag to the descriptor. -je
	 *
	 * It is a much more logical extension for a CON_DISCONNECT to be used
	 * for in-game socket closes and CON_CLOSE for out of game closings.
	 * This will retain the stability of the close_me hack while being
	 * neater in appearance. -gg 12/1/97
	 *
	 * For those unlucky souls who actually manage to get disconnected
	 * by two different immortals in the same 1/10th of a second, we have
	 * the below 'if' check. -gg 12/17/99
	 */
	if (STATE(d) == CON_DISCONNECT || STATE(d) == CON_CLOSE)
		send_to_char("They're already being disconnected.\r\n", ch);
	else
	{
		/*
		 * Remember that we can disconnect people not in the game and
		 * that rather confuses the code when it expected there to be
		 * a character context.
		 */
		if (STATE(d) == CON_PLAYING)
			STATE(d) = CON_DISCONNECT;
		else
			STATE(d) = CON_CLOSE;
		
		sprintf(buf, "Connection #%d closed.\r\n", num_to_dc);
		send_to_char(buf, ch);
		log("(GC) Connection closed by %s.", GET_NAME(ch));
	}
}



ACMD(do_wizlock)
{
	int value;
	const char *when;
	
	one_argument(argument, arg);
	if (*arg)
	{
		value = atoi(arg);
		if (value < 0 || value > GET_LEVEL(ch))
		{
			send_to_char("Invalid wizlock value.\r\n", ch);
			return;
		}
		circle_restrict = value;
		when = "now";
	}
	else
		when = "currently";
	
	switch (circle_restrict)
	{
	case 0:
		sprintf(buf, "The game is %s completely open.\r\n", when);
		break;
	case 1:
		sprintf(buf, "The game is %s closed to new players.\r\n", when);
		break;
	default:
		sprintf(buf, "Only level %d and above may enter the game %s.\r\n",
			circle_restrict, when);
		break;
	}
	send_to_char(buf, ch);
}


ACMD(do_date)
{
	char *tmstr;
	time_t mytime;
	int d, h, m;
	
	if (subcmd == SCMD_DATE)
		mytime = time(0);
	else
		mytime = boot_time;
	
	tmstr = (char *) asctime(localtime(&mytime));
	*(tmstr + strlen(tmstr) - 1) = '\0';
	
	if (subcmd == SCMD_DATE)
		sprintf(buf, "Current machine time: %s\r\n", tmstr);
	else
	{
		mytime = time(0) - boot_time;
		d = mytime / 86400;
		h = (mytime / 3600) % 24;
		m = (mytime / 60) % 60;
		
		sprintf(buf, "Up since %s: %d day%s, %d:%02d\r\n", tmstr, d,
			((d == 1) ? "" : "s"), h, m);
	}
	
	send_to_char(buf, ch);
}



ACMD(do_last)
{
	CHAR_DATA *vict = NULL;
	
	one_argument(argument, arg);
	if (!*arg)
	{
		send_to_char("For whom do you wish to search?\r\n", ch);
		return;
	}
	CREATE(vict, struct char_data, 1);
	clear_char(vict);
	CREATE(vict->player_specials, PLAYER_SPECIAL, 1);
	if (load_char(arg, vict) <  0)
	{
		send_to_char("There is no such player.\r\n", ch);
		free_char(vict);
		return;
	}
	if ((GET_LEVEL(vict) > GET_LEVEL(ch)) && (GET_LEVEL(ch) < LVL_IMPL))
	{
		send_to_char("You are not sufficiently godly for that!\r\n", ch);
		return;
	}
	sprintf(buf, "[%5ld] [%2d %s %s] %-12s : %-18s : %-20s\r\n",
		GET_IDNUM(vict), GET_LEVEL(vict),
		race_abbrevs[(int) GET_RACE(vict)],
		class_abbrevs[(int) GET_CLASS(vict)], GET_NAME(vict),
		vict->player_specials->host && *vict->player_specials->host
		? vict->player_specials->host : "(NOHOST)",
		ctime(&vict->player.time.logon));
	send_to_char(buf, ch);
	free_char(vict);
}


ACMD(do_force)
{
	DESCRIPTOR_DATA *i, *next_desc;
	CHAR_DATA *vict, *next_force;
	char to_force[MAX_INPUT_LENGTH + 2];
	
	half_chop(argument, arg, to_force);
	
	sprintf(buf1, "$n has forced you to '%s'.", to_force);
	
	if (!*arg || !*to_force)
		send_to_char("Whom do you wish to force do what?\r\n", ch);
	else if ((GET_LEVEL(ch) < LVL_GRGOD) || (str_cmp("all", arg) && str_cmp("room", arg)))
	{
		if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
			send_to_char(NOPERSON, ch);
		else if (!IS_NPC(vict) && GET_LEVEL(ch) <= GET_LEVEL(vict))
			send_to_char("No, no, no!\r\n", ch);
		else
		{
			send_to_char(OK, ch);
			act(buf1, TRUE, ch, NULL, vict, TO_VICT);
			sprintf(buf, "(GC) %s forced %s to %s", GET_NAME(ch), GET_NAME(vict), to_force);
			mudlog(buf, NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
			command_interpreter(vict, to_force);
		}
	}
	else if (!str_cmp("room", arg))
	{
		send_to_char(OK, ch);
		sprintf(buf, "(GC) %s forced room %d to %s",
			GET_NAME(ch), ch->in_room->number, to_force);
		mudlog(buf, NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
		
		for (vict = ch->in_room->people; vict; vict = next_force)
		{
			next_force = vict->next_in_room;
			if (!IS_NPC(vict) && GET_LEVEL(vict) >= GET_LEVEL(ch))
				continue;
			act(buf1, TRUE, ch, NULL, vict, TO_VICT);
			command_interpreter(vict, to_force);
		}
	}
	else		 /* force all */
	{
		send_to_char(OK, ch);
		sprintf(buf, "(GC) %s forced all to %s", GET_NAME(ch), to_force);
		mudlog(buf, NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
		
		for (i = descriptor_list; i; i = next_desc)
		{
			next_desc = i->next;
			
			if (STATE(i) != CON_PLAYING || !(vict = i->character) || (!IS_NPC(vict) && GET_LEVEL(vict) >= GET_LEVEL(ch)))
				continue;
			act(buf1, TRUE, ch, NULL, vict, TO_VICT);
			command_interpreter(vict, to_force);
		}
	}
}



ACMD(do_wiznet)
{
	DESCRIPTOR_DATA *d;
	char emote = FALSE;
	char any = FALSE;
	int level = LVL_IMMORT;
	
	skip_spaces(&argument);
	delete_doubledollar(argument);
	
	if (!*argument)
	{
		send_to_char("Usage: wiznet <text> | #<level> <text> | *<emotetext> |\r\n "
			"       wiznet @<level> *<emotetext> | wiz @\r\n", ch);
		return;
	}

	switch (*argument)
	{
	case '*':
		emote = TRUE;

	case '#':
		one_argument(argument + 1, buf1);
		if (is_number(buf1))
		{
			half_chop(argument+1, buf1, argument);
			level = MAX(atoi(buf1), LVL_IMMORT);
			if (level > GET_LEVEL(ch))
			{
				send_to_char("You can't wizline above your own level.\r\n", ch);
				return;
			}
		}
		else if (emote)
			argument++;
		break;

	case '@':
		for (d = descriptor_list; d; d = d->next)
		{
			if (STATE(d) == CON_PLAYING && GET_LEVEL(d->character) >= LVL_IMMORT &&
				!PRF_FLAGGED(d->character, PRF_NOWIZ) &&
				(CAN_SEE(ch, d->character) || GET_LEVEL(ch) == LVL_IMPL))
			{
				if (!any)
				{
					strcpy(buf1, "Gods online:\r\n");
					any = TRUE;
				}
				sprintf(buf1 + strlen(buf1), "  %s", GET_NAME(d->character));
				if (PLR_FLAGGED(d->character, PLR_WRITING))
					strcat(buf1, " (Writing)\r\n");
				else if (PLR_FLAGGED(d->character, PLR_MAILING))
					strcat(buf1, " (Writing mail)\r\n");
				else
					strcat(buf1, "\r\n");
			}
		}
		any = FALSE;
		for (d = descriptor_list; d; d = d->next)
		{
			if (STATE(d) == CON_PLAYING && GET_LEVEL(d->character) >= LVL_IMMORT &&
				PRF_FLAGGED(d->character, PRF_NOWIZ) &&
				CAN_SEE(ch, d->character))
			{
				if (!any)
				{
					strcat(buf1, "Gods offline:\r\n");
					any = TRUE;
				}
				sprintf(buf1 + strlen(buf1), "  %s\r\n", GET_NAME(d->character));
			}
		}
		send_to_char(buf1, ch);
		return;

	case '\\':
		++argument;
		break;

	default:
		break;
	}

	if (PRF_FLAGGED(ch, PRF_NOWIZ))
	{
		send_to_char("You are offline!\r\n", ch);
		return;
	}
	skip_spaces(&argument);

	if (!*argument)
	{
		send_to_char("Don't bother the gods like that!\r\n", ch);
		return;
	}
	if (level > LVL_IMMORT)
	{
		sprintf(buf1, "%s: <%d> %s%s\r\n", GET_NAME(ch), level,
			emote ? "<--- " : "", argument);
		sprintf(buf2, "Someone: <%d> %s%s\r\n", level, emote ? "<--- " : "",
			argument);
	}
	else
	{
		sprintf(buf1, "%s: %s%s\r\n", GET_NAME(ch), emote ? "<--- " : "",
			argument);
		sprintf(buf2, "Someone: %s%s\r\n", emote ? "<--- " : "", argument);
	}

	for (d = descriptor_list; d; d = d->next)
	{
		if ((STATE(d) == CON_PLAYING) && (GET_LEVEL(d->character) >= level) &&
			(!PRF_FLAGGED(d->character, PRF_NOWIZ)) &&
			(!PLR_FLAGGED(d->character, PLR_WRITING | PLR_MAILING))
			&& (d != ch->desc || !(PRF_FLAGGED(d->character, PRF_NOREPEAT))))
		{
			send_to_char(CCCYN(d->character, C_NRM), d->character);
			if (CAN_SEE(d->character, ch))
				send_to_char(buf1, d->character);
			else
				send_to_char(buf2, d->character);
			send_to_char(CCNRM(d->character, C_NRM), d->character);
		}
	}

	if (PRF_FLAGGED(ch, PRF_NOREPEAT))
		send_to_char(OK, ch);
}



ACMD(do_zreset)
{
	zone_rnum i;
	zone_vnum j;
	
	one_argument(argument, arg);
	if (!*arg)
	{
		send_to_char("You must specify a zone.\r\n", ch);
		return;
	}
	if (*arg == '*')
	{
		for (i = 0; i <= top_of_zone_table; i++)
			reset_zone(i);
		send_to_char("Reset world.\r\n", ch);
		sprintf(buf, "(GC) %s reset entire world.", GET_NAME(ch));
		mudlog(buf, NRM, MAX(LVL_GRGOD, GET_INVIS_LEV(ch)), TRUE);
		return;
	}
	else if (*arg == '.')
		i = ch->in_room->zone;
	else
	{
		j = atoi(arg);
		for (i = 0; i <= top_of_zone_table; i++)
			if (zone_table[i].number == j)
				break;
	}

	if (i >= 0 && i <= top_of_zone_table)
	{
		reset_zone(i);
		sprintf(buf, "Reset zone %d (#%d): %s.\r\n", i, zone_table[i].number,
			zone_table[i].name);
		send_to_char(buf, ch);
		sprintf(buf, "(GC) %s reset zone %d (%s)", GET_NAME(ch), i, zone_table[i].name);
		mudlog(buf, NRM, MAX(LVL_GRGOD, GET_INVIS_LEV(ch)), TRUE);
	}
	else
		send_to_char("Invalid zone number.\r\n", ch);
}


/*
 *  General fn for wizcommands of the sort: cmd <player>
 */
ACMD(do_wizutil)
{
	CHAR_DATA *vict;
	long result;
	
	one_argument(argument, arg);
	
	if (!*arg)
		send_to_char("Yes, but for whom?!?\r\n", ch);
	else if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)))
		send_to_char("There is no such player.\r\n", ch);
	else if (IS_NPC(vict))
		send_to_char("You can't do that to a mob!\r\n", ch);
	else if (GET_LEVEL(vict) > GET_LEVEL(ch))
		send_to_char("Hmmm...you'd better not.\r\n", ch);
	else
	{
		switch (subcmd)
		{
		case SCMD_REROLL:
			send_to_char("Rerolled...\r\n", ch);
			roll_real_abils(vict);
			log("(GC) %s has rerolled %s.", GET_NAME(ch), GET_NAME(vict));
			sprintf(buf, "New stats: Str %d, Int %d, Wis %d, Dex %d, Con %d, Cha %d\r\n",
				GET_STR(vict), GET_INT(vict), GET_WIS(vict),
				GET_DEX(vict), GET_CON(vict), GET_CHA(vict));
			send_to_char(buf, ch);
			break;

		case SCMD_PARDON:
			if (!PLR_FLAGGED(vict, PLR_THIEF | PLR_KILLER))
			{
				send_to_char("Your victim is not flagged.\r\n", ch);
				return;
			}
			REMOVE_BIT(PLR_FLAGS(vict), PLR_THIEF | PLR_KILLER);
			send_to_char("Pardoned.\r\n", ch);
			send_to_char("You have been pardoned by the Gods!\r\n", vict);
			sprintf(buf, "(GC) %s pardoned by %s", GET_NAME(vict), GET_NAME(ch));
			mudlog(buf, BRF, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
			break;

		case SCMD_NOTITLE:
			result = PLR_TOG_CHK(vict, PLR_NOTITLE);
			sprintf(buf, "(GC) Notitle %s for %s by %s.", ONOFF(result),
				GET_NAME(vict), GET_NAME(ch));
			mudlog(buf, NRM, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
			strcat(buf, "\r\n");
			send_to_char(buf, ch);
			break;

		case SCMD_SQUELCH:
			result = PLR_TOG_CHK(vict, PLR_NOSHOUT);
			sprintf(buf, "(GC) Squelch %s for %s by %s.", ONOFF(result),
				GET_NAME(vict), GET_NAME(ch));
			mudlog(buf, BRF, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
			strcat(buf, "\r\n");
			send_to_char(buf, ch);
			break;

		case SCMD_FREEZE:
			if (ch == vict)
			{
				send_to_char("Oh, yeah, THAT'S real smart...\r\n", ch);
				return;
			}
			if (PLR_FLAGGED(vict, PLR_FROZEN))
			{
				send_to_char("Your victim is already pretty cold.\r\n", ch);
				return;
			}
			SET_BIT(PLR_FLAGS(vict), PLR_FROZEN);
			GET_FREEZE_LEV(vict) = GET_LEVEL(ch);
			send_to_char("A bitter wind suddenly rises and drains every erg of heat from your body!\r\nYou feel frozen!\r\n", vict);
			send_to_char("Frozen.\r\n", ch);
			act("A sudden cold wind conjured from nowhere freezes $n!", FALSE, vict, 0, 0, TO_ROOM);
			sprintf(buf, "(GC) %s frozen by %s.", GET_NAME(vict), GET_NAME(ch));
			mudlog(buf, BRF, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
			break;

		case SCMD_THAW:
			if (!PLR_FLAGGED(vict, PLR_FROZEN))
			{
				send_to_char("Sorry, your victim is not morbidly encased in ice at the moment.\r\n", ch);
				return;
			}
			if (GET_FREEZE_LEV(vict) > GET_LEVEL(ch))
			{
				sprintf(buf, "Sorry, a level %d God froze %s... you can't unfreeze %s.\r\n",
					GET_FREEZE_LEV(vict), GET_NAME(vict), HMHR(vict));
				send_to_char(buf, ch);
				return;
			}
			sprintf(buf, "(GC) %s un-frozen by %s.", GET_NAME(vict), GET_NAME(ch));
			mudlog(buf, BRF, MAX(LVL_GOD, GET_INVIS_LEV(ch)), TRUE);
			REMOVE_BIT(PLR_FLAGS(vict), PLR_FROZEN);
			send_to_char("A fireball suddenly explodes in front of you, melting the ice!\r\nYou feel thawed.\r\n", vict);
			send_to_char("Thawed.\r\n", ch);
			act("A sudden fireball conjured from nowhere thaws $n!", FALSE, vict, 0, 0, TO_ROOM);
			break;

		case SCMD_UNAFFECT:
			if (vict->affected)
			{
				while (vict->affected)
					affect_remove(vict, vict->affected);
				send_to_char("There is a brief flash of light!\r\n"
					"You feel slightly different.\r\n", vict);
				send_to_char("All spells removed.\r\n", ch);
			}
			else
			{
				send_to_char("Your victim does not have any affections!\r\n", ch);
				return;
			}
			break;
		default:
			log("SYSERR: Unknown subcmd %d passed to do_wizutil (%s)", subcmd, __FILE__);
			return;
		}

		save_char(vict, NULL);
	}
}


/* single zone printing fn used by "show zone" so it's not repeated in the
   code 3 times ... -je, 4/6/93 */

void print_zone_to_buf(char *bufptr, zone_rnum zone)
{
	sprintf(bufptr, "%s%3d %-30.30s Age: %3d; Reset: %3d (%1d); Range: %5d-%5d\r\n",
		bufptr, zone_table[zone].number, zone_table[zone].name,
		zone_table[zone].age, zone_table[zone].lifespan,
		zone_table[zone].reset_mode,
		zone_table[zone].bot, zone_table[zone].top);
}


ACMD(do_show)
{
	CHAR_DATA *vict = NULL;
	OBJ_DATA *obj;
	DESCRIPTOR_DATA *d;
	int i, j, k, l, con;		/* i, j, k to specifics? */
	zone_rnum zrn;
	zone_vnum zvn;
	char self = 0;
	char field[MAX_INPUT_LENGTH], value[MAX_INPUT_LENGTH], birth[80];
	
	struct show_struct
	{
		const char *cmd;
		const char level;
	} fields[] =
	{
		{ "nothing",	0  },				/* 0 */
		{ "zones",	LVL_IMMORT },			/* 1 */
		{ "player",	LVL_GOD },
		{ "rent",	LVL_GOD },
		{ "stats",	LVL_IMMORT },
		{ "errors",	LVL_IMPL },			/* 5 */
		{ "death",	LVL_GOD },
		{ "godrooms",	LVL_GOD },
		{ "shops",	LVL_IMMORT },
		{ "houses",	LVL_GOD },
		{ "snoop",	LVL_GRGOD },			/* 10 */
		{ "specprocs",	LVL_GOD },
		{ "\n", 0 }
	};

	skip_spaces(&argument);
	
	if (!*argument)
	{
		strcpy(buf, "Show options:\r\n");
		for (j = 0, i = 1; fields[i].level; i++)
			if (fields[i].level <= GET_LEVEL(ch))
				sprintf(buf + strlen(buf), "%-15s%s", fields[i].cmd, (!(++j % 5) ? "\r\n" : ""));
		strcat(buf, "\r\n");
		send_to_char(buf, ch);
		return;
	}
	
	strcpy(arg, two_arguments(argument, field, value));

	for (l = 0; *(fields[l].cmd) != '\n'; l++)
	{
		if (!strncmp(field, fields[l].cmd, strlen(field)))
			break;
	}
	
	if (GET_LEVEL(ch) < fields[l].level)
	{
		send_to_char("You are not godly enough for that!\r\n", ch);
		return;
	}
	
	if (!strcmp(value, "."))
		self = 1;
	
	buf[0] = '\0';
	
	switch (l)
	{
	case 1:			/* zone */
		/* tightened up by JE 4/6/93 */
		if (self)
			print_zone_to_buf(buf, ch->in_room->zone);
		else if (*value && is_number(value))
		{
			for (zvn = atoi(value), zrn = 0; zone_table[zrn].number != zvn && zrn <= top_of_zone_table; zrn++);
			if (zrn <= top_of_zone_table)
				print_zone_to_buf(buf, zrn);
			else
			{
				send_to_char("That is not a valid zone.\r\n", ch);
				return;
			}
		}
		else
			for (zrn = 0; zrn <= top_of_zone_table; zrn++)
				print_zone_to_buf(buf, zrn);
		page_string(ch->desc, buf, TRUE);
		break;

	case 2:			/* player */
		if (!*value)
		{
			send_to_char("A name would help.\r\n", ch);
			return;
		}
		
		CREATE(vict, CHAR_DATA, 1);
		clear_char(vict);
		CREATE(vict->player_specials, PLAYER_SPECIAL, 1);
		if (load_char(value, vict) < 0)
		{
			send_to_char("There is no such player.\r\n", ch);
			free_char(vict);
			return;
		}
		sprintf(buf, "Player: %-12s (%s) [%2d %s %s]\r\n", GET_NAME(vict),
			genders[(int) GET_SEX(vict)], GET_LEVEL(vict),
			race_abbrevs[(int)GET_RACE(vict)],
			class_abbrevs[(int)GET_CLASS(vict)]);
		sprintf(buf + strlen(buf),
			"Exp: %-8d  Align: %-5d  Lessons: %-3d\r\n",
			GET_EXP(vict), GET_ALIGNMENT(vict), GET_PRACTICES(vict));

		//sprintf(buf, "Bank: [%9d], Coins: %d\r\n", GET_BANK_GOLD(vict), GET_GOLD(ch));

		strcpy(birth, ctime(&vict->player.time.birth));
		sprintf(buf + strlen(buf),
			"Started: %-20.16s  Last: %-20.16s  Played: %3dh %2dm\r\n",
			birth, ctime(&vict->player.time.logon),
			(vict->player.time.played / 3600),
			(vict->player.time.played / 60 % 60));
		send_to_char(buf, ch);
		free_char(vict);
		break;

	case 3:
		if (!*value)
		{
			send_to_char("A name would help.\r\n", ch);
			return;
		}
		send_to_char("Currently disabled, sorry.\r\n", ch);
		break;
	case 4:
		i = 0;
		j = 0;
		k = 0;
		con = 0;
		for (vict = character_list; vict; vict = vict->next)
		{
			if (IS_NPC(vict))
				j++;
			else if (CAN_SEE(ch, vict))
			{
				i++;
				if (vict->desc)
					con++;
			}
		}
		for (obj = first_object; obj; obj = obj->next)
			k++;
		strcpy(buf, "Current stats:\r\n");
		sprintf(buf + strlen(buf), "  %5d players in game  %5d connected\r\n",
			i, con);
		sprintf(buf + strlen(buf), "  %5d registered\r\n",
			top_of_p_table + 1);
		sprintf(buf + strlen(buf), "  %5d mobiles          %5d prototypes\r\n",
			j, top_of_mobt + 1);
		sprintf(buf + strlen(buf), "  %5d objects          %5d prototypes\r\n",
			k, top_of_objt + 1);
		sprintf(buf + strlen(buf), "  %5d rooms            %5d zones\r\n",
			top_of_world + 1, top_of_zone_table + 1);
		sprintf(buf + strlen(buf), " %6d wilderness rooms in memory\r\n",
			loaded_rooms);
		sprintf(buf + strlen(buf), "  %5d large bufs\r\n",
			buf_largecount);
		sprintf(buf + strlen(buf), "  %5d buf switches     %5d overflows\r\n",
			buf_switches, buf_overflows);
		send_to_char(buf, ch);
		break;
	case 5:
	case 6:
	case 7:
		send_to_char("Currently disabled, sorry.\r\n", ch);
		break;
	case 8:
		show_shops(ch, value);
		break;
	case 9:
		hcontrol_list_houses(ch);
		break;
	case 10:
		*buf = '\0';
		send_to_char("People currently snooping:\r\n", ch);
		send_to_char("--------------------------\r\n", ch);
		for (d = descriptor_list; d; d = d->next)
		{
			if (d->snooping == NULL || d->character == NULL)
				continue;
			if (STATE(d) != CON_PLAYING || GET_LEVEL(ch) < GET_LEVEL(d->character))
				continue;
			if (!CAN_SEE(ch, d->character) || IN_ROOM(d->character) == NULL)
				continue;
			sprintf(buf + strlen(buf), "%-10s - snooped by %s.\r\n",
				GET_NAME(d->snooping->character), GET_NAME(d->character));
		}
		send_to_char(*buf ? buf : "No one is currently snooping.\r\n", ch);
		break;

	case 11:
		send_to_char("List of Special Procedures:\r\n", ch);
		send_to_char("---------------------------\r\n", ch);
		list_spec_procs(ch);
		break;
	default:
		send_to_char("Sorry, I don't understand that.\r\n", ch);
		break;
	}
}


/***************** The do_set function ***********************************/

#define PC   1
#define NPC  2
#define BOTH 3

#define MISC	0
#define BINARY	1
#define NUMBER	2

#define SET_OR_REMOVE(flagset, flags) { \
	if (on) SET_BIT(flagset, flags); \
	else if (off) REMOVE_BIT(flagset, flags); }

#define RANGE(low, high) (value = MAX((low), MIN((high), (value))))


/* The set options available */
struct set_struct
{
  const char		*cmd;
  const char		level;
  const char		pcnpc;
  const char		type;
} set_fields[] =
{
   { "brief",		LVL_GOD, 	PC, 	BINARY },  /* 0 */
   { "invstart", 	LVL_GOD, 	PC, 	BINARY },  /* 1 */
   { "title",		LVL_GOD, 	PC, 	MISC },
   { "nosummon", 	LVL_GRGOD, 	PC, 	BINARY },
   { "maxhit",		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "maxmana", 	LVL_GRGOD, 	BOTH, 	NUMBER },  /* 5 */
   { "maxmove", 	LVL_GRGOD, 	BOTH, 	NUMBER },
   { "hit", 		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "mana",		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "move",		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "align",		LVL_GOD, 	BOTH, 	NUMBER },  /* 10 */
   { "str",			LVL_GRGOD, 	BOTH, 	NUMBER },
   { "stradd",		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "int", 		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "wis", 		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "dex", 		LVL_GRGOD, 	BOTH, 	NUMBER },  /* 15 */
   { "con", 		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "cha",			LVL_GRGOD, 	BOTH, 	NUMBER },
   { "ac", 			LVL_GRGOD, 	BOTH, 	NUMBER },
   { "gold",		LVL_GOD, 	BOTH, 	NUMBER },
   { "bank",		LVL_GOD, 	PC, 	NUMBER },  /* 20 */
   { "exp", 		LVL_GRGOD, 	BOTH, 	NUMBER },
   { "hitroll", 	LVL_GRGOD, 	BOTH, 	NUMBER },
   { "damroll", 	LVL_GRGOD, 	BOTH, 	NUMBER },
   { "invis",		LVL_IMPL, 	PC, 	NUMBER },
   { "nohassle", 	LVL_GRGOD, 	PC, 	BINARY },  /* 25 */
   { "frozen",		LVL_FREEZE, PC, 	BINARY },
   { "practices", 	LVL_GRGOD, 	PC, 	NUMBER },
   { "lessons", 	LVL_GRGOD, 	PC, 	NUMBER },
   { "drunk",		LVL_GRGOD, 	BOTH, 	MISC },
   { "hunger",		LVL_GRGOD, 	BOTH, 	MISC },    /* 30 */
   { "thirst",		LVL_GRGOD, 	BOTH, 	MISC },
   { "killer",		LVL_GOD, 	PC, 	BINARY },
   { "thief",		LVL_GOD, 	PC, 	BINARY },
   { "level",		LVL_IMPL, 	BOTH, 	NUMBER },
   { "room",		LVL_IMPL, 	BOTH, 	NUMBER },  /* 35 */
   { "roomflag", 	LVL_GRGOD, 	PC, 	BINARY },
   { "siteok",		LVL_GRGOD, 	PC, 	BINARY },
   { "deleted", 	LVL_IMPL, 	PC, 	BINARY },
   { "class",		LVL_GRGOD, 	BOTH, 	MISC },
   { "nowizlist", 	LVL_GOD, 	PC, 	BINARY },  /* 40 */
   { "quest",		LVL_GOD, 	PC, 	BINARY },
   { "loadroom", 	LVL_GRGOD, 	PC, 	MISC },
   { "color",		LVL_GOD, 	PC, 	BINARY },
   { "idnum",		LVL_IMPL, 	PC, 	NUMBER },
   { "passwd",		LVL_IMPL, 	PC, 	MISC },    /* 45 */
   { "nodelete", 	LVL_GOD, 	PC, 	BINARY },
   { "sex", 		LVL_GRGOD, 	BOTH, 	MISC },
   { "age",			LVL_GRGOD,	BOTH,	NUMBER },
   { "height",		LVL_GOD,	BOTH,	NUMBER },
   { "weight",		LVL_GOD,	BOTH,	NUMBER },  /* 50 */
   { "race",		LVL_GRGOD,	BOTH,	MISC },
   { "\n", 0, BOTH, MISC }
};


int perform_set(CHAR_DATA *ch, CHAR_DATA *vict, int mode, char *val_arg)
{
	ROOM_DATA *rnum;
	room_vnum rvnum;
	char output[MAX_STRING_LENGTH];
	int i, on = 0, off = 0, value = 0;
	
	/* Check to make sure all the levels are correct */
	if (GET_LEVEL(ch) != LVL_IMPL) 
	{
		if (!IS_NPC(vict) && GET_LEVEL(ch) <= GET_LEVEL(vict) && vict != ch)
		{
			send_to_char("Maybe that's not such a great idea...\r\n", ch);
			return (0);
		}
	}

	if (GET_LEVEL(ch) < set_fields[mode].level)
	{
		send_to_char("You are not godly enough for that!\r\n", ch);
		return (0);
	}
	
	/* Make sure the PC/NPC is correct */
	if (IS_NPC(vict) && !(set_fields[mode].pcnpc & NPC))
	{
		send_to_char("You can't do that to a beast!\r\n", ch);
		return (0);
	}
	else if (!IS_NPC(vict) && !(set_fields[mode].pcnpc & PC))
	{
		send_to_char("That can only be done to a beast!\r\n", ch);
		return (0);
	}
	
	/* Find the value of the argument */
	if (set_fields[mode].type == BINARY)
	{
		if (!strcmp(val_arg, "on") || !strcmp(val_arg, "yes"))
			on = 1;
		else if (!strcmp(val_arg, "off") || !strcmp(val_arg, "no"))
			off = 1;
		if (!(on || off))
		{
			send_to_char("Value must be 'on' or 'off'.\r\n", ch);
			return (0);
		}
		sprintf(output, "%s %s for %s.", set_fields[mode].cmd, ONOFF(on),
			GET_NAME(vict));
	}
	else if (set_fields[mode].type == NUMBER)
	{
		value = atoi(val_arg);
		sprintf(output, "%s's %s set to %d.", GET_NAME(vict),
			set_fields[mode].cmd, value);
	}
	else
		strcpy(output, "Okay.");  /* can't use OK macro here 'cause of \r\n */
	
	switch (mode)
	{
	case 0:
		SET_OR_REMOVE(PRF_FLAGS(vict), PRF_BRIEF);
		break;
	case 1:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_INVSTART);
		break;
	case 2:
		set_title(vict, val_arg);
		sprintf(output, "%s's title is now: %s", GET_NAME(vict), GET_TITLE(vict));
		break;
	case 3:
		SET_OR_REMOVE(PRF_FLAGS(vict), PRF_SUMMONABLE);
		sprintf(output, "Nosummon %s for %s.\r\n", ONOFF(!on), GET_NAME(vict));
		break;
	case 4:
		vict->points.max_hit = RANGE(1, 5000);
		affect_total(vict);
		break;
	case 5:
		vict->points.max_mana = RANGE(1, 5000);
		affect_total(vict);
		break;
	case 6:
		vict->points.max_move = RANGE(1, 5000);
		affect_total(vict);
		break;
	case 7:
		vict->points.hit = RANGE(-9, vict->points.max_hit);
		affect_total(vict);
		break;
	case 8:
		vict->points.mana = RANGE(0, vict->points.max_mana);
		affect_total(vict);
		break;
	case 9:
		vict->points.move = RANGE(0, vict->points.max_move);
		affect_total(vict);
		break;
	case 10:
		GET_ALIGNMENT(vict) = RANGE(-1000, 1000);
		affect_total(vict);
		break;
	case 11:
		if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD)
			RANGE(3, 25);
		else
			RANGE(3, 18);
		vict->real_abils.str = value;
		affect_total(vict);
		break;
	case 12:
		break;
	case 13:
		if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD)
			RANGE(3, 25);
		else
			RANGE(3, 18);
		vict->real_abils.intel = value;
		affect_total(vict);
		break;
	case 14:
		if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD)
			RANGE(3, 25);
		else
			RANGE(3, 18);
		vict->real_abils.wis = value;
		affect_total(vict);
		break;
	case 15:
		if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD)
			RANGE(3, 25);
		else
			RANGE(3, 18);
		vict->real_abils.dex = value;
		affect_total(vict);
		break;
	case 16:
		if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD)
			RANGE(3, 25);
		else
			RANGE(3, 18);
		vict->real_abils.con = value;
		affect_total(vict);
		break;
	case 17:
		if (IS_NPC(vict) || GET_LEVEL(vict) >= LVL_GRGOD)
			RANGE(3, 25);
		else
			RANGE(3, 18);
		vict->real_abils.cha = value;
		affect_total(vict);
		break;
	case 18:
		vict->points.armor = RANGE(-100, 100);
		affect_total(vict);
		break;
	case 19:
		add_gold(vict, RANGE(0, 100000));
		break;
	case 20:
		GET_BANK_GOLD(vict) = RANGE(0, 100000000);
		break;
	case 21:
		vict->points.exp = RANGE(0, 50000000);
		break;
	case 22:
		vict->points.hitroll = RANGE(-20, 20);
		affect_total(vict);
		break;
	case 23:
		vict->points.damroll = RANGE(-20, 20);
		affect_total(vict);
		break;
	case 24:
		if (GET_LEVEL(ch) < LVL_IMPL && ch != vict)
		{
			send_to_char("You aren't godly enough for that!\r\n", ch);
			return (0);
		}
		GET_INVIS_LEV(vict) = RANGE(0, GET_LEVEL(vict));
		break;
	case 25:
		if (GET_LEVEL(ch) < LVL_IMPL && ch != vict)
		{
			send_to_char("You aren't godly enough for that!\r\n", ch);
			return (0);
		}
		SET_OR_REMOVE(PRF_FLAGS(vict), PRF_NOHASSLE);
		break;
	case 26:
		if (ch == vict && on)
		{
			send_to_char("Better not -- could be a long winter!\r\n", ch);
			return (0);
		}
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_FROZEN);
		break;
	case 27:
	case 28:
		GET_PRACTICES(vict) = RANGE(0, 100);
		break;
	case 29:
	case 30:
	case 31:
		if (!str_cmp(val_arg, "off"))
		{
			GET_COND(vict, (mode - 29)) = -1; /* warning: magic number here */
			sprintf(output, "%s's %s now off.", GET_NAME(vict), set_fields[mode].cmd);
		}
		else if (is_number(val_arg))
		{
			value = atoi(val_arg);
			RANGE(0, 24);
			GET_COND(vict, (mode - 29)) = value; /* and here too */
			sprintf(output, "%s's %s set to %d.", GET_NAME(vict),
				set_fields[mode].cmd, value);
		}
		else
		{
			send_to_char("Must be 'off' or a value from 0 to 24.\r\n", ch);
			return (0);
		}
		break;
	case 32:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_KILLER);
		break;
	case 33:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_THIEF);
		break;
	case 34:
		if (value > GET_LEVEL(ch) || value > LVL_IMPL)
		{
			send_to_char("You can't do that.\r\n", ch);
			return (0);
		}
		RANGE(0, LVL_IMPL);
		vict->player.level = value;
		break;
	case 35:
		if (!(rnum = get_room(value)))
		{
			send_to_char("No room exists with that number.\r\n", ch);
			return (0);
		}
		if (IN_ROOM(vict) != NULL)	/* Another Eric Green special. */
			char_from_room(vict);
		char_to_room(vict, rnum);
		break;
	case 36:
		SET_OR_REMOVE(PRF_FLAGS(vict), PRF_ROOMFLAGS);
		break;
	case 37:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_SITEOK);
		break;
	case 38:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_DELETED);
		break;
	case 39:
		if ((i = parse_class(*val_arg)) == CLASS_UNDEFINED)
		{
			send_to_char("That is not a class.\r\n", ch);
			return (0);
		}
		GET_CLASS(vict) = i;
		break;
	case 40:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_NOWIZLIST);
		break;
	case 41:
		SET_OR_REMOVE(PRF_FLAGS(vict), PRF_QUEST);
		break;
	case 42:
		if (!str_cmp(val_arg, "off"))
			REMOVE_BIT(PLR_FLAGS(vict), PLR_LOADROOM);
		else if (is_number(val_arg))
		{
			rvnum = atoi(val_arg);
			if (get_room(rvnum) != NULL)
			{
				SET_BIT(PLR_FLAGS(vict), PLR_LOADROOM);
				GET_LOADROOM(vict) = rvnum;
				sprintf(output, "%s will enter at room #%d.", GET_NAME(vict),
					GET_LOADROOM(vict));
			}
			else
			{
				send_to_char("That room does not exist!\r\n", ch);
				return (0);
			}
		}
		else
		{
			send_to_char("Must be 'off' or a room's virtual number.\r\n", ch);
			return (0);
		}
		break;
	case 43:
		SET_OR_REMOVE(PRF_FLAGS(vict), (PRF_COLOR_1 | PRF_COLOR_2));
		break;
	case 44:
		if (GET_IDNUM(ch) != 1 || !IS_NPC(vict))
			return (0);
		GET_IDNUM(vict) = value;
		break;
	case 45:
		if (GET_IDNUM(ch) > 1)
		{
			send_to_char("Please don't use this command, yet.\r\n", ch);
			return (0);
		}
		if (GET_LEVEL(vict) >= LVL_GRGOD)
		{
			send_to_char("You cannot change that.\r\n", ch);
			return (0);
		}
		strncpy(GET_PASSWD(vict), CRYPT(val_arg, GET_NAME(vict)), MAX_PWD_LENGTH);
		*(GET_PASSWD(vict) + MAX_PWD_LENGTH) = '\0';
		sprintf(output, "Password changed to '%s'.", val_arg);
		break;
	case 46:
		SET_OR_REMOVE(PLR_FLAGS(vict), PLR_NODELETE);
		break;
	case 47:
		if ((i = search_block(val_arg, genders, FALSE)) < 0)
		{
			send_to_char("Must be 'male', 'female', or 'neutral'.\r\n", ch);
			return (0);
		}
		GET_SEX(vict) = i;
		break;
	case 48:	/* set age */
		if (value < 2 || value > 200)		/* Arbitrary limits. */
		{
			send_to_char("Ages 2 to 200 accepted.\r\n", ch);
			return (0);
		}
		/*
		 * NOTE: May not display the exact age specified due to the integer
		 * division used elsewhere in the code.  Seems to only happen for
		 * some values below the starting age (17) anyway. -gg 5/27/98
		 */
		vict->player.time.birth = time(0) - ((value - 17) * SECS_PER_MUD_YEAR);
		break;
		
	case 49:	/* Blame/Thank Rick Glover. :) */
		GET_HEIGHT(vict) = value;
		affect_total(vict);
		break;
		
	case 50:
		GET_WEIGHT(vict) = value;
		affect_total(vict);
		break;
		
	case 51:
		if ((i = parse_race(*val_arg)) == RACE_UNDEFINED)
		{
			send_to_char("That is not a valid race.\r\n", ch);
			return (0);
		}
		GET_RACE(vict) = i;
		break;

	default:
		send_to_char("Can't set that!\r\n", ch);
		return (0);
	}
  
	strcat(output, "\r\n");
	send_to_char(CAP(output), ch);
	return (1);
}


ACMD(do_set)
{
	CHAR_DATA *vict = NULL, *cbuf = NULL;
	char field[MAX_INPUT_LENGTH], name[MAX_INPUT_LENGTH],
		val_arg[MAX_INPUT_LENGTH];
	int mode, len, player_i = 0, retval;
	char is_file = 0, is_player = 0;
	
	half_chop(argument, name, buf);
	
	if (!strcmp(name, "file"))
	{
		is_file = 1;
		half_chop(buf, name, buf);
	}
	else if (!str_cmp(name, "player"))
	{
		is_player = 1;
		half_chop(buf, name, buf);
	}
	else if (!str_cmp(name, "mob"))
		half_chop(buf, name, buf);
	
	half_chop(buf, field, buf);
	strcpy(val_arg, buf);
	
	if (!*name || !*field)
	{
		send_to_char("Usage: set <victim> <field> <value>\r\n", ch);
		return;
	}
	
	/* find the target */
	if (!is_file)
	{
		if (is_player)
		{
			if (!(vict = get_player_vis(ch, name, NULL, FIND_CHAR_WORLD)))
			{
				send_to_char("There is no such player.\r\n", ch);
				return;
			}
		}
		else			/* is_mob */
		{
			if (!(vict = get_char_vis(ch, name, NULL, FIND_CHAR_WORLD)))
			{
				send_to_char("There is no such creature.\r\n", ch);
				return;
			}
		}
	}
	else if (is_file)
	{
		/* try to load the player off disk */
		CREATE(cbuf, CHAR_DATA, 1);
		clear_char(cbuf);
		CREATE(cbuf->player_specials, PLAYER_SPECIAL, 1);
		if ((player_i = load_char(name, cbuf)) > -1)
		{
			if (GET_LEVEL(cbuf) >= GET_LEVEL(ch))
			{
				free_char(cbuf);
				send_to_char("Sorry, you can't do that.\r\n", ch);
				return;
			}
			vict = cbuf;
		}
		else
		{
			free_char(cbuf);
			send_to_char("There is no such player.\r\n", ch);
			return;
		}
	}
	
	/* find the command in the list */
	len = strlen(field);
	for (mode = 0; *(set_fields[mode].cmd) != '\n'; mode++)
		if (!strncmp(field, set_fields[mode].cmd, len))
			break;
		
	/* perform the set */
	retval = perform_set(ch, vict, mode, val_arg);
		
	/* save the character if a change was made */
	if (retval)
	{
		if (!is_file && !IS_NPC(vict))
			save_char(vict, NULL);
		if (is_file)
		{
			GET_PFILEPOS(cbuf) = player_i;
			save_char(cbuf, get_room(GET_LOADROOM(cbuf)));
			send_to_char("Saved in file.\r\n", ch);
		}
	}
		
	/* free the memory if we allocated it earlier */
	if (is_file)
		free_char(cbuf);
}


/* ************************************************************** */

void vlist_wear_pos(int wearpos, CHAR_DATA *ch)
{
	char sbuf[MAX_STRING_LENGTH];
	int nr, found = 0;

	*sbuf = '\0';
	for (nr = 0; nr <= top_of_objt; nr++)
	{
		if (CAN_WEAR(&obj_proto[nr], wearpos))
			sprintf(sbuf+strlen(sbuf), "%3d. [%5d] %s\r\n", ++found,
				obj_index[nr].vnum, obj_proto[nr].short_description);
	}

	if ( *sbuf )
		page_string(ch->desc, sbuf, 1);
}

void vlist_obj_type(int type, CHAR_DATA *ch)
{
	char sbuf[MAX_STRING_LENGTH];
	int nr, found = 0;

	*sbuf = '\0';
	for (nr = 0; nr <= top_of_objt; nr++)
	{
		if (GET_OBJ_TYPE(&obj_proto[nr]) == type)
			sprintf(sbuf+strlen(sbuf), "%3d. [%5d] %s\r\n", ++found,
				obj_index[nr].vnum, obj_proto[nr].short_description);
	}

	if ( *sbuf )
		page_string(ch->desc, sbuf, 1);
}

ACMD(do_vlist)
{
	one_argument(argument, buf);
	
	if (!*buf)
	{
		send_to_char(
			"&b&7Usage: vlist <wear position>&0\r\n"
			"Wear positions are:\r\n"
			"finger  neck   body   head   legs    feet    hands\r\n"
			"shield  arms   about  waist  wrist   wield   hold\r\n"
			"------------------------------------------------------\r\n"
			"&b&7Usage: vlist <item type>&0\r\n"
			"Item types are:\r\n"
			"light   scroll wand   staff  treasure  armor\r\n"
			"potion  worn   other  trash  container liquid\r\n"
			"key     food   money  pen    boat      fountain\r\n", ch);
		
		return;
	}

	if		(!str_cmp(buf, "finger"))		vlist_wear_pos(ITEM_WEAR_FINGER, ch);
	else if (!str_cmp(buf, "neck"))			vlist_wear_pos(ITEM_WEAR_NECK, ch);
	else if (!str_cmp(buf, "body"))			vlist_wear_pos(ITEM_WEAR_BODY, ch);
	else if (!str_cmp(buf, "head"))			vlist_wear_pos(ITEM_WEAR_HEAD, ch);
	else if (!str_cmp(buf, "legs"))			vlist_wear_pos(ITEM_WEAR_LEGS, ch);
	else if (!str_cmp(buf, "feet"))			vlist_wear_pos(ITEM_WEAR_FEET, ch);
	else if (!str_cmp(buf, "hands"))		vlist_wear_pos(ITEM_WEAR_HANDS, ch);
	else if (!str_cmp(buf, "arms"))			vlist_wear_pos(ITEM_WEAR_ARMS, ch);
	else if (!str_cmp(buf, "shield"))		vlist_wear_pos(ITEM_WEAR_SHIELD, ch);
	else if (!str_cmp(buf, "about"))		vlist_wear_pos(ITEM_WEAR_ABOUT, ch);
	else if (!str_cmp(buf, "waist"))		vlist_wear_pos(ITEM_WEAR_WAIST, ch);
	else if (!str_cmp(buf, "wrist"))		vlist_wear_pos(ITEM_WEAR_WRIST, ch);
	else if (!str_cmp(buf, "wield"))		vlist_wear_pos(ITEM_WEAR_WIELD, ch);
	else if (!str_cmp(buf, "hold"))			vlist_wear_pos(ITEM_WEAR_HOLD, ch);
	else if (!str_cmp(buf, "light"))		vlist_obj_type(ITEM_LIGHT, ch);
	else if (!str_cmp(buf, "scroll"))		vlist_obj_type(ITEM_SCROLL, ch);
	else if (!str_cmp(buf, "wand"))			vlist_obj_type(ITEM_WAND, ch);
	else if (!str_cmp(buf, "staff"))		vlist_obj_type(ITEM_STAFF, ch);
	else if (!str_cmp(buf, "treasure"))		vlist_obj_type(ITEM_TREASURE, ch);
	else if (!str_cmp(buf, "armor"))		vlist_obj_type(ITEM_ARMOR, ch);
	else if (!str_cmp(buf, "potion"))		vlist_obj_type(ITEM_POTION, ch);
	else if (!str_cmp(buf, "worn"))			vlist_obj_type(ITEM_WORN, ch);
	else if (!str_cmp(buf, "other"))		vlist_obj_type(ITEM_OTHER, ch);
	else if (!str_cmp(buf, "trash"))		vlist_obj_type(ITEM_TRASH, ch);
	else if (!str_cmp(buf, "container"))	vlist_obj_type(ITEM_CONTAINER, ch);
	else if (!str_cmp(buf, "liquid"))		vlist_obj_type(ITEM_DRINKCON, ch);
	else if (!str_cmp(buf, "key"))			vlist_obj_type(ITEM_KEY, ch);
	else if (!str_cmp(buf, "food"))			vlist_obj_type(ITEM_FOOD, ch);
	else if (!str_cmp(buf, "money"))		vlist_obj_type(ITEM_MONEY, ch);
	else if (!str_cmp(buf, "pen"))			vlist_obj_type(ITEM_PEN, ch);
	else if (!str_cmp(buf, "boat"))			vlist_obj_type(ITEM_BOAT, ch);
	else if (!str_cmp(buf, "fountain"))		vlist_obj_type(ITEM_FOUNTAIN, ch);
	else
	{
		send_to_char(
			"Possible wear positions are:\r\n"
			"finger  neck   body   head   legs    feet    hands\r\n"
			"shield  arms   about  waist  wrist   wield   hold\r\n"
			"------------------------------------------------------\r\n"
			"Possible item types are:\r\n"
			"light   scroll  wand   staff  treasure  armor\r\n"
			"potion  worn    other  trash  container liquid\r\n"
			"key     food    money  pen    boat      fountain\r\n", ch);
	}
}

/* ************************************************************** */

/* array for stats to search for mobs */
const char *mob_search[] =
{
  "level",
  "damroll",
  "hitroll",
  "hitpoints",
  "\n",
};

/* array for stats to search for objects */
const char *obj_search[] =
{
  "level",
  "perm",
  "type",
  "\n",
};

/* main search for mobs */
int search_mobile(CHAR_DATA *ch, int mode, int value)
{
	int nr, found = 0, hp;
	
	switch (mode)
	{
	case 0:		/* level */
		for (nr = 0; nr <= top_of_mobt; nr++)
		{
			if (mob_proto[nr].player.level == value)
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, mob_index[nr].vnum,
					mob_proto[nr].player.short_descr);
				send_to_char(buf, ch);
			}
		}
		break;

	case 1:		/* damroll */
		for (nr = 0; nr <= top_of_mobt; nr++)
		{
			if (mob_proto[nr].points.damroll >= value)
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, mob_index[nr].vnum,
					mob_proto[nr].player.short_descr);
				send_to_char(buf, ch);
			}
		}
		break;

	case 2:		/* hitroll */
		for (nr = 0; nr <= top_of_mobt; nr++)
		{
			if (mob_proto[nr].points.hitroll >= value)
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, mob_index[nr].vnum,
					mob_proto[nr].player.short_descr);
				send_to_char(buf, ch);
			}
		}
		break;

	case 3:		/* hitpoints */
		for (nr = 0; nr <= top_of_mobt; nr++)
		{
			hp = (mob_proto[nr].points.hit * mob_proto[nr].points.mana) + mob_proto[nr].points.move;

			if (hp >= value)
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, mob_index[nr].vnum,
					mob_proto[nr].player.short_descr);
				send_to_char(buf, ch);
			}
		}
		break;

	default:
		send_to_char("Can't search on that!\r\n", ch);
		return (0);
	}
	
	return (found);
}

/*
 * some funcs to display the list of available perm affects,
 * spells, and types - used with the 
 * arrays found in constants.c
 */

/* permanent affects */
void list_perm_affects(CHAR_DATA *ch)
{
	int counter, columns = 0;
	
	for (counter = 0; counter < NUM_AFF_FLAGS; counter++)
	{
		sprintf(buf, "%d) %-20.20s %s", counter, affected_bits[counter], !(++columns % 2) ? "\r\n" : "");
		send_to_char(buf, ch);
	}
}


/* item types */
void list_obj_types(CHAR_DATA *ch)
{
	int counter, columns = 0;
	
	for (counter = 0; counter < NUM_ITEM_TYPES; counter++)
	{
		sprintf(buf, "%d) %-20.20s %s", counter, item_types[counter], !(++columns % 2) ? "\r\n" : "");
		send_to_char(buf, ch);
	}
}

/* main search for objects */
int search_object(CHAR_DATA *ch, int mode, int value)
{
	int nr, found = 0;
	
	switch (mode)
	{
	case 0:  /* level */
		for (nr = 0; nr <= top_of_objt; nr++)
		{
			if (obj_proto[nr].obj_flags.level == value)
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, obj_index[nr].vnum, 
					obj_proto[nr].short_description);
				send_to_char(buf, ch);
			}
		}
		break;

	case 1:  /* perm */
		if (value == -1)
		{
			list_perm_affects(ch);
			send_to_char("\r\nSearch with the given value.\r\n", ch);
			return (0);
		}

		for (nr = 0; nr <= top_of_objt; nr++)
		{
			if (obj_proto[nr].obj_flags.bitvector == (1 << value))
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, obj_index[nr].vnum, 
					obj_proto[nr].short_description);
				send_to_char(buf, ch);
			}
		}
		break;

	case 2:  /* type */
		if (value == -1)
		{
			list_obj_types(ch);
			send_to_char("\r\nSearch with the given value.\r\n", ch);
			return (0);
		}
		for (nr = 0; nr <= top_of_objt; nr++)
		{
			if (obj_proto[nr].obj_flags.type_flag == value)
			{
				sprintf(buf, "%3d. [%5d] %s\r\n", ++found, obj_index[nr].vnum, 
					obj_proto[nr].short_description);
				send_to_char(buf, ch);
			}
		}
		break;

	default:
		send_to_char("Can't search on that!\r\n", ch);
		return (0);
	}
	
	return (found);
}

/* the main function for find */
ACMD(do_find)
{
	char stat[MAX_INPUT_LENGTH], type[MAX_INPUT_LENGTH], val[MAX_INPUT_LENGTH];
	int mode, value = -1, mobobj, columns = 0;
	
	half_chop(argument, type, argument);
	half_chop(argument, stat, argument);
	
	if (!*type)
	{
		send_to_char("Usage: find mob|obj <stat> <value>\r\n", ch);
		return;
	}
	
	/* Make sure we're looking for a mob or an object */
	if (is_abbrev(type, "mobile"))
		mobobj = 1;
	else if (is_abbrev(type, "object")) 
		mobobj = 2;
	else
	{
		send_to_char("That'll have to be either 'mob' or 'obj'.\r\n", ch);
		return;
	}
	
	/* get the value, if given */
	half_chop(argument, val, argument);
	
	if (*val)
		value = atoi(val);
	else
		value = -1;
	
	/* find the stat in the list, and perform the search */
	switch (mobobj)
	{
	case 1:
		if (!*stat)	/* list the searchable fields */
		{
			send_to_char("Search for the following:\r\n", ch);
			for (mode = 0; strcmp(mob_search[mode], "\n"); mode++)
			{
				sprintf(buf, "%s %s", mob_search[mode], !(++columns % 5) ? "\r\n" : " ");
				send_to_char(buf, ch);
			}
			send_to_char("\r\nUsage: find mob|obj <stat> <value>\r\n", ch);
			return;
		}
		/* find the stat */
		for (mode = 0; strcmp(mob_search[mode], "\n"); mode++)
			if (is_abbrev(stat, mob_search[mode]))
				break;

		sprintf(buf, "Searching mobs for %s (%d).\r\n", mob_search[mode], value);
		send_to_char(buf, ch);
		
		/* display results */
		if (!search_mobile(ch, mode, value))
			send_to_char("No mobs found with the given parameters.\r\n", ch);

		break;

	case 2:
		if (!*stat)		/* list the searchable fields */
		{
			send_to_char("Search for the following:\r\n", ch);
			for (mode = 0; strcmp(obj_search[mode], "\n"); mode++)
			{
				sprintf(buf, "%s %s", obj_search[mode], !(++columns % 5) ? "\r\n" : " ");
				send_to_char(buf, ch);
			}
			send_to_char("\r\nUsage: find mob|obj <stat> <value>\r\n", ch);
			return;
		}
		/* find the stat */
		for (mode = 0; strcmp(obj_search[mode], "\n"); mode++)
			if (is_abbrev(stat, obj_search[mode]))
				break;
		
		sprintf(buf, "Searching objs for %s (%d).\r\n", obj_search[mode], value);
		send_to_char(buf, ch);
		
		/* display results */
		if (!search_object(ch, mode, value))
			send_to_char("No objects found with the given parameters.\r\n", ch);

		break;

	default:
		/* We should never get here... */
		log("SYSERR: default case reached in search.c, ACMD(do_find)");
		return;
	}
}

ACMD(do_zoneconn)
{
	FILE *fp;
	ROOM_DATA *pRoom;
	EXIT_DATA *pExit;
	int iHash;

	fp = fopen("zoneconn3.txt", "w");

	for (iHash = 0; iHash < ROOM_HASH; iHash++)
	{
		for (pRoom = World[iHash]; pRoom; pRoom = pRoom->next)
		{
			if (IS_WILD(pRoom))		continue;
			if (IS_BUILDING(pRoom))	continue;
			if (IS_SHIP(pRoom))		continue;
			if (IS_FERRY(pRoom))	continue;

			for (pExit = pRoom->first_exit; pExit; pExit = pExit->next)
			{

				if (!pExit->to_room)				continue;
				if (IS_WILD(pExit->to_room))		continue;
				if (IS_BUILDING(pExit->to_room))	continue;
				if (IS_SHIP(pExit->to_room))		continue;
				if (IS_FERRY(pExit->to_room))		continue;

				if (pExit->to_room->zone != pRoom->zone)
					fprintf(fp,
						//"Zone [%d] - Room [%d] exit on Room [%d], Zone [%d], Dir [%s]\r\n",
						"%d;%d;%d;%d;%s\n",
						zone_table[pRoom->zone].number, pRoom->number,
						pExit->to_room->number, zone_table[pExit->to_room->zone].number,
						dirs[pExit->vdir]);
			}
		}
	}

	fclose(fp);
}