empiremud/cnf/
empiremud/doc/
empiremud/lib/boards/
empiremud/lib/etc/
empiremud/lib/misc/
empiremud/lib/plralias/F-J/
empiremud/lib/plralias/K-O/
empiremud/lib/plralias/P-T/
empiremud/lib/plralias/U-Z/
empiremud/lib/plrobjs/
empiremud/lib/plrobjs/F-J/
empiremud/lib/plrobjs/K-O/
empiremud/lib/plrobjs/P-T/
empiremud/lib/plrobjs/U-Z/
empiremud/lib/world/
empiremud/lib/world/mob/
empiremud/lib/world/obj/
empiremud/log/
/* ************************************************************************
*   File: act.informative.c                              EmpireMUD AD 1.0 *
*  Usage: Player-level commands of an informative nature                  *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Code base by Paul Clarke.  EmpireMUD Project, a tbgMUD Production.     *
*  Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson.        *
*                                                                         *
*  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 "utils.h"
#include "empire.h"
#include "skills.h"

/* extern variables */
extern const char *where[];
extern const char *sector_name[];
extern const char *dirs[];
extern const char *month_name[];
extern struct tribe_data tribe[];
extern const char *auspice[];
extern const char *breed[];


/* Outputs Attributes in a White Wolf manner, ex. Strength: **** */
void display_attributes(Creature ch, Creature to, bool real) {
	sbyte att[9];
	int i, j;

	const char *att_types[] = { "Strength", "Charisma", "Perception", "Dexterity", "Manipulation", "Intelligence", "Stamina", "Appearance", "Wits" };

	if (real) {
		att[0] = ch->real_abils.strength;
		att[1] = ch->real_abils.charisma;
		att[2] = ch->real_abils.perception;
		att[3] = ch->real_abils.dexterity;
		att[4] = ch->real_abils.manipulation;
		att[5] = ch->real_abils.intelligence;
		att[6] = ch->real_abils.stamina;
		att[7] = ch->real_abils.appearance;
		att[8] = ch->real_abils.wits;
		}
	else {
		att[0] = ch->aff_abils.strength;
		att[1] = ch->aff_abils.charisma;
		att[2] = ch->aff_abils.perception;
		att[3] = ch->aff_abils.dexterity;
		att[4] = ch->aff_abils.manipulation;
		att[5] = ch->aff_abils.intelligence;
		att[6] = ch->aff_abils.stamina;
		att[7] = ch->aff_abils.appearance;
		att[8] = ch->aff_abils.wits;
		}

	/* The header */
	msg_to_char(to, "        Physical                   Social                      Mental\r\n ");

	for (i = 0; i < 9; i++) {
		sprintf(buf, " %-14.14s", att_types[i]);
		for (j = 1; j <= 10; j++) {
			if (att[i] >= j)
				strcat(buf, "*");
			else
				strcat(buf, " ");
			}

		/* Terminate at three columns */
		if (!((i + 1) % 3))
			strcat(buf, "\r\n ");

		msg_to_char(to, buf);
		}
	}


/* Outputs Abilities in a White Wolf manner */
void display_abilities(Creature ch, Creature to) {
	extern const char *talents[];
	extern const char *skills[];
	extern const char *knowledges[];
	int i, j;

	/* Header */
	msg_to_char(to, "        Talents                    Skills                    Knowledges\r\n ");

	for (i = 0; i < NUM_ABILITIES; i++) {
		*buf = '\0';
		sprintf(buf + strlen(buf), " %-14.14s", talents[i]);
		for (j = 1; j <= 10; j++) {
			if (GET_TALENT(ch, i) >= j)
				strcat(buf, "*");
			else
				strcat(buf, " ");
			}

		sprintf(buf + strlen(buf), " %-14.14s", skills[i]);
		for (j = 1; j <= 10; j++) {
			if (GET_SKILL(ch, i) >= j)
				strcat(buf, "*");
			else
				strcat(buf, " ");
			}

		sprintf(buf + strlen(buf), " %-14.14s", knowledges[i]);
		for (j = 1; j <= 10; j++) {
			if (GET_KNOWLEDGE(ch, i) >= j)
				strcat(buf, "*");
			else
				strcat(buf, " ");
			}
		strcat(buf, "\r\n ");
		msg_to_char(to, buf);
		}
	}


char *corpse_desc(Object obj, Creature ch) {
	char name[MAX_INPUT_LENGTH];
	static char out[MAX_INPUT_LENGTH];

	if (GET_OBJ_VAL(obj, 0) >= 0 && real_mobile(GET_OBJ_VAL(obj, 0)) != NOTHING)
		strcpy(name, GET_MOB_NAME_BY_PROTO(real_mobile(GET_OBJ_VAL(obj, 0))));
	else
		strcpy(name, "a person");

	sprintf(out, "the corpse of %s", name);

	return out;
	}


char *get_obj_desc(Object obj, Creature ch, int mode) {
	extern int fill_word(char *argument);
	extern int reserved_word(char *argument);

	static char output[MAX_STRING_LENGTH];
	char sdesc[MAX_STRING_LENGTH], b1[MAX_STRING_LENGTH], b2[MAX_STRING_LENGTH];

	*output = '\0';

	/* sdesc will be empty unless the short desc is modified */
	*sdesc = '\0';

	if (IS_OBJ_STAT(obj, ITEM_SUPERIOR)) {
		if (!*sdesc)
			strcpy(sdesc, obj->short_description);
		half_chop(sdesc, b1, b2);
		if (fill_word(b1) || reserved_word(b1)) {
			if (!str_cmp(b1, "an"))
				strcpy(b1, "a");
			sprintf(sdesc, "%s superior %s", b1, b2);
			}
		else {
			strcpy(b1, sdesc);
			sprintf(sdesc, "superior %s", b1);
			}
		}
	if (IS_CORPSE(obj))
		strcpy(sdesc, corpse_desc(obj, ch));

	switch (mode) {
		case 0:			/* Long description */
			if (*sdesc)
				sprintf(output, "%s is lying here.", CAP(sdesc));
			else
				strcpy(output, obj->description);
			break;
		default:
			if (*sdesc)
				strcpy(output, sdesc);
			else
				strcpy(output, obj->short_description);
			break;
		}
	return output;
	}


/*
 * This function screams bitvector... -gg 6/45/98
 */
void show_obj_to_char(Object obj, Creature ch, int mode) {
	extern int Board_show_board(int board_type, Creature ch, char *arg, Object board);
	extern int board_loaded;
	void init_boards(void);
	extern int find_board(Creature ch);

	int board_type;

	*buf = '\0';
	if (mode == 0) {
		if (!IN_CHAIR(obj))
			strcpy(buf, GET_OBJ_DESC(obj, ch, 0));
		else		/* It won't get here if I'm not sitting on it */
			sprintf(buf, "You are sitting on %s.", GET_OBJ_DESC(obj, ch, 1));
		}
	else if (mode == 1 || mode == 2 || mode == 3 || mode == 4)
		strcpy(buf, GET_OBJ_DESC(obj, ch, mode));
	else if (mode == 5) {
		if (GET_OBJ_RNUM(obj) != NOTHING && obj_index[GET_OBJ_RNUM(obj)].look_spec && ((obj_index[GET_OBJ_RNUM(obj)].look_spec) (ch, obj, 0)))
			return;
		else if (GET_OBJ_TYPE(obj) == ITEM_BOARD) {
			if (!board_loaded) {
				init_boards();
				board_loaded = 1;
				}
			if ((board_type = find_board(ch)) != -1)
				if (Board_show_board(board_type, ch, "board", obj))
					return;
			strcpy(buf, "You see nothing special..");
			}
		else if (GET_OBJ_TYPE(obj) == ITEM_MAIL)
			page_string(ch->desc, obj->action_description ? obj->action_description : "It's blank.\r\n", 1);
		else if (GET_OBJ_TYPE(obj) != ITEM_DRINKCON)
			strcpy(buf, "You see nothing special..");
		else			/* ITEM_TYPE == ITEM_DRINKCON */
			strcpy(buf, "It looks like a drink container.");
		}
	strcat(buf, "\r\n");
	page_string(ch->desc, buf, TRUE);
	}


void list_obj_to_char(Object list, Creature ch, int mode, int show) {
	Object i, j = NULL;
	bool found = FALSE;
	int num;

	for (i = list; i; i = i->next_content) {
		num = 0;
		for (j = list; j != i; j = j->next_content) {
			strcpy(buf, GET_OBJ_DESC(j, ch, 1));
			if (!strcmp(buf, GET_OBJ_DESC(i, ch, 1))) {
				if (j->item_number == NOTHING)
					break;
				else if (j->item_number == i->item_number)
					break;
				}
			}
		if (j != i && GET_OBJ_TYPE(i) != ITEM_CONTAINER)
			continue;
		for (j = i; j; j = j->next_content)
			if (GET_OBJ_TYPE(j) != ITEM_CONTAINER) {
				strcpy(buf, GET_OBJ_DESC(j, ch, 1));
				if (!strcmp(buf, GET_OBJ_DESC(i, ch, 1))) {
					if (j->item_number == NOTHING)
						num++;
					else if (j->item_number == i->item_number)
						num++;
					}
				}

		if (CAN_SEE_OBJ(ch, i) && (!IN_CHAIR(i) || ch == IN_CHAIR(i))) {
			if (num > 1)
				msg_to_char(ch, "%s(%2i) ", mode == 0 ? "&2" : "", num);
			else
				msg_to_char(ch, "%s", mode == 0 ? "&2" : "");
			show_obj_to_char(i, ch, mode);
			found = TRUE;
			}
		}
	if (!found && show)
		msg_to_char(ch, " Nothing.\r\n");
	}


void diag_char_to_char(Creature i, Creature ch) {
	strcpy(buf, "$n");

	switch (GET_DAMAGE(i)) {
		case 0:
			strcat(buf, " is in excellent condition.");
			break;
		case 1:
			strcat(buf, " has some small wounds and bruises.");
			break;
		case 2:
			strcat(buf, " looks hurt.");
			break;
		case 3:
			strcat(buf, " appears injured.");
			break;
		case 4:
			strcat(buf, " is greatly wounded.");
			break;
		case 5:
			strcat(buf, " looks mauled!");
			break;
		case 6:
			strcat(buf, " is crippled!");
			break;
		case 7:
			strcat(buf, " is incapacitated!");
			break;
		default:
			strcat(buf, " is utterly torn up!");
			break;
		}

	act(buf, FALSE, i, 0, ch, TO_VICT);
	}


void look_at_char(Creature i, Creature ch, bool eq, bool self) {
	int j, found;

	if (!ch->desc)
		return;

	if (ch == i && !self) {
		msg_to_char(ch, "Yep, you're still there!\r\n");
		return;
		}

	act("You look at $N.", FALSE, ch, 0, i, TO_CHAR);

	if (IS_NPC(i) && GET_MOB_RNUM(i) != NOTHING && mob_index[GET_MOB_RNUM(i)].look_spec && ((mob_index[GET_MOB_RNUM(i)].look_spec) (ch, i, 0)))
		return;

	/* For morphs, we'll give a description instead of equipment */
	if (!IS_NPC(i) && GET_MORPH(i))
		act(morph_string(i, MORPH_STRING_DESC), FALSE, ch, 0, i, TO_CHAR);
	else {
		if (!IS_NPC(i)) {
			*buf = '\0';

			/* skin color */
			if (!IS_VAMPIRE(i) || DSC_FLAGGED(i, DSC_MASK)) {
				switch (GET_SKIN(i)) {
					case SKIN_FAIR:
						strcat(buf, "$S skin is fair in color.");
						break;
					case SKIN_REDDENED:
						strcat(buf, "$E has reddish skin.");
						break;
					case SKIN_YELLOWISH:
						strcat(buf, "$S skin has a yellow undertone.");
						break;
					case SKIN_TAN_LIGHT:
						strcat(buf, "$E has a lightly tanned complexion.");
						break;
					case SKIN_TAN_DARK:
						strcat(buf, "$S skin has a bronze tone.");
						break;
					case SKIN_LIGHT_BROWN:
						strcat(buf, "$S skin is light brown.");
						break;
					case SKIN_DARK_BROWN:
						strcat(buf, "$S skin is dark brown, almost black.");
						break;
					}
				}
			else {
				/* Clan Weakness: Cappadocian */
				switch (MIN((GET_CLAN(i) == CLAN_CAPPADOCIAN ? 0 : 10), (GET_BLOOD(i) * 10 / GET_MAX_BLOOD(i)) * GET_HUMANITY(i) / 10)) {
					case 3:
						strcat(buf, "$S skin is a pale white, barely even showing veins.");
						break;
					case 4:
						strcat(buf, "$S skin is pale and sickly.");
						break;
					case 5:
						strcat(buf, "$S skin is pale.");
						break;
					case 6:
						strcat(buf, "$S's skin is extremely light pink.");
						break;
					case 7:
						strcat(buf, "$S's skin is light pink.");
						break;
					case 8:
						strcat(buf, "$S skin has a pink tone.");
						break;
					case 9:
						strcat(buf, "$S skin is fair in color.");
						break;
					case 10:
						strcat(buf, "$S skin is flushed a healthy red.");
						break;
					default:
						strcat(buf, "$S skin is chalk-white and even $S veins are invisible through it.  It's truly mortifying!");
						break;
					}
				}

			/* Clan Weakness: Nosferatu */
			if (GET_CLAN(i) == CLAN_NOSFERATU && !DSC_FLAGGED(i, DSC_MASK))
				strcat(buf, "  $N appears to have leprosy.  $S skin is flaking and rotting off!");

			if (DSC_FLAGGED(i, DSC_TONGUE_OF_THE_ASP) && ww_dice(GET_PERCEPTION(ch) + GET_OCCULT(ch), 8 - SENSES_BONUS(ch)) >= 1 && !DSC_FLAGGED(i, DSC_MASK))
				strcat(buf, "  You think you notice a forked tongue in $S mouth!");

			/* hair */
			switch (GET_HAIR(i)) {
				case HAIR_BLACK:
					strcat(buf, "  $N's hair is a deep, foreboding black");
					break;
				case HAIR_BROWN:
					strcat(buf, "  Thick, brown hair flows from $S head");
					break;
				case HAIR_BLONDE:
					strcat(buf, "  $N's head is topped with playful, blonde hair");
					break;
				case HAIR_RED:
					strcat(buf, "  $N's hair is a sinister shade of red");
					break;
				case HAIR_WHITE:
					strcat(buf, "  $N's age shows through in $S white hair");
					break;
				case HAIR_GREEN:
					strcat(buf, "  $N has unusually green hair");
					break;
				case HAIR_BLUE:
					strcat(buf, "  $N's hair is a strange shade of blue");
					break;
				}

			/* eyes */
			switch (GET_EYES(i)) {
				case EYES_BLACK:
					strcat(buf, " with bottomless, black eyes.");
					break;
				case EYES_BROWN:
					strcat(buf, ", accompanied by dark brown eyes.");
					break;
				case EYES_BLUE:
					strcat(buf, ", accented by shining blue eyes.");
					break;
				case EYES_GREEN:
					strcat(buf, " and $E has vibrant green eyes.");
					break;
				case EYES_VIOLET:
					strcat(buf, " with mysterious, violet eyes.");
					break;
				case EYES_RED:
					strcat(buf, " and $E has flaming red eyes!");
					break;
				}

			if (HAS_INFRA(i))
				strcat(buf, "  You notice a distinct, red glint in $S eyes.");

			if (DSC_FLAGGED(i, DSC_TALONS_OF_THE_BEAST) && !DSC_FLAGGED(i, DSC_MASK))
				strcat(buf, "  $N's hands are huge, distorted, and very sharp!");

			if (ch != i) {
				if (GET_HEIGHT(i) > GET_HEIGHT(ch))
					strcat(buf, "  You are shorter than $M");
				else
					strcat(buf, "  You are slightly taller than $M");

				if (GET_WEIGHT(i) > GET_WEIGHT(ch))
					strcat(buf, " and $E is much larger than you.");
				else
					strcat(buf, " and $E is smaller than you.");

				if (DSC_FLAGGED(i, DSC_AWE | DSC_MAJESTY))
					strcat(buf, "  $N has an aura of awe about $M.");
				}


			/* Infernal override: */
			if (DSC_FLAGGED(i, DSC_BITUMENOUS_FLESH))
				act("$E is mummified in a hard, dark substance!", FALSE, ch, 0, i, TO_CHAR);
			else {
				act(buf, FALSE, ch, 0, i, TO_CHAR);
				diag_char_to_char(i, ch);
				}
			}
		else
			diag_char_to_char(i, ch);

		if (!eq)
			return;

		found = FALSE;
		for (j = 0; !found && j < NUM_WEARS; j++)
			if (GET_EQ(i, j) && CAN_SEE_OBJ(ch, GET_EQ(i, j)))
				found = TRUE;

		if (found) {
			msg_to_char(ch, "\r\n");	/* act() does capitalization. */
			act("$n is using:", FALSE, i, 0, ch, TO_VICT);
			for (j = 0; j < NUM_WEARS; j++)
				if (GET_EQ(i, j) && CAN_SEE_OBJ(ch, GET_EQ(i, j))) {
					msg_to_char(ch, where[j]);
					show_obj_to_char(GET_EQ(i, j), ch, 1);
					}
			}
		}

	if (!eq)
		return;

	if (ch != i && (IS_NPC(i) || GET_MORPH(i) == MORPH_NONE) && (GET_LEVEL(ch) >= LVL_GOD || GET_LARCENY(ch) >= 2)) {
		act("\r\nYou attempt to peek at $s inventory:", FALSE, i, 0, ch, TO_VICT);
		list_obj_to_char(i->carrying, ch, 1, TRUE);
		}
	}


void list_one_char(Creature i, Creature ch, int num) {
	const char *positions[] = {
		" is lying here, dead.",
		" is lying here, mortally wounded.",
		" is lying here, incapacitated.",
		" is lying here, stunned.",
		" is sleeping here.",
		" is resting here.",
		" is sitting here.",
		"!FIGHTING!",
		" is standing here."
		};

	if (num > 1)
		msg_to_char(ch, "(%2d) ", num);

	if (AFF_FLAGGED(i, AFF_NO_SEE_IN_ROOM))
		return;

	if (DSC_FLAGGED(i, DSC_MASQUE_OF_DEATH | DSC_DEATHS_WHISPER) && GET_POS(i) <= POS_RESTING) {
		msg_to_char(ch, "&2The corpse of %s is lying here.&3\r\n", IS_NPC(i) ? PERS(i, i, 0) : "a human");
		return;
		}

	if (IS_NPC(i) && i->player.long_descr && GET_POS(i) == POS_STANDING) {
		if (AFF_FLAGGED(i, AFF_INVISIBLE))
			msg_to_char(ch, "*");

		msg_to_char(ch, i->player.long_descr);
		}
	else {
		if (IS_NPC(i)) {
			strcpy(buf, i->player.short_descr);
			CAP(buf);
			}
		else {
			if (AFF_FLAGGED(i, AFF_INVISIBLE))
				strcpy(buf, "*");
			else
				*buf = '\0';

			sprintf(buf1, PERS(i, ch, 0));
			strcat(buf, CAP(buf1));

			switch (GET_SEX(i)) {
				case SEX_MALE:		strcpy(buf1, "man");	break;
				case SEX_FEMALE:	strcpy(buf1, "woman");	break;
				default:			strcpy(buf1, "person");	break;
				}

			if (GET_MORPH(i) == MORPH_NONE)
				switch (DSC_FLAGGED(i, DSC_MASK) ? MAX(4, GET_APPEARANCE(i)) : GET_APPEARANCE(i)) {
					case 0:
						sprintf(buf + strlen(buf), ", a hideously ugly %s,", buf1);
						break;
					case 1:
						sprintf(buf + strlen(buf), ", an ugly %s,", buf1);
						break;
					case 2:
						break;
					case 3:
						sprintf(buf + strlen(buf), ", a comely %s,", buf1);
						break;
					case 4:
						sprintf(buf + strlen(buf), ", an attractive %s,", buf1);
						break;
					default:
						sprintf(buf + strlen(buf), ", a very attractive %s,", buf1);
						break;
					}
				}

		if (AFF_FLAGGED(i, AFF_HIDE))
			strcat(buf, " (hidden)");
		if (!IS_NPC(i) && !i->desc)
			strcat(buf, " (linkless)");
		if (!IS_NPC(i) && PLR_FLAGGED(i, PLR_WRITING))
			strcat(buf, " (writing)");

		if (GET_POS(i) != POS_FIGHTING) {
			if (!ON_CHAIR(i))
				strcat(buf, positions[(int) GET_POS(i)]);
			else
				sprintf(buf + strlen(buf), " is sitting on %s.", CAN_SEE_OBJ(ch, ON_CHAIR(i)) ? GET_OBJ_DESC(ON_CHAIR(i), ch, 1) : "something");
			}
		else {
			if (FIGHTING(i)) {
				strcat(buf, " is here, fighting ");
				if (FIGHTING(i) == ch)
					strcat(buf, "YOU!");
				else {
					if (i->in_room == FIGHTING(i)->in_room)
						strcat(buf, PERS(FIGHTING(i), ch, 0));
					else
						strcat(buf, "someone who has already left");
					strcat(buf, "!");
					}
				}
			else			/* NIL fighting pointer */
				strcat(buf, " is here struggling with thin air.");
			}

		msg_to_char(ch, "%s\r\n", buf);
		}

	if (GET_RIDING(i)) {
		sprintf(buf, "...%s is mounted upon $N.", HSSH(i));
		act(buf, FALSE, ch, 0, GET_RIDING(i), TO_CHAR);
		}
	if (GET_LED_BY(i)) {
		sprintf(buf, "...%s is being led by %s.", HSSH(i), GET_LED_BY(i) == ch ? "you" : "$N");
		act(buf, FALSE, ch, 0, GET_LED_BY(i), TO_CHAR);
		}
	if (MOB_FLAGGED(i, MOB_TIED))
		act("...$e is tied up here.", FALSE, i, 0, ch, TO_VICT);
	if (DSC_FLAGGED(i, DSC_MASQUE_OF_DEATH))
		act("...$e is a walking corpse!", FALSE, i, 0, ch, TO_VICT);
	if (!IS_NPC(i) && GET_SLIT_WRIST(i))
		act("...$s wrist has been cut open and is dripping blood!", FALSE, i, 0, ch, TO_VICT);
	if (AFF_FLAGGED(i, AFF_BLIND))
		act("...$e is groping around blindly!", FALSE, i, 0, ch, TO_VICT);
	if (IS_INJURED(i, INJ_TIED))
		act("...$e is bound and gagged!", FALSE, i, 0, ch, TO_VICT);
	if (IS_INJURED(i, INJ_STAKED))
		act("...$e has a stake through $s heart!", FALSE, i, 0, ch, TO_VICT);
	if (DSC_FLAGGED(i, DSC_BITUMENOUS_FLESH))
		act("...$e is mummified in a hard outer covering!", FALSE, i, 0, ch, TO_VICT);
	if (IS_VAMPIRE(i) && HAS_INFRA(i))
		act("...$s eyes have an eerie, red glow!", FALSE, i, 0, ch, TO_VICT);
	if (DSC_FLAGGED(i, DSC_TALONS_OF_THE_BEAST) && !DSC_FLAGGED(i, DSC_MASK))
		act("...$s fingers are mutated into giant, hideous talons!", FALSE, i, 0, ch, TO_VICT);
	if (DSC_FLAGGED(i, DSC_AWE))
		act("...$e is surrounded by an awesome aura!", FALSE, i, 0, ch, TO_VICT);
	if (DSC_FLAGGED(i, DSC_MAJESTY))
		act("...$e has a majestic aura about $m!", FALSE, i, 0, ch, TO_VICT);
	if (PLR_FLAGGED(i, PLR_PIRATE))
		act("...$e has a tattoo of a pirate!", FALSE, i, 0, ch, TO_VICT);
	if (!IS_NPC(i) && GET_DAYS_SINCE_BATHING(i) > 3) {
		switch (GET_DAYS_SINCE_BATHING(i)) {
			case 4:
				act("...$e has a putrid smell about $m.", FALSE, i, 0, ch, TO_VICT);
				break;
			case 5:
				act("...a cloud of flies is circling around $m.", FALSE, i, 0, ch, TO_VICT);
				break;
			case 6:
				act("...$e is extremely dirty, and smells disgusting!", FALSE, i, 0, ch, TO_VICT);
				break;
			case 7:
				act("...$e is filthy, and smells like manure!", FALSE, i, 0, ch, TO_VICT);
				break;
			default:
				act("...$e is absolutely revolting!", FALSE, i, 0, ch, TO_VICT);
				break;
			}
		}
	if (GET_FED_ON_BY(i)) {
		sprintf(buf, "...$e is held held tightly by %s!", PERS(GET_FED_ON_BY(i), ch, 0));
		act(buf, FALSE, i, 0, ch, TO_VICT);
		}
	if (GET_FEEDING_FROM(i)) {
		sprintf(buf, "...$e has his teeth firmly implanted in %s!", PERS(GET_FEEDING_FROM(i), ch, 0));
		act(buf, FALSE, i, 0, ch, TO_VICT);
		}
	if (!IS_NPC(i) && GET_ACTION(i) == ACT_MORPHING)
		act("...$e is undergoing a hideous transformation!", FALSE, i, 0, ch, TO_VICT);
	if (IS_INJURED(i, INJ_TORPOR))
		act("...$e appears to be in torpor!", FALSE, i, 0, ch, TO_VICT);
	if (((IS_WEREWOLF(i) && IS_WEREWOLF(ch)) || IS_IMMORTAL(ch)) && !IS_NPC(i) && GET_MORPH(i) != MORPH_NONE)
		act("...this appears to be $o.", FALSE, i, 0, ch, TO_VICT);
	}


void list_char_to_char(Creature list, Creature ch) {
	Creature i, j;
	int c = 1;

	for (i = list; i; i = i->next_in_room, c = 1)
		if (ch != i) {
			if (GET_RIDDEN_BY(i))
				continue;
			if (IS_NPC(i) && GET_POS(i) != POS_FIGHTING) {
				for (j = list; j != i; j = j->next_in_room)
					if (GET_MOB_VNUM(j) == GET_MOB_VNUM(i) && !GET_RIDDEN_BY(j) && CAN_SEE(ch, j) && GET_POS(j) == GET_POS(i) && GET_POS(j) != POS_FIGHTING)
						break;
				if (j != i)
					continue;
				for (j = i->next_in_room; j; j = j->next_in_room)
					if (GET_MOB_VNUM(j) == GET_MOB_VNUM(i) && !GET_RIDDEN_BY(j) && CAN_SEE(ch, j) && GET_POS(j) == GET_POS(i) && GET_POS(j) != POS_FIGHTING)
						c++;
				}

			if (CAN_SEE(ch, i))
				list_one_char(i, ch, c);
			else if (!CAN_SEE_IN_DARK_ROOM(ch, ch->in_room) && HAS_INFRA(i) && !NOCTURNE_DARK(ch->in_room)) {
				if (c > 1)
					msg_to_char(ch, "(%2d) ", c);
				msg_to_char(ch, "You see a pair of glowing red eyes looking your way.\r\n");
				}
			}
	}


ACMD(do_weather) {
	void list_moons_to_char(Creature ch);
	const char *sky_look[] = {
		"cloudless",
		"cloudy",
		"rainy",
		"lit by flashes of lightning"
		};

	if (IS_OUTDOORS(ch)) {
		msg_to_char(ch, "The sky is %s and %s.\r\n", sky_look[weather_info.sky], (weather_info.change >= 0 ? "you feel a warm wind from south" : "your foot tells you bad weather is due"));
		if (weather_info.sunlight == SUN_SET || weather_info.sunlight == SUN_DARK)
			list_moons_to_char(ch);
		}
	else
		msg_to_char(ch, "You can't see the sky from here.\r\n");
	}


ACMD(do_exits) {
	extern char *get_room_name(int room, bool color);
	int door, room;

	if (subcmd == -1)
		room = ch->in_room;
	else
		room = subcmd;

	*buf = '\0';

	if (AFF_FLAGGED(ch, AFF_BLIND)) {
		msg_to_char(ch, "You can't see a damned thing, you're blind!\r\n");
		return;
		}
	for (door = 0; door < NUM_OF_DIRS; door++)
		if (world[room].dir_option[door] && world[room].dir_option[door]->to_room != NOWHERE && !EXIT_FLAGGED(world[room].dir_option[door], EX_CLOSED)) {
			sprintf(buf2, "%-5s - ", dirs[door]);
			if (!CAN_SEE_IN_DARK_ROOM(ch, world[room].dir_option[door]->to_room))
				strcat(buf2, "Too dark to tell\r\n");
			else
				sprintf(buf2 + strlen(buf2), "%s\r\n", get_room_name(world[room].dir_option[door]->to_room, 0));
			strcat(buf, CAP(buf2));
			}
	msg_to_char(ch, "Obvious exits:\r\n%s", *buf ? buf : "None.\r\n");
	}


void show_map_to_char(Creature ch, room_rnum room, int shift_x, int shift_y) {
	extern bool show_pc_in_room(Creature ch, room_rnum room);
	extern const char *sector_disp[];
	extern const char *building_disp[];
	extern const char *crop_disp[];
	extern const char *open_monument_icons[];
	extern const char *closed_monument_icons[];
	extern const char *multi_disp[][3];

	char buf[30], buf1[30];
	room_rnum to_room = real_shift(room, shift_x, shift_y);
	int i, j;
	bool hidden = FALSE;

	#define distance(x, y, a, b)		((x - a) * (x - a) + (y - b) * (y - b))

	*buf = '\0';

	if (shift_x == 0 && shift_y == 0)
		strcpy(buf, "&0<oo>");
	else if (show_pc_in_room(ch, real_shift(room, shift_x, shift_y)))
		return;

	/* Hidden buildings */
	else if (distance(FLAT_X_COORD(ch->in_room), FLAT_Y_COORD(ch->in_room), FLAT_X_COORD(to_room), FLAT_Y_COORD(to_room)) > 2 && ROOM_AFF_FLAGGED(to_room, ROOM_AFF_CHAMELEON_FOREST | ROOM_AFF_CHAMELEON_DESERT | ROOM_AFF_CHAMELEON_MOUNTAIN)) {
		if (ROOM_AFF_FLAGGED(to_room, ROOM_AFF_CHAMELEON_FOREST))
			strcat(buf, sector_disp[SECT_FOREST_4]);
		else if (ROOM_AFF_FLAGGED(to_room, ROOM_AFF_CHAMELEON_DESERT))
			strcat(buf, sector_disp[SECT_DESERT]);
		else if (ROOM_AFF_FLAGGED(to_room, ROOM_AFF_CHAMELEON_MOUNTAIN))
			strcat(buf, sector_disp[SECT_MOUNTAIN]);
		hidden = TRUE;
		}

	/* Rooms with custom icons (take precedence over all but hidden rooms */
	else if (world[to_room].icon)
		strcat(buf, world[to_room].icon);
	else if (SECT(to_room) == SECT_BARRIER) {
		if (world[to_room].type2 == 0)
			strcpy(buf, "&0");
		else if (world[to_room].type2 == 1)
			strcpy(buf, "&2");
		else if (world[to_room].type2 == 2)
			strcpy(buf, "&3");

		if (world[to_room].type == 1) {
			if (SECT(shift_w(to_room)) == SECT_BARRIER || BUILDING_TYPE(shift_w(to_room)) == BUILDING_GATEHOUSE)
				strcat(buf, "VV");
			else
				strcat(buf, ".V");
			if (SECT(shift_e(to_room)) == SECT_BARRIER || BUILDING_TYPE(shift_e(to_room)) == BUILDING_GATEHOUSE)
				strcat(buf, "VV");
			else
				strcat(buf, "V.");
			}
		else {
			if (SECT(shift_w(to_room)) == SECT_BARRIER || BUILDING_TYPE(shift_w(to_room)) == BUILDING_GATEHOUSE)
				strcat(buf, "vv");
			else
				strcat(buf, ".v");
			if (SECT(shift_e(to_room)) == SECT_BARRIER || BUILDING_TYPE(shift_e(to_room)) == BUILDING_GATEHOUSE)
				strcat(buf, "vv");
			else
				strcat(buf, "v.");
			}
		}
	else if (BUILDING_TYPE(to_room) == BUILDING_GATEHOUSE) {
		strcpy(buf, "&0");
		if ((world[to_room].building_entrance == NORTH || world[to_room].building_entrance == SOUTH) && (SECT(shift_w(to_room)) == SECT_BARRIER && !world[shift_w(to_room)].type))
			strcat(buf, "v");
		else if ((world[to_room].building_entrance == SOUTH || world[to_room].building_entrance == NORTH) && (SECT(shift_w(to_room)) == SECT_BARRIER && world[shift_w(to_room)].type))
			strcat(buf, "V");
		else if ((world[to_room].building_entrance == EAST || world[to_room].building_entrance == WEST) && SECT(shift_w(to_room)) == SECT_ROAD)
			strcat(buf, "-");
		else
			strcat(buf, ".");
		if (world[to_room].building_entrance == WEST || world[to_room].building_entrance == EAST)
			strcat(buf, "==");
		else if (world[to_room].building_entrance == NORTH || world[to_room].building_entrance == SOUTH)
			strcat(buf, "||");
		else if (world[to_room].building_entrance == NORTHWEST || world[to_room].building_entrance == SOUTHEAST)
			strcat(buf, "\\\\");
		else if (world[to_room].building_entrance == SOUTHWEST || world[to_room].building_entrance == NORTHEAST)
			strcat(buf, "//");
		if ((world[to_room].building_entrance == NORTH || world[to_room].building_entrance == SOUTH) && (SECT(shift_e(to_room)) == SECT_BARRIER && !world[shift_e(to_room)].type))
			strcat(buf, "v");
		else if ((world[to_room].building_entrance == SOUTH || world[to_room].building_entrance == NORTH) && (SECT(shift_e(to_room)) == SECT_BARRIER && world[shift_e(to_room)].type))
			strcat(buf, "V");
		else if ((world[to_room].building_entrance == EAST || world[to_room].building_entrance == WEST) && SECT(shift_e(to_room)) == SECT_ROAD)
			strcat(buf, "-");
		else
			strcat(buf, ".");
		}
	else if (SECT(to_room) == SECT_ROAD) {
		*buf = '\0';
		if (SECT(shift_w(to_room)) == SECT_ROAD || BUILDING_TYPE(shift_w(to_room)) == BUILDING_GATEHOUSE)
			strcat(buf, "&0--");
		else if (SECT(shift_n(to_room)) == SECT_ROAD || SECT(shift_s(to_room)) == SECT_ROAD || BUILDING_TYPE(shift_n(to_room)) == BUILDING_GATEHOUSE || BUILDING_TYPE(shift_s(to_room)) == BUILDING_GATEHOUSE) {
			if (world[to_room].type)
				strcat(buf, "&4~~&0");
			else
				strcat(buf, "&0..");
			}
		else if (world[to_room].type)
			strcat(buf, "&4~&0-");
		else
			strcat(buf, "&0.-");
		if (SECT(shift_n(to_room)) == SECT_ROAD || SECT(shift_s(to_room)) == SECT_ROAD || BUILDING_TYPE(shift_n(to_room)) == BUILDING_GATEHOUSE ||BUILDING_TYPE(shift_s(to_room)) == BUILDING_GATEHOUSE) {
			if (strstr(buf, "-") || SECT(shift_e(to_room)) == SECT_ROAD)
				strcat(buf, "+");
			else
				strcat(buf, "|");
			}
		else
			strcat(buf, "-");
		if (SECT(shift_e(to_room)) == SECT_ROAD || BUILDING_TYPE(shift_e(to_room)) == BUILDING_GATEHOUSE)
			strcat(buf, "-");
		else if (world[to_room].type)
			strcat(buf, "&4~");
		else
			strcat(buf, ".");
		if (world[to_room].type2)
			for (i = 0; i < strlen(buf); i++)
				if (buf[i] == '0')
					buf[i] = '3';
		}
	else if (BUILDING_TYPE(to_room) == BUILDING_SHIPYARD || BUILDING_TYPE(to_room) == BUILDING_SHIPYARD2) {
		if (world[to_room].building_entrance == NORTH || world[to_room].building_entrance == NORTHEAST || world[to_room].building_entrance == EAST || world[to_room].building_entrance == SOUTHEAST || world[to_room].building_entrance == SOUTH)
			strcpy(buf, "&4~");
		else
			strcpy(buf, "&0.");
		if (BUILDING_TYPE(to_room) == BUILDING_SHIPYARD)
			strcat(buf, "&0()");
		else
			strcat(buf, "&0[]");
		if (world[to_room].building_entrance == NORTH || world[to_room].building_entrance == NORTHWEST || world[to_room].building_entrance == WEST || world[to_room].building_entrance == SOUTHWEST || world[to_room].building_entrance == SOUTH)
			strcat(buf, "&4~");
		else
			strcat(buf, ".");
		}
	else if (BUILDING_TYPE(to_room) == BUILDING_DOCKS) {
		if (world[to_room].building_entrance == NORTHEAST || world[to_room].building_entrance == EAST || world[to_room].building_entrance == SOUTHEAST)
			strcpy(buf, "&4~~&0==");
		else if (world[to_room].building_entrance == NORTH || world[to_room].building_entrance == SOUTH)
			strcpy(buf, "&4~&0==&4~");
		else
			strcpy(buf, "&0==&4~~");
		}
	/* Altered icons */
	else if (BUILDING_TYPE(to_room) == BUILDING_GUARD_TOWER && world[to_room].type2 == 2)
		strcat(buf, "&3.&0TT&3.");
	else if (SECT(to_room) == SECT_BUILDING)
		strcat(buf, building_disp[(int) world[to_room].type]);
	else if (SECT(to_room) == SECT_MULTI)
		strcat(buf, multi_disp[(int) world[to_room].type][(int) world[to_room].type2]);
	else if (SECT(to_room) == SECT_FOREST_1 && world[to_room].type2) {
		switch (number(0, 3)) {
			case 0:		strcat(buf, "&2^&3...");	break;
			case 1:		strcat(buf, "&3.&2^&3..");	break;
			case 2:		strcat(buf, "&3..&2^&3.");	break;
			case 3:		strcat(buf, "&3...&2^");	break;
			}
		}
	else if (SECT(to_room) == SECT_CROP && world[to_room].type == CROP_FRUIT) {
		switch (number(0, 9)) {
			case 0:		strcat(buf, "&1o&3^^^");	break;
			case 1:		strcat(buf, "&3^&1o&3^^");	break;
			case 2:		strcat(buf, "&3^^&1o&3^");	break;
			case 3:		strcat(buf, "&3^^^&1o");	break;
			default:
				strcat(buf, crop_disp[(int) world[to_room].type]);
				break;
			}
		}
	else if (SECT(to_room) == SECT_CROP)
		strcat(buf, crop_disp[(int) world[to_room].type]);
	else if (SECT(to_room) == SECT_MONUMENT_OPEN)
		strcat(buf, open_monument_icons[(int) world[to_room].type]);
	else if (SECT(to_room) == SECT_MONUMENT_CLOSED)
		strcat(buf, closed_monument_icons[(int) world[to_room].type]);
	else
		strcat(buf, sector_disp[(int) SECT(to_room)]);

	if (PRF_FLAGGED(ch, PRF_NOMAPCOL) || PRF_FLAGGED(ch, PRF_POLITICAL)) {
		for (i = 0, j = 0; i < strlen(buf); i++) {
			if (buf[i] == '&')
				i++;
			else
				buf1[j++] = buf[i];
			}
		buf1[4] = '\0';

		/* world[to_room].owner == -1 is a key for unclaimable */
		if (PRF_FLAGGED(ch, PRF_POLITICAL) && !hidden)
			sprintf(buf, "%s%s&0", world[to_room].owner != -1 && real_empire(world[to_room].owner) != -1 && empire[real_empire(world[to_room].owner)].banner ? empire[real_empire(world[to_room].owner)].banner : "&0", buf1);
		else
			strcpy(buf, buf1);
		}

	send_to_char(buf, ch);
	}


bool show_pc_in_room(Creature ch, room_rnum room) {
	Creature c;

	if (!PRF_FLAGGED(ch, PRF_MAPPC))
		return FALSE;

	if (SECT(room) == SECT_MONUMENT_CLOSED || SECT(room) == SECT_BUILDING || SECT(room) == SECT_MULTI || SECT(room) == SECT_MOUNTAIN || SECT(room) == SECT_FOREST_4)
		if (IS_COMPLETE(room) || ALWAYS_CLOSED(room))
			return FALSE;

	/* Hidden people are left off the map, even if you sense life */
	for (c = world[room].people; c; c = c->next_in_room)
		if (!IS_NPC(c) && CAN_SEE(ch, c) && !AFF_FLAGGED(c, AFF_NO_SEE_IN_ROOM) && !MORPH_FLAGGED(c, MORPH_FLAG_NPC) && (!AFF_FLAGGED(c, AFF_HIDE) || IS_GOD(ch) || IS_IMMORTAL(ch))) {
			msg_to_char(ch, "&0<oo>");
			return TRUE;
			}
	return FALSE;
	}


bool adjacent_room_is_light(room_rnum room) {
	int i;

	for (i = 0; i < NUM_SIMPLE_DIRS; i++)
		if (IS_REAL_LIGHT(real_shift(room, shift_dir[i][0], shift_dir[i][1])))
			return 1;
	return 0;
	}


char *get_room_name(int room, bool color) {
	extern const char *multi_entrance_names[];
	extern const char *building_names[];
	extern const char *room_names[];
	extern const char *crop_names[];
	extern const char *open_monument_names[];
	extern const char *closed_monument_names[];

	static char name[MAX_STRING_LENGTH];

	/* world[room].owner == -1 is a key for unclaimable */
	if (real_empire(world[room].owner) != -1 && world[room].owner != -1 && color)
		strcpy(name, empire[real_empire(world[room].owner)].banner);
	else
		*name = '\0';

	/* Note: room names ALWAYS override other names */
	if (world[room].name)
		strcat(name, world[room].name);

	/* Names by type */
	else if (SECT(room) == SECT_INSIDE)
		strcat(name, room_names[(int) world[room].type]);
	else if (SECT(room) == SECT_CROP)
		strcat(name, crop_names[(int) world[room].type]);

	/* Custom names by type */
	else if ((BUILDING_TYPE(room) == BUILDING_GUARD_TOWER2 || BUILDING_TYPE(room) == BUILDING_GUARD_TOWER3 || BUILDING_TYPE(room) == BUILDING_GUARD_TOWER) && world[room].type2 == 2 && IS_COMPLETE(room))
		strcat(name, "A Desert Sentry");
	else if (SECT(room) == SECT_FOREST_1 && world[room].type2)
		strcat(name, "A Palm Tree");

	/* Building */
	else if (SECT(room) == SECT_BUILDING)
		strcat(name, building_names[(int) world[room].type]);

	/* Wall */
	else if (SECT(room) == SECT_BARRIER) {
		if (world[room].type2 == 0)
			sprintf(name + strlen(name), "%s", world[room].type ? "A Stone Wall" : "A Rickety Fence");
		else if (world[room].type2 == 1)
			sprintf(name + strlen(name), "%s", world[room].type ? "A Rocky Palisade" : "An Inpenetrable Trellis");
		else if (world[room].type2 == 2)
			sprintf(name + strlen(name), "%s", world[room].type ? "An Adobe Wall" : "A Spiked Barrier");
		}

	/* Closed Monuments */
	else if (CLOSED_MONUMENT_TYPE(room) == MONUMENT_C_SHRINE)
		sprintf(name + strlen(name), "A Shrine to %s", get_name_by_id(world[room].spare) ? CAP(get_name_by_id(world[room].spare)) : "a Former God");
	else if (CLOSED_MONUMENT_TYPE(room) == MONUMENT_C_TEMPLE_UL)
		sprintf(name + strlen(name), "A Temple of %s", get_name_by_id(world[room].spare) ? CAP(get_name_by_id(world[room].spare)) : "a Former God");
	else if (CLOSED_MONUMENT_TYPE(room) == MONUMENT_C_HIGHTEMPLE_UC)
		sprintf(name + strlen(name), "A Temple of %s", get_name_by_id(world[room].spare) ? CAP(get_name_by_id(world[room].spare)) : "a Former God");
	else if (SECT(room) == SECT_MONUMENT_CLOSED)
		strcat(name, closed_monument_names[(int) world[room].type]);

	/* Open Monuments */
	else if (OPEN_MONUMENT_TYPE(room) == MONUMENT_O_STATUE)
		sprintf(name + strlen(name), "A Statue of %s", get_name_by_id(world[room].spare) ? CAP(get_name_by_id(world[room].spare)) : "a Former God");
	else if (SECT(room) == SECT_MONUMENT_OPEN)
		strcat(name, open_monument_names[(int) world[room].type]);

	/* Multi- */
	else if (SECT(room) == SECT_MULTI)
		strcat(name, multi_entrance_names[(int) world[room].type2]);

	/* Normal */
	else
		strcat(name, sector_name[(int) SECT(room)]);

	return (name);
	}


char *get_room_description(int room) {
	extern const char *multi_entrance_descriptions[];
	extern const char *building_descs[];
	extern const char *room_descs[];
	extern const char *closed_monument_description[];

	static char desc[MAX_STRING_LENGTH];

	*desc = '\0';

	/* Note: room descs ALWAYS override other descs */
	if (world[room].description)
		strcat(desc, world[room].description);
	else if (SECT(room) == SECT_BUILDING && (ALWAYS_CLOSED(room) || IS_COMPLETE(room)))
		strcat(desc, building_descs[(int) world[room].type]);
	else if (SECT(room) == SECT_INSIDE)
		strcat(desc, room_descs[(int) world[room].type]);
	else if (SECT(room) == SECT_MULTI && IS_COMPLETE(room))
		strcat(desc, multi_entrance_descriptions[(int) world[room].type2]);
	else if (SECT(room) == SECT_MONUMENT_CLOSED)
		strcat(desc, closed_monument_description[(int) world[room].type]);
	else
		return NULL;

	return (desc);
	}


#define AbsMax(x, y)	MAX(((x) < 0 ? (-1 * x) : (x)), ((y) < 0 ? (-1 * y) : (y)))

void look_at_room_by_rnum(Creature ch, room_rnum room) {
	extern byte distance_can_see(Creature ch);
	extern char *get_name_by_id(long id);

	char output[MAX_STRING_LENGTH];
	int i, j, s, t;


	if (!ch || !ch->desc)
		return;

	if (AFF_FLAGGED(ch, AFF_BLIND)) {
		msg_to_char(ch, "You see nothing but infinite darkness...\r\n");
		return;
		}

	if (DSC_FLAGGED(ch, DSC_EARTHMELD) && (SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_MONUMENT_CLOSED)) {
		msg_to_char(ch, "You are beneath a building.\r\n");
		return;
		}

	sprintf(output, "%s (%d, %d)&0", get_room_name(room, 1), X_COORD(room), Y_COORD(room));

	if (!PRF_FLAGGED(ch, PRF_BRIEF) && get_room_description(room))
		sprintf(output + strlen(output), "\r\n%s", get_room_description(room));
	else
		strcat(output, "\r\n");

	if (!PRF_FLAGGED(ch, PRF_NOCLEAR))
		msg_to_char(ch, "");

	if ((SECT(room) != SECT_BUILDING && SECT(room) != SECT_MONUMENT_CLOSED && SECT(room) != SECT_MULTI && SECT(room) != SECT_INSIDE) || !(IS_COMPLETE(room) || ALWAYS_CLOSED(room))) {
		*buf = '\0';
		s = (62 - strlen(output))/2;
		if (!PRF_FLAGGED(ch, PRF_BRIEF))
			for (t = 1; t <= s; t++)
				send_to_char(" ", ch);
		msg_to_char(ch, output);
		if (PRF_FLAGGED(ch, PRF_BRIEF))
			msg_to_char(ch, "+----------------------------+\r\n");
		else
			msg_to_char(ch, "+------------------------------------------------------------+\r\n");

		for (i = (PRF_FLAGGED(ch, PRF_BRIEF) ? 3 : 7); i > (PRF_FLAGGED(ch, PRF_BRIEF) ? -4 : -8); i--) {			/* Rows */
			msg_to_char(ch, "|");
			for (j = (PRF_FLAGGED(ch, PRF_BRIEF) ? -3 : -7); j < (PRF_FLAGGED(ch, PRF_BRIEF) ? 4 : 8); j++) {		/* Columns */
				if ((!CAN_SEE_IN_DARK_ROOM(ch, real_shift(room, j, i)) && AbsMax(j, i) > distance_can_see(ch) && !adjacent_room_is_light(real_shift(room, j, i))) || ROOM_AFF_FLAGGED(real_shift(room, j, i), ROOM_AFF_DARK))
					send_to_char("    ", ch);
				else if (ROOM_AFF_FLAGGED(real_shift(room, j, i), ROOM_AFF_DARK))
					send_to_char("    ", ch);
				else
					show_map_to_char(ch, room, j, i);
				}

			msg_to_char(ch, "&0|\r\n");
			}

		if (PRF_FLAGGED(ch, PRF_BRIEF))
			msg_to_char(ch, "+----------------------------+\r\n");
		else
			msg_to_char(ch, "+------------------------------------------------------------+\r\n");
		}
	else {
		if (!CAN_SEE_IN_DARK_ROOM(ch, room))
			send_to_char("It is pitch black...\r\n", ch);
		else
			msg_to_char(ch, output);
		}

	/* world[HOME_ROOM(room)].owner == -1 is a key for unclaimable */
	if (real_empire(world[HOME_ROOM(room)].owner) != -1 && world[HOME_ROOM(room)].owner != -1)
		msg_to_char(ch, "This area is owned by %s%s&0.\r\n", empire[real_empire(world[HOME_ROOM(room)].owner)].banner, empire[real_empire(world[HOME_ROOM(room)].owner)].name);

	if (!IS_COMPLETE(room)) {
		msg_to_char(ch, "Remaining to Completion:");
		if (world[room].res.sticks)
			msg_to_char(ch, " Sticks: %d", world[room].res.sticks);
		if (world[room].res.logs)
			msg_to_char(ch, " Logs: %d", world[room].res.logs);
		if (world[room].res.rocks)
			msg_to_char(ch, " Rocks: %d", world[room].res.rocks);
		if (world[room].res.iron)
			msg_to_char(ch, " Iron: %d", world[room].res.iron);
		msg_to_char(ch, "\r\n");
		}

	if (!DSC_FLAGGED(ch, DSC_EARTHMELD)) {
		/* now list characters & objects */
		send_to_char("&2", ch);
		list_obj_to_char(world[room].contents, ch, 0, FALSE);
		send_to_char("&3", ch);
		list_char_to_char(world[room].people, ch);
		send_to_char("&0", ch);
		}

	/* Exits ? */
	if ((SECT(room) == SECT_BUILDING || SECT(room) == SECT_MONUMENT_CLOSED || SECT(room) == SECT_MULTI || SECT(room) == SECT_INSIDE) && (IS_COMPLETE(room) || ALWAYS_CLOSED(room)))
		do_exits(ch, "", 0, room);
	}


void look_in_direction(Creature ch, int dir) {
	Creature c;
	int i;
	room_rnum to_room;

	if ((SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_INSIDE) && (IS_COMPLETE(ch->in_room) || ALWAYS_CLOSED(ch->in_room))) {
		if (EXIT(ch, dir)) {
			if (EXIT_FLAGGED(EXIT(ch, dir), EX_CLOSED) && EXIT(ch, dir)->keyword) {
				sprintf(buf, "The %s is closed.\r\n", fname(EXIT(ch, dir)->keyword));
				send_to_char(buf, ch);
				}
			else if (EXIT_FLAGGED(EXIT(ch, dir), EX_ISDOOR) && EXIT(ch, dir)->keyword) {
				sprintf(buf, "The %s is open.\r\n", fname(EXIT(ch, dir)->keyword));
				send_to_char(buf, ch);
				}
			if (!EXIT_FLAGGED(EXIT(ch, dir), EX_CLOSED)) {
				*buf = '\0';
				to_room = EXIT(ch, dir)->to_room;
				if (CAN_SEE_IN_DARK_ROOM(ch, to_room))
					for (c = world[to_room].people; c; c = c->next_in_room)
						if (CAN_SEE(ch, c))
							sprintf(buf+strlen(buf), "%s, ", PERS(c, c, 0));

				/* Now, we clean up that buf */
				if (*buf) {
					sprintf(buf+strlen(buf)-2, ".\r\n");

					for (i = strlen(buf)-1; i > 0; i--)
						if (buf[i] == ',') {
							sprintf(buf1, buf + i+1);
							buf[i] = '\0';
							strcat(buf, " and");
							strcat(buf, buf1);
							break;
							}
					msg_to_char(ch, "You see %s", buf);
					}
				else
					msg_to_char(ch, "You don't see anyone in that direction.\r\n");
				}
			}
		else
			send_to_char("Nothing special there...\r\n", ch);
		}
	else {
		*buf = '\0';
		if (dir == UP) {
			do_weather(ch, "", 0, 0);
			return;
			}
		if (dir == DOWN) {
			switch(SECT(ch->in_room)) {
				case SECT_BUILDING:
				case SECT_MULTI:
				case SECT_INSIDE:
					msg_to_char(ch, "The floor is made of wood.\r\n");
					break;
				case SECT_MONUMENT_CLOSED:
				case SECT_MONUMENT_OPEN:
					msg_to_char(ch, "The floor is made of stone.\r\n");
					break;
				case SECT_FOUNTAIN:
					msg_to_char(ch, "The water in the fountain is clear.\r\n");
					break;
				case SECT_WELL:
					msg_to_char(ch, "The water in the well is clear.\r\n");
					break;
				case SECT_SEEDED:
				case SECT_CROP:
					msg_to_char(ch, "The crops are growing nicely this year.\r\n");
					break;
				case SECT_RIVER:
				case SECT_OCEAN:
					msg_to_char(ch, "The flowing waves make you queasy.\r\n");
					break;
				case SECT_ROAD:
					msg_to_char(ch, "The road is cobbled with stones.\r\n");
					break;
				default:
					msg_to_char(ch, "The ground is covered with dirt and with grass.\r\n");
					break;
				}
			return;
			}
		to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1]);
		if (SECT(to_room) == SECT_MOUNTAIN || SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MONUMENT_CLOSED || SECT(to_room) == SECT_MONUMENT_OPEN || SECT(to_room) == SECT_MULTI) {
			msg_to_char(ch, "You can't see past the %s.\r\n", SECT(to_room) == SECT_BUILDING || SECT(to_room) == SECT_MULTI ? "building" : "mountains");
			return;
			}
		if (CAN_SEE_IN_DARK_ROOM(ch, to_room))
			for (c = world[to_room].people; c; c = c->next_in_room)
				if (CAN_SEE(ch, c))
					sprintf(buf+strlen(buf), "%s, ", PERS(c, c, 0));
		/* Shift, rinse, repeat */
		to_room = real_shift(to_room, shift_dir[dir][0], shift_dir[dir][1]);
		if (SECT(to_room) != SECT_MOUNTAIN && SECT(to_room) != SECT_BUILDING && SECT(to_room) != SECT_MONUMENT_CLOSED && SECT(to_room) != SECT_MONUMENT_OPEN && SECT(to_room) != SECT_MULTI) {
			if (CAN_SEE_IN_DARK_ROOM(ch, to_room))
				for (c = world[to_room].people; c; c = c->next_in_room)
					if (CAN_SEE(ch, c))
						sprintf(buf+strlen(buf), "%s, ", PERS(c, c, 0));
			/* And a third time for good measure */
			to_room = real_shift(to_room, shift_dir[dir][0], shift_dir[dir][1]);
			if (SECT(to_room) != SECT_MOUNTAIN && SECT(to_room) != SECT_BUILDING && SECT(to_room) != SECT_MONUMENT_CLOSED && SECT(to_room) != SECT_MONUMENT_OPEN && SECT(to_room) != SECT_MULTI) {
				if (CAN_SEE_IN_DARK_ROOM(ch, to_room))
					for (c = world[to_room].people; c; c = c->next_in_room)
						if (CAN_SEE(ch, c))
							sprintf(buf+strlen(buf), "%s, ", PERS(c, c, 0));
				}
			}

		/* Now, we clean up that buf */
		if (*buf) {
			sprintf(buf+strlen(buf)-2, ".\r\n");

			for (i = strlen(buf)-1; i > 0; i--)
				if (buf[i] == ',') {
					sprintf(buf1, buf + i+1);
					buf[i] = '\0';
					strcat(buf, " and");
					strcat(buf, buf1);
					break;
					}
			msg_to_char(ch, "You see %s", buf);
			}
		else
			msg_to_char(ch, "You don't see anyone in that direction.\r\n");
		}
	}


void look_in_obj(Creature ch, char *arg) {
	extern const char *color_liquid[];
	extern const char *fullness[];
	Object obj = NULL;
	Creature dummy = NULL;
	int amt, bits;

	if (!*arg)
		send_to_char("Look in what?\r\n", ch);
	else if (!(bits = generic_find(arg, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP, ch, &dummy, &obj))) {
		sprintf(buf, "There doesn't seem to be %s %s here.\r\n", AN(arg), arg);
		send_to_char(buf, ch);
		}
	else if ((GET_OBJ_TYPE(obj) != ITEM_DRINKCON) && (GET_OBJ_TYPE(obj) != ITEM_CORPSE) && (GET_OBJ_TYPE(obj) != ITEM_CONTAINER) && (GET_OBJ_TYPE(obj) != ITEM_CART))
		send_to_char("There's nothing inside that!\r\n", ch);
	else {
		if (GET_OBJ_TYPE(obj) == ITEM_CONTAINER || GET_OBJ_TYPE(obj) == ITEM_CORPSE || GET_OBJ_TYPE(obj) == ITEM_CART) {
			if (OBJVAL_FLAGGED(obj, CONT_CLOSED) && GET_OBJ_TYPE(obj) != ITEM_CORPSE && GET_OBJ_TYPE(obj) != ITEM_CART)
				send_to_char("It is closed.\r\n", ch);
			else {
				send_to_char(fname(obj->name), ch);
				switch (bits) {
					case FIND_OBJ_INV:
						send_to_char(" (carried): \r\n", ch);
						break;
					case FIND_OBJ_ROOM:
						send_to_char(" (here): \r\n", ch);
						break;
					case FIND_OBJ_EQUIP:
						send_to_char(" (used): \r\n", ch);
						break;
					}

				list_obj_to_char(obj->contains, ch, 2, TRUE);
				}
			}
		else {		/* item must be a fountain or drink container */
			if (GET_OBJ_VAL(obj, 1) <= 0)
				send_to_char("It is empty.\r\n", ch);
			else {
				if (GET_OBJ_VAL(obj,0) <= 0 || GET_OBJ_VAL(obj,1)>GET_OBJ_VAL(obj,0)) {
					sprintf(buf, "Its contents seem somewhat murky.\r\n"); /* BUG */
					}
				else {
					amt = (GET_OBJ_VAL(obj, 1) * 3) / GET_OBJ_VAL(obj, 0);
					sprinttype(GET_OBJ_VAL(obj, 2), color_liquid, buf2);
					sprintf(buf, "It's %sfull of a %s liquid.\r\n", fullness[amt], buf2);
					}
				send_to_char(buf, ch);
				}
			}
		}
	}


char *find_exdesc(char *word, struct extra_descr_data *list) {
	struct extra_descr_data *i;

	for (i = list; i; i = i->next)
		if (isname(word, i->keyword))
			return (i->description);

	return (NULL);
	}


/*
 * Given the argument "look at <target>", figure out what object or char
 * matches the target.  First, see if there is another char in the room
 * with the name.  Then check local objs for exdescs.
 *
 * Thanks to Angus Mezick <angus@EDGIL.CCMAIL.COMPUSERVE.COM> for the
 * suggested fix to this problem.
 */
void look_at_target(Creature ch, char *arg) {
	int bits, found = FALSE, j, fnum, i = 0;
	Creature found_char = NULL;
	Object obj, found_obj = NULL;
	char *desc;

	if (!ch->desc)
		return;

	if (!*arg) {
		send_to_char("Look at what?\r\n", ch);
		return;
		}

	bits = generic_find(arg, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_OBJ_EQUIP | FIND_CHAR_ROOM, ch, &found_char, &found_obj);

	/* Is the target a character? */
	if (found_char != NULL) {
		look_at_char(found_char, ch, TRUE, FALSE);
		if (ch != found_char) {
			if (CAN_SEE(found_char, ch)) {
				act("$n looks at you.", TRUE, ch, 0, found_char, TO_VICT);
				act("$n looks at $N.", TRUE, ch, 0, found_char, TO_NOTVICT);
				}
			}
		return;
		}

	/* Strip off "number." from 2.foo and friends. */
	if (!(fnum = get_number(&arg))) {
		send_to_char("Look at what?\r\n", ch);
		return;
		}

	/* Does the argument match an extra desc in the char's inventory? */
	for (obj = ch->carrying; obj && !found; obj = obj->next_content) {
		if (CAN_SEE_OBJ(ch, obj))
			if ((desc = find_exdesc(arg, obj->ex_description)) != NULL && ++i == fnum) {
				send_to_char(desc, ch);
				found = TRUE;
				}
		}

	/* Does the argument match an extra desc in the char's equipment? */
	for (j = 0; j < NUM_WEARS && !found; j++)
		if (GET_EQ(ch, j) && CAN_SEE_OBJ(ch, GET_EQ(ch, j)))
			if ((desc = find_exdesc(arg, GET_EQ(ch, j)->ex_description)) != NULL && ++i == fnum) {
				send_to_char(desc, ch);
				found = TRUE;
				}

	/* Does the argument match an extra desc of an object in the room? */
	for (obj = world[ch->in_room].contents; obj && !found; obj = obj->next_content)
		if (CAN_SEE_OBJ(ch, obj))
			if ((desc = find_exdesc(arg, obj->ex_description)) != NULL && ++i == fnum) {
				send_to_char(desc, ch);
				found = TRUE;
				}

	/* If an object was found back in generic_find */
	if (bits) {
		if (!found)
			show_obj_to_char(found_obj, ch, 5);	/* Show no-description */
		else
			show_obj_to_char(found_obj, ch, 6);	/* Find hum, glow etc */
		}
	else if (!found)
		send_to_char("You do not see that here.\r\n", ch);
	}


ACMD(do_look) {
	char arg2[MAX_INPUT_LENGTH];
	int look_type;

	if (!ch->desc)
		return;

	if (GET_POS(ch) < POS_SLEEPING)
		send_to_char("You can't see anything but stars!\r\n", ch);
	else if (AFF_FLAGGED(ch, AFF_BLIND))
		send_to_char("You can't see a damned thing, you're blind!\r\n", ch);
	else if (!CAN_SEE_IN_DARK_ROOM(ch, ch->in_room) && (IS_COMPLETE(ch->in_room) || ALWAYS_CLOSED(ch->in_room)) && (SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_INSIDE)) {
		send_to_char("It is pitch black...\r\n", ch);
		list_char_to_char(world[ch->in_room].people, ch);	/* glowing red eyes */
		}
	else {
		half_chop(argument, arg, arg2);

		if (!*arg)			/* "look" alone, without an argument at all */
			look_at_room(ch);
		else if (is_abbrev(arg, "in"))
			look_in_obj(ch, arg2);
		/* did the char type 'look <direction>?' */
		else if ((look_type = parse_direction(arg)) >= 0)
			look_in_direction(ch, look_type);
		else if (is_abbrev(arg, "at"))
			look_at_target(ch, arg2);
		else
			look_at_target(ch, arg);
		}
	}


ACMD(do_examine) {
	Creature tmp_char;
	Object tmp_object;

	one_argument(argument, arg);

	if (!*arg) {
		send_to_char("Examine what?\r\n", ch);
		return;
		}
	look_at_target(ch, arg);

	generic_find(arg, FIND_OBJ_INV | FIND_OBJ_ROOM | FIND_CHAR_ROOM | FIND_OBJ_EQUIP, ch, &tmp_char, &tmp_object);

	if (tmp_object) {
		if ((GET_OBJ_TYPE(tmp_object) == ITEM_DRINKCON) || (GET_OBJ_TYPE(tmp_object) == ITEM_CONTAINER) || (GET_OBJ_TYPE(tmp_object) == ITEM_CORPSE) || (GET_OBJ_TYPE(tmp_object) == ITEM_CART)) {
			send_to_char("When you look inside, you see:\r\n", ch);
			look_in_obj(ch, arg);
			}
		}
	}


void display_score_to_char(Creature ch, Creature to) {
	struct time_info_data *real_time_passed(time_t t2, time_t t1);
	extern const char *materials[];
	extern struct discipline_data path[];

	int i, j, c;
	struct time_info_data playing_time;


	msg_to_char(to, " +------------------------------ EmpireMUD AD --------------------------------+\r\n");

	msg_to_char(to, "  Name: %-18.18s", PERS(ch, ch, 1));
	if (real_empire(GET_LOYALTY(ch)) != -1)
		msg_to_char(to, " Rank: %s\r\n", empire[real_empire(GET_LOYALTY(ch))].rank[GET_RANK(ch)-1]);
	else
		msg_to_char(to, "\r\n");

	sprintf(buf, "%d point%s", GET_EXPERIENCE(ch), GET_EXPERIENCE(ch) != 1 ? "s" : "");

	playing_time = *real_time_passed((time(0) - ch->player.time.logon) + ch->player.time.played, 0);
	sprintf(buf1, "%dd, %dh", playing_time.day, playing_time.hours);

	if (IS_WEREWOLF(ch))
		sprintf(buf2, "Breed: %s", breed[(int) GET_BREED(ch)]);
	else
		*buf2 = '\0';

	msg_to_char(to, "  Experience: %-12.12s Play Time: %-13.13s %s\r\n", buf, buf1, buf2);

	if (IS_VAMPIRE(ch) && GET_REAL_AGE(ch) > GET_AGE(ch))
		sprintf(buf, "%d/%d years", GET_AGE(ch), GET_REAL_AGE(ch));
	else
		sprintf(buf, "%d years", GET_AGE(ch));
	msg_to_char(to, "  Age: %-20.20s", buf);

	if (IS_VAMPIRE(ch))
		msg_to_char(to, "Clan: %-18.18s Generation: %d\r\n", clan[(int) GET_CLAN(ch)].name, GET_GENERATION(ch));
	else if (IS_WEREWOLF(ch))
		msg_to_char(to, "Tribe: %-17.17s Auspice: %s\r\n", tribe[(int) GET_TRIBE(ch)].name, auspice[(int) GET_AUSPICE(ch)]);
	else if (IS_GHOUL(ch))
		msg_to_char(to, "Status: Ghoul\r\n");
	else
		msg_to_char(to, "\r\n");

	msg_to_char(to, " +------------------------------- Attributes ---------------------------------+\r\n");
	display_attributes(ch, to, FALSE);

	msg_to_char(to, "+------------------------------- Abilities ----------------------------------+\r\n");
	display_abilities(ch, to);

	if (IS_VAMPIRE(ch) || IS_GHOUL(ch)) {
		*buf = '\0';
		for (i = 0, c = 0; i < NUM_DISCS; i++)
			if (GET_DISC(ch, i) > 0) {
				sprintf(buf + strlen(buf), " %-14.14s", disc[i].name);
				for (j = 1; j <= 10; j++) {
					if (GET_DISC(ch, i) >= j)
						strcat(buf, "*");
					else
						strcat(buf, " ");
					}
				if (!(++c % 3))
					strcat(buf, "\r\n ");
				}
		if (c % 3)
			strcat(buf, "\r\n ");
		if (*buf) {
			msg_to_char(to, "+------------------------------- Disciplines --------------------------------+\r\n ");
			msg_to_char(to, buf);
			}

		if (GET_THAUMATURGY(ch)) {
			*buf = '\0';
			for (i = 0, c = 0; i < NUM_PATHS; i++)
				if (GET_PATH(ch, i) > 0) {
					sprintf(buf + strlen(buf), " %-19.19s", path[i].name);
					for (j = 1; j <= 5; j++) {
						if (GET_PATH(ch, i) >= j)
							strcat(buf, "*");
						else
							strcat(buf, " ");
						}
					if (!(++c % 3))
						strcat(buf, "\r\n ");
					}
			if (c % 3)
				strcat(buf, "\r\n ");
			if (*buf) {
				msg_to_char(to, "+---------------------------------- Paths ----------------------------------+\r\n ");
				msg_to_char(to, buf);
				}
			}
		}

	msg_to_char(to, "+----------------------------------------------------------------------------+\r\n ");

	/* Willpower setup into buf */
	sprintf(buf, "%-14.14s", "Willpower");
	for (i = 0; i < 10; i++)
		if (GET_MAX_WILLPOWER(ch) > i)
			strcat(buf, "*");
		else
			strcat(buf, " ");

	/* Humanity setup into buf1 */
	sprintf(buf1, "%-14.14s", "Humanity");
	for (i = 0; i < 10; i++)
		if (GET_HUMANITY(ch) > i)
			strcat(buf1, "*");
		else
			strcat(buf1, " ");

	/* Current Willpower setup into buf2 */
	sprintf(buf2, "%-15.15s", "");
	for (i = 0; i < 10; i++)
		if (GET_WILLPOWER(ch) > i)
			strcat(buf2, "o");
		else
			strcat(buf2, " ");

	/* These are displayed a little different for vampires */
	if (IS_VAMPIRE(ch)) {
		/* Line 1: 2 place-holders, then Willpower, leave the third column for now.. */
		msg_to_char(to, " %-14.14s%-10.10s %s ", "", "", buf);

		msg_to_char(to, "%-14.14s", "Conscience");
		for (i = 0; i < 10; i++)
			if (GET_CONSCIENCE(ch) > i)
				msg_to_char(to, "*");
			else
				msg_to_char(to, " ");
		msg_to_char(to, "\r\n");

		/* Line 2: 2 place-holders, Current Willpower, leave third column */
		msg_to_char(to, " %-14.14s%-10.10s %s ", "", "", buf2);

		msg_to_char(to, "%-14.14s", "Self-Control");
		for (i = 0; i < 10; i++)
			if (GET_SELF_CONTROL(ch) > i)
				msg_to_char(to, "*");
			else
				msg_to_char(to, " ");
		msg_to_char(to, "\r\n");

		/* Line 3: 2 place-holders, Humanity, leave third column */
		msg_to_char(to, " %-14.14s%-10.10s  %s ", "", "", buf1);

		msg_to_char(to, "%-14.14s", "Courage");
		for (i = 0; i < 10; i++)
			if (GET_COURAGE(ch) > i)
				msg_to_char(to, "*");
			else
				msg_to_char(to, " ");
		msg_to_char(to, "\r\n");
		}
	else {
		msg_to_char(to, " %s %s\r\n", buf, buf1);
		msg_to_char(to, " %s\r\n", buf2);
		}

	/* Gods and Immortals: */
	if (IS_GOD(ch) || IS_IMMORTAL(ch)) {
		*buf = '\0';
		for (i = 0, j = 0; i < NUM_ITEM_MATS; i++)
			if (GET_RESOURCE(ch, i))
				sprintf(buf + strlen(buf), "  %-14.14s %-6d%s", materials[i], GET_RESOURCE(ch, i), !(++j % 3) ? "\r\n" : "  ");
		if (j % 3)
			strcat(buf, "\r\n");
		if (*buf)
			msg_to_char(to, " +------------------------------- Resources ----------------------------------+\r\n%s", buf);
		}
	}


ACMD(do_score) {
	Creature victim;

	if (REAL_NPC(ch))
		return;

	one_argument(argument, arg);

	if (IS_IMMORTAL(ch) && *arg) {
		if (!(victim = get_char_vis(ch, arg, FIND_CHAR_WORLD)))
			msg_to_char(ch, NOPERSON);
		else if (IS_NPC(victim))
			msg_to_char(ch, "You can't get a score sheet for an NPC.\r\n");
		else if (GET_REAL_LEVEL(victim) > GET_REAL_LEVEL(ch))
			msg_to_char(ch, "You can't get a score sheet for someone of a higher level!\r\n");
		else
			display_score_to_char(victim, ch);
		return;
		}

	display_score_to_char(ch, ch);
	}

/* Form is like affects only designed to tell you the name of your shape */
ACMD(do_form) {
	/* See What form you are in */
	if(GET_MORPH(ch) == MORPH_NONE){
			msg_to_char(ch, "You are currently in homid form.\r\n");
		} else if(GET_MORPH(ch) == MORPH_GLABRO){
			msg_to_char(ch, "You are currently in glabro form.\r\n");
		} else if(GET_MORPH(ch) == MORPH_CRINOS){
        	        msg_to_char(ch, "You are currently in crino form.\r\n");
                } else if(GET_MORPH(ch) == MORPH_HISPO){
                	msg_to_char(ch, "You are currently in hispo form.\r\n");
                } else if(GET_MORPH(ch) == MORPH_LUPUS){
	                msg_to_char(ch, "You are currently in lupus form.\r\n");
                } else {
			/* This should never be seen */
			msg_to_char(ch, "You should never see this message, please report it to the admin!\r\n");
		}


	}

ACMD(do_affects) {
	int i;

	if (IS_NPC(ch))
		return;

	msg_to_char(ch, "  Affects:\r\n");

	/* Conditions */
	/* This reports conditions all on one line. */
	sprintf(buf1, "   You are ");
	if (GET_COND(ch, FULL) >= 420)
		strcat(buf1, "hungry, ");
	if (GET_COND(ch, THIRST) >= 345)
		strcat(buf1, "thirsty, ");
	if (GET_COND(ch, DRUNK) > 150)
		strcat(buf1, "inebriated, ");
	if (GET_COND(ch, TIRED) >= 345)
		strcat(buf1, "exhausted, ");

	if (strlen(buf1) > 13) {	/* We have a condition */
		buf1[strlen(buf1)-2] = '\0';	/* This removes the final ", " */
		for (i = strlen(buf1); i >= 0; i--)
			if (buf1[i] == ',') {	/* Looking for that last ',' */
				strcpy(buf2, buf1 + i + 1);
				sprintf(buf1 + i, " and%s", buf2);
				break;
				}
		msg_to_char(ch, "%s.\r\n", buf1);
		}

	/* Morph */
	if (GET_MORPH(ch) != MORPH_NONE)
		msg_to_char(ch, "   You are in the form of %s!\r\n", morph_string(ch, MORPH_STRING_NAME));

	/*
	 * Other Affects
	 *  I've decided it's unrealistic to list all affects.. how would you know
	 *  how many hours of a charm you have left?  How do you know it's really
	 *  even still active until you try to use it?
	 */
	if (AFF_FLAGGED(ch, AFF_BLIND))
		msg_to_char(ch, "   You have been blinded!\r\n");
	if (AFF_FLAGGED(ch, AFF_CHARM))
		msg_to_char(ch, "   You have been charmed!\r\n");
	if (HAS_INFRA(ch))
		msg_to_char(ch, "   Your eyes glow red.\r\n");
	if (AFF_FLAGGED(ch, AFF_INVISIBLE))
		msg_to_char(ch, "   You're invisible!\r\n");
	if (DSC_FLAGGED(ch, DSC_HEIGHTENED_SENSES))
		msg_to_char(ch, "   Your perceptions are heightened.\r\n");
	if (DSC_FLAGGED(ch, DSC_UNSEEN_PRESENCE))
		msg_to_char(ch, "   You are walking through the shadows, out of sight!\r\n");
	if (DSC_FLAGGED(ch, DSC_CANT_SPEND_WILLPOWER))
		msg_to_char(ch, "   Your Beast is cowering in the depths of your mind!\r\n");
	if (DSC_FLAGGED(ch, DSC_TALONS_OF_THE_BEAST))
		msg_to_char(ch, "   Your fingers are bent into hideous talons!\r\n");
	if (DSC_FLAGGED(ch, DSC_EARTHMELD))
		msg_to_char(ch, "   You are interred within the earth!\r\n");
	if (DSC_FLAGGED(ch, DSC_AWE))
		msg_to_char(ch, "   You have a supernatural aura of desire!\r\n");
	if (DSC_FLAGGED(ch, DSC_ENTRANCEMENT))
		msg_to_char(ch, "   You have been entranced!\r\n");
	if (DSC_FLAGGED(ch, DSC_MAJESTY))
		msg_to_char(ch, "   You have a majestic aura about you!\r\n");
	if (DSC_FLAGGED(ch, DSC_TONGUE_OF_THE_ASP))
		msg_to_char(ch, "   Your tongue is long and forked!\r\n");
	if (DSC_FLAGGED(ch, DSC_BITUMENOUS_FLESH))
		msg_to_char(ch, "   You are mummified in hardened flesh!\r\n");
	if (DSC_FLAGGED(ch, DSC_ARMOR_OF_VITALITY))
		msg_to_char(ch, "   Your flesh is as hard as marble!\r\n");
	if (DSC_FLAGGED(ch, DSC_MASK))
		msg_to_char(ch, "   Your Cainite appearance is masked!\r\n");
	if (DSC_FLAGGED(ch, DSC_SOUL_MASK))
		msg_to_char(ch, "   Your aura is masked!\r\n");
	}


ACMD(do_inventory) {
	void inventory_store_building(Creature ch);

	send_to_char("You are carrying:\r\n", ch);
	list_obj_to_char(ch->carrying, ch, 1, TRUE);

	if (STORE_BUILDING(ch->in_room) && CAN_USE_ROOM(ch, ch->in_room, 0))
		inventory_store_building(ch);
	}


ACMD(do_equipment) {
	int i, found = 0;

	send_to_char("You are using:\r\n", ch);
	for (i = 0; i < NUM_WEARS; i++) {
		if (GET_EQ(ch, i)) {
			if (CAN_SEE_OBJ(ch, GET_EQ(ch, i))) {
				send_to_char(where[i], ch);
				show_obj_to_char(GET_EQ(ch, i), ch, 1);
				found = TRUE;
				}
			else {
				send_to_char(where[i], ch);
				send_to_char("Something.\r\n", ch);
				found = TRUE;
				}
			}
		}
	if (!found)
		send_to_char(" Nothing.\r\n", ch);
	}


ACMD(do_time) {
	extern const char *weekdays[];
	extern struct time_info_data time_info;
	const char *suf;
	int weekday, day;

	sprintf(buf, "It is %d o'clock %s, on ", ((time_info.hours % 12 == 0) ? 12 : ((time_info.hours) % 12)), ((time_info.hours >= 12) ? "pm" : "am"));

	/* 30 days in a month */
	weekday = ((30 * time_info.month) + time_info.day + 1) % 7;

	strcat(buf, weekdays[weekday]);
	strcat(buf, "\r\n");
	send_to_char(buf, ch);

	day = time_info.day + 1;	/* day in [1..30] */

	/* 11, 12, and 13 are the infernal exceptions to the rule */
	if ((day % 10) == 1 && day != 11)
		suf = "st";
	else if ((day % 10) == 2 && day != 12)
		suf = "nd";
	else if ((day % 10) == 3 && day != 13)
		suf = "rd";
	else
		suf = "th";

	msg_to_char(ch, "The %d%s day of the month of %s, year %d.\r\n", day, suf, month_name[(int) time_info.month], time_info.year);
	}


ACMD(do_help) {
	extern char *help;
	extern struct help_index_element *help_table;
	extern int top_of_helpt;
	int chk, bot, top, mid, minlen;

	if (!ch->desc)
		return;

	skip_spaces(&argument);

	if (!*argument) {
		page_string(ch->desc, help, 0);
		return;
		}
	if (!help_table) {
		send_to_char("No help available.\r\n", ch);
		return;
		}

	bot = 0;
	top = top_of_helpt;
	minlen = strlen(argument);

	for (;;) {
		mid = (bot + top) / 2;

		if (bot > top) {
			send_to_char("There is no help on that word.\r\n", ch);
			return;
			}
		else if (!(chk = strn_cmp(argument, help_table[mid].keyword, minlen))) {
			/* trace backwards to find first matching entry. Thanks Jeff Fink! */
			while ((mid > 0) && (!(chk = strn_cmp(argument, help_table[mid - 1].keyword, minlen))))
				mid--;
			if (GET_LEVEL(ch) < help_table[mid].level)
				msg_to_char(ch, "There is no help on that word.\r\n");
			else
				page_string(ch->desc, help_table[mid].entry, 0);
			return;
			}
		else {
			if (chk > 0)
				bot = mid + 1;
			else
				top = mid - 1;
			}
		}
	}


/* Estimates length of all color codes in a string */
int count_amp(char *string) {
	int i, t;

	for (i = 0, t = 0; i < strlen(string); i++)
		if (string[i] == '&' || string[i] == '\r')
			t += 2;
	return t;
	}


/* centers 1 line of text (may crash with long lines, use center_full_text) */
char *center_text(char *input) {
	static char output[MAX_STRING_LENGTH];
	int s, t;

	*output = '\0';
	s = (78 - (strlen(input)-count_amp(input))) / 2;
	if (s >= 1)
		for (t = 1; t <= s; t++)
			strcat(output, " ");
	strcat(output, input);
	return output;
	}


/* centers a long line of text (please don't include linebreaks) */
char *center_full_text(char *input) {
	static char out[MAX_STRING_LENGTH];
	char buffer[MAX_INPUT_LENGTH], buffer1[MAX_INPUT_LENGTH], buffer2[MAX_INPUT_LENGTH];

	*buffer = *buffer1 = *buffer2 = '\0';

	strcpy(buffer, input);

	if (strlen(input) > 80) {
		strcpy(buffer1, buffer + 78);
		sprintf(buffer + 78, "\r\n");
		if (strlen(buffer1) > 80) {
			strcpy(buffer2, buffer1 + 78);
			sprintf(buffer1 + 78, "\r\n");
			}
		}

	sprintf(out, "%s", center_text(buffer));
	if (*buffer1)
		strcat(out, center_text(buffer1));
	if (*buffer2)
		strcat(out, center_text(buffer2));
	return out;
	}


char *perform_immort_who(Creature ch, char *name_search, int low, int high, int empire_who, int rp, int mode) {
	static char who_output[MAX_STRING_LENGTH];
	Creature tch;
	Descr d;

	/* who_output is our final message */
	*who_output = '\0';
	/* buf1 will store our string of who's */
	*buf1 = '\0';

	for (d = descriptor_list; d; d = d->next) {
		if (STATE(d) != CON_PLAYING)
			continue;

		if (d->original)
			tch = d->original;
		else if (!(tch = d->character))
			continue;

		if (*name_search && !is_abbrev(name_search, PERS(tch, tch, 1)) && !strstr(GET_TITLE(tch), name_search))
			continue;
		if (!CAN_SEE_NO_DARK(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
			continue;
		if (!IS_IMMORTAL(tch) && !mode)
			continue;
		if (!IS_GOD(tch) && mode)
			continue;
		if (empire_who && real_empire(GET_LOYALTY(ch)) != real_empire(GET_LOYALTY(tch)))
			continue;
		if (rp && !PRF_FLAGGED(tch, PRF_RP))
			continue;

		/* Initialize the buffer */
		*buf = '\0';

		if (real_empire(GET_LOYALTY(tch)) != -1)
			sprintf(buf + strlen(buf), "<%s&0> ", empire[real_empire(GET_LOYALTY(tch))].rank[GET_RANK(tch)-1]);

		sprintf(buf + strlen(buf), "%s%s&0", PERS(tch, tch, 1), GET_TITLE(tch));

		if (IS_AFK(tch))
			strcat(buf, " &1[AFK]&0");
		if ((tch->char_specials.timer * SECS_PER_MUD_HOUR / SECS_PER_REAL_MIN) >= 5)
			sprintf(buf, "%s (idle: %d)", buf, (tch->char_specials.timer * SECS_PER_MUD_HOUR / SECS_PER_REAL_MIN));

		if (PRF_FLAGGED(tch, PRF_RP))
			strcat(buf, " &5[RP]&0");

		if (GET_INVIS_LEV(tch))
			sprintf(buf + strlen(buf), " (i%d)", GET_INVIS_LEV(tch));
		else if (AFF_FLAGGED(tch, AFF_INVISIBLE))
			sprintf(buf + strlen(buf), " (invis)");

		if (PLR_FLAGGED(tch, PLR_MAILING))
			strcat(buf, " &6(mailing)&0");
		else if (PLR_FLAGGED(tch, PLR_WRITING))
			strcat(buf, " &6(writing)&0");

		if (PRF_FLAGGED(tch, PRF_DEAF))
			strcat(buf, " (deaf)");
		if (PRF_FLAGGED(tch, PRF_NOTELL))
			strcat(buf, " (notell)");
		if (PRF_FLAGGED(tch, PRF_NOOOC))
			strcat(buf, " (!ooc)");
		if (PRF_FLAGGED(tch, PRF_NOGOSS))
			strcat(buf, " (!gos)");

		strcat(buf1, strcat(buf, "&0\r\n"));
		}

	if (*buf1) {
		if (mode)
			strcpy(buf, "Gods");
		else
			strcpy(buf, "Administrators");
		strcat(buf, "\r\n");
		if (mode)
			strcat(buf, "----");
		else
			strcat(buf, "--------------");
		sprintf(who_output, "\r\n%s\r\n%s", buf, buf1);
		}
	return (who_output);
	}


char *perform_mort_who(Creature ch, char *name_search, int low, int high, int empire_who, int rp) {
	static char who_output[MAX_STRING_LENGTH];

	Creature tch;
	Descr d;

	/* who_output is our final message */
	*who_output = '\0';
	/* buf1 will store our string of who's */
	*buf1 = '\0';

	for (d = descriptor_list; d; d = d->next) {
		if (STATE(d) != CON_PLAYING)
			continue;

		if (d->original)
			tch = d->original;
		else if (!(tch = d->character))
			continue;

		if (*name_search && !is_abbrev(name_search, PERS(tch, tch, 1)) && !strstr(GET_TITLE(tch), name_search))
			continue;
		if (!CAN_SEE_NO_DARK(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
			continue;
		if (IS_IMMORTAL(tch) || IS_GOD(tch))
			continue;
		if (empire_who && real_empire(GET_LOYALTY(ch)) != real_empire(GET_LOYALTY(tch)))
			continue;
		if (rp && !PRF_FLAGGED(tch, PRF_RP))
			continue;

		/* Initialize the buffer */
		*buf = '\0';

		if (real_empire(GET_LOYALTY(tch)) != -1)
			sprintf(buf + strlen(buf), "<%s&0> ", empire[real_empire(GET_LOYALTY(tch))].rank[GET_RANK(tch)-1]);

		sprintf(buf + strlen(buf), "%s%s&0", PERS(tch, tch, 1), GET_TITLE(tch));

		if (IS_AFK(tch))
			strcat(buf, " &1[AFK]&0");
		if ((tch->char_specials.timer * SECS_PER_MUD_HOUR / SECS_PER_REAL_MIN) >= 5)
			sprintf(buf, "%s (idle: %d)", buf, (tch->char_specials.timer * SECS_PER_MUD_HOUR / SECS_PER_REAL_MIN));

		if (PRF_FLAGGED(tch, PRF_RP))
			strcat(buf, " &5[RP]&0");

		if (GET_INVIS_LEV(tch))
			sprintf(buf + strlen(buf), " (i%d)", GET_INVIS_LEV(tch));
		else if (AFF_FLAGGED(tch, AFF_INVISIBLE))
			sprintf(buf + strlen(buf), " (invis)");

		if (PLR_FLAGGED(tch, PLR_MAILING))
			strcat(buf, " &6(mailing)&0");
		else if (PLR_FLAGGED(tch, PLR_WRITING))
			strcat(buf, " &6(writing)&0");

		if (PRF_FLAGGED(tch, PRF_DEAF))
			strcat(buf, " (deaf)");
		if (PRF_FLAGGED(tch, PRF_NOTELL))
			strcat(buf, " (notell)");
		if (PRF_FLAGGED(tch, PRF_NOOOC))
			strcat(buf, " (!ooc)");
		if (PRF_FLAGGED(tch, PRF_NOGOSS))
			strcat(buf, " (!gos)");

		strcat(buf1, strcat(buf, "&0\r\n"));
		}

	if (*buf1) {
		strcpy(buf, "Mortals\r\n-------");
		sprintf(who_output, "\r\n%s\r\n%s", buf, buf1);
		}
	return (who_output);
	}


#define WHO_FORMAT \
"format: who [minlev[-maxlev]] [-n name] [-o] [-e] [-r]\r\n"

ACMD(do_who) {
	char name_search[MAX_INPUT_LENGTH], output[MAX_STRING_LENGTH], part[MAX_STRING_LENGTH];
	char mode;
	int low = 0, high = LVL_IMPL;
	int rp = 0, empire_who = 0;

	skip_spaces(&argument);
	strcpy(buf, argument);
	name_search[0] = '\0';

	while (*buf) {
		half_chop(buf, arg, buf1);
		if (isdigit(*arg)) {
			sscanf(arg, "%d-%d", &low, &high);
			strcpy(buf, buf1);
			}
		else if (*arg == '-') {
			mode = *(arg + 1);       /* just in case; we destroy arg in the switch */
			switch (mode) {
				case 'r':
					rp = 1;
					strcpy(buf, buf1);
					break;
				case 'l':
					half_chop(buf1, arg, buf);
					sscanf(arg, "%d-%d", &low, &high);
					break;
				case 'n':
					half_chop(buf1, name_search, buf);
					break;
				case 'e':
					empire_who = 1;
					strcpy(buf, buf1);
					break;
				default:
					send_to_char(WHO_FORMAT, ch);
					return;
				}				/* end of switch */
			}
		else {			/* endif */
			send_to_char(WHO_FORMAT, ch);
			return;
			}
  		}				/* end while (parser) */

	*output = '\0';

	/* Immortals first */
	strcat(output, perform_immort_who(ch, name_search, low, high, empire_who, rp, 0));

	/* Gods second */
	strcpy(part, perform_immort_who(ch, name_search, low, high, empire_who, rp, 1));
	if (*part)
		strcat(output, part);

	/* Then mortals */
	strcpy(part, perform_mort_who(ch, name_search, low, high, empire_who, rp));
	if (*part)
		strcat(output, part);

	if (*output)
		page_string(ch->desc, output, 1);
	else
		/* Didn't find a match to set parameters */
		msg_to_char(ch, "You don't see anyone like that.\r\n");
	}


void users_output(Creature to, Creature tch, Descr d, char *name_search, int low, int high, int rp) {
	extern const char *connected_types[];
	Creature ch = tch;
	char levelname[20], *timeptr, idletime[10];
	char line[200], line2[220], state[30];
	const char *format = "%3d %s %-13s %-15s %-3s %-8s ";

	if (!ch && STATE(d) == CON_PLAYING) {
		if (d->original)
			ch = d->original;
		else if (!(ch = d->character))
			return;
		}

	if (ch) {
		if (*name_search && !is_abbrev(name_search, GET_NAME(ch)))
			return;
		if (!CAN_SEE(to, ch) || GET_LEVEL(ch) < low || GET_LEVEL(ch) > high)
			return;
		if (rp && !PRF_FLAGGED(ch, PRF_RP))
			return;
		if (GET_INVIS_LEV(ch) > GET_LEVEL(to))
			return;

		sprintf(levelname, "%d", GET_LEVEL(ch));
		}
	else
		strcpy(levelname, "-");

	if (d) {
		timeptr = asctime(localtime(&d->login_time));
		timeptr += 11;
		*(timeptr + 8) = '\0';
		}
	else
		timeptr = str_dup("");

	if (ch && d && ch == d->original)
		strcpy(state, "Switched");
	else if (d)
		strcpy(state, connected_types[STATE(d)]);
	else
		strcpy(state, "Linkdead");

	if (ch)
		sprintf(idletime, "%3d", ch->char_specials.timer * SECS_PER_MUD_HOUR / SECS_PER_REAL_MIN);
	else
		strcpy(idletime, "");

	sprintf(line, format, d ? d->desc_num : 0, levelname, (ch && ch->player.name) ? ch->player.name : (d && d->character && d->character->player.name) ? d->character->player.name : "UNDEFINED", state, idletime, timeptr);

	if (d && d->host && *d->host && (!ch || GET_LEVEL(ch) <= GET_LEVEL(to)))
		sprintf(line + strlen(line), "[%s]\r\n", d->host);
	else if (d)
		strcat(line, "[Hostname unknown]\r\n");
	else
		strcat(line, "\r\n");

	if (!d)
		sprintf(line2, "&6%s&0", line);
	else if (STATE(d) != CON_PLAYING)
		sprintf(line2, "&2%s&0", line);
	else
		strcpy(line2, line);
	strcpy(line, line2);

	send_to_char(line, to);
	}


#define USERS_FORMAT	\
	"format: users [-l minlevel[-maxlevel]] [-n name] [-h host] [-r] [-p]\r\n"

ACMD(do_users) {
	char mode;
	char name_search[MAX_INPUT_LENGTH], host_search[MAX_INPUT_LENGTH];
	Creature tch;
	Descr d;
	int low = 0, high = LVL_IMPL, num_can_see = 0;
	int rp = 0, playing = 0, deadweight = 0;

	host_search[0] = name_search[0] = '\0';

	strcpy(buf, argument);
	while (*buf) {
		half_chop(buf, arg, buf1);
		if (*arg == '-') {
			mode = *(arg + 1);  /* just in case; we destroy arg in the switch */
			switch (mode) {
				case 'r':
					rp = 1;
					playing = 1;
					strcpy(buf, buf1);
					break;
				case 'p':
					playing = 1;
					strcpy(buf, buf1);
					break;
				case 'd':
					deadweight = 1;
					strcpy(buf, buf1);
					break;
				case 'l':
					playing = 1;
					half_chop(buf1, arg, buf);
					sscanf(arg, "%d-%d", &low, &high);
					break;
				case 'n':
					playing = 1;
					half_chop(buf1, name_search, buf);
					break;
				case 'h':
					playing = 1;
					half_chop(buf1, host_search, buf);
					break;
				default:
					send_to_char(USERS_FORMAT, ch);
					return;
				}				/* end of switch */

			}
		else {			/* endif */
			send_to_char(USERS_FORMAT, ch);
			return;
			}
		}				/* end while (parser) */
	msg_to_char(ch, "Num L Name          State           Idl Login@   Site\r\n");
	msg_to_char(ch, "--- - ------------- --------------- --- -------- ------------------------\r\n");

	one_argument(argument, arg);

	if (!*host_search)
		for (tch = character_list; tch; tch = tch->next) {
			if (IS_NPC(tch) || tch->desc)
				continue;
			users_output(ch, tch, NULL, name_search, low, high, rp);
			num_can_see++;
			}

	for (d = descriptor_list; d; d = d->next) {
		if (STATE(d) != CON_PLAYING && playing)
			continue;
		if (STATE(d) == CON_PLAYING && deadweight)
			continue;
		if (*host_search && !strstr(d->host, host_search))
			continue;
		users_output(ch, NULL, d, name_search, low, high, rp);
		num_can_see++;
		}

	msg_to_char(ch, "\r\n%d visible sockets connected.\r\n", num_can_see);
	}


/* Generic page_string function for displaying text */
ACMD(do_gen_ps) {
	extern char *credits;
	extern char *info;
	extern char *motd;
	extern char *imotd;
	extern char *wizlist;
	extern char *godlist;
	extern char *policies;
	extern char *handbook;
	extern const char *version;

	switch (subcmd) {
		case SCMD_CREDITS:	page_string(ch->desc, credits, 0);		break;
		case SCMD_INFO:		page_string(ch->desc, info, 0);			break;
		case SCMD_WIZLIST:	page_string(ch->desc, wizlist, 0);		break;
		case SCMD_GODLIST:	page_string(ch->desc, godlist, 0);		break;
		case SCMD_HANDBOOK:	page_string(ch->desc, handbook, 0);		break;
		case SCMD_POLICIES:	page_string(ch->desc, policies, 0);		break;
		case SCMD_MOTD:		page_string(ch->desc, motd, 0);			break;
		case SCMD_IMOTD:	page_string(ch->desc, imotd, 0);		break;
		case SCMD_CLEAR:	send_to_char("\033[H\033[J", ch);		break;
		case SCMD_VERSION:	send_to_char(strcat(strcpy(buf, version), "\r\n"), ch);	break;
		default:			log("SYSERR: Unhandled case in do_gen_ps. (%d)", subcmd);			return;
		}
	}


/* Basic distance */
#define distance(x, y, a, b)		((x - a) * (x - a) + (y - b) * (y - b))

void perform_mortal_where(Creature ch, char *arg) {
	register Creature i;
	register Descr d;

	if (!*arg) {
		send_to_char("Players near you\r\n--------------------\r\n", ch);
		for (d = descriptor_list; d; d = d->next) {
			if (STATE(d) != CON_PLAYING || d->character == ch)
				continue;
			if ((i = (d->original ? d->original : d->character)) == NULL)
				continue;
			if (i->in_room == NOWHERE || !CAN_SEE(ch, i))
				continue;
			if (distance(X_COORD(ch->in_room), Y_COORD(ch->in_room), X_COORD(i->in_room), Y_COORD(i->in_room)) > distance(0, 0, 0, 50))
				continue;
			if (AFF_FLAGGED(i, AFF_NO_SEE_IN_ROOM) || MORPH_FLAGGED(i, MORPH_FLAG_NPC))
				continue;
			sprintf(buf, "%-20s - (%3d, %3d) %s\r\n", PERS(i, ch, 0), X_COORD(i->in_room), Y_COORD(i->in_room), get_room_name(i->in_room, 0));
			send_to_char(buf, ch);
			}
		}
	else {			/* print only FIRST char, not all. */
		for (i = character_list; i; i = i->next) {
			if (i->in_room == NOWHERE || i == ch)
				continue;
			if (!CAN_SEE(ch, i))
				continue;
			if (!isname(arg, i->player.name))
				continue;
			if (distance(X_COORD(ch->in_room), Y_COORD(ch->in_room), X_COORD(i->in_room), Y_COORD(i->in_room)) > distance(0, 0, 0, 50))
				continue;
			if (AFF_FLAGGED(i, AFF_NO_SEE_IN_ROOM) || MORPH_FLAGGED(i, MORPH_FLAG_NPC))
				continue;
			sprintf(buf, "%-25s - (%3d, %3d) %s\r\n", PERS(i, ch, 0), X_COORD(i->in_room), Y_COORD(i->in_room), get_room_name(i->in_room, 0));
			send_to_char(buf, ch);
			return;
			}
		send_to_char("No-one around by that name.\r\n", ch);
		}
	}


void print_object_location(int num, Object obj, Creature ch, int recur) {
	if (num > 0)
		sprintf(buf, "O%3d. %-25s - ", num, GET_OBJ_DESC(obj, ch, 1));
	else
		sprintf(buf, "%33s", " - ");

	if (obj->in_room > NOWHERE) {
		sprintf(buf + strlen(buf), "(%d, %d) %s\r\n", X_COORD(obj->in_room), Y_COORD(obj->in_room), get_room_name(obj->in_room, 0));
		send_to_char(buf, ch);
		}
	else if (obj->carried_by) {
		sprintf(buf + strlen(buf), "carried by %s\r\n", PERS(obj->carried_by, ch, 1));
		send_to_char(buf, ch);
		}
	else if (obj->worn_by) {
		sprintf(buf + strlen(buf), "worn by %s\r\n", PERS(obj->worn_by, ch, 1));
		send_to_char(buf, ch);
		}
	else if (obj->in_obj) {
		sprintf(buf + strlen(buf), "inside %s%s\r\n", GET_OBJ_DESC(obj->in_obj, ch, 1), (recur ? ", which is" : " "));
		send_to_char(buf, ch);
		if (recur)
			print_object_location(0, obj->in_obj, ch, recur);
		}
	else {
		sprintf(buf + strlen(buf), "in an unknown location\r\n");
		send_to_char(buf, ch);
		}
	}


void perform_immort_where(Creature ch, char *arg) {
	register Creature i;
	register Object k;
	Descr d;
	int num = 0, found = 0;

	if (!*arg) {
		send_to_char("Players\r\n-------\r\n", ch);
		for (d = descriptor_list; d; d = d->next)
			if (STATE(d) == CON_PLAYING) {
				i = (d->original ? d->original : d->character);
				if (i && CAN_SEE(ch, i) && (i->in_room != NOWHERE)) {
					if (d->original)
						sprintf(buf, "%-20s - (%3d, %3d) %s (in %s)\r\n", GET_NAME(i), X_COORD(d->character->in_room), Y_COORD(d->character->in_room), get_room_name(d->character->in_room, 0), GET_NAME(d->character));
					else
						sprintf(buf, "%-20s - (%3d, %3d) %s\r\n", GET_NAME(i), X_COORD(i->in_room), Y_COORD(i->in_room), get_room_name(i->in_room, 0));
					send_to_char(buf, ch);
					}
				}
		}
	else {
		for (i = character_list; i; i = i->next)
			if (CAN_SEE(ch, i) && i->in_room != NOWHERE && isname(arg, i->player.name)) {
				found = 1;
				sprintf(buf, "M%3d. %-25s - (%3d, %3d) %s\r\n", ++num, GET_NAME(i), X_COORD(i->in_room), Y_COORD(i->in_room), get_room_name(i->in_room, 0));
				send_to_char(buf, ch);
				}
		for (num = 0, k = object_list; k; k = k->next)
			if (CAN_SEE_OBJ(ch, k) && isname(arg, k->name)) {
				found = 1;
				print_object_location(++num, k, ch, TRUE);
				}
			if (!found)
				send_to_char("Couldn't find any such thing.\r\n", ch);
		}
	}


ACMD(do_where) {
	one_argument(argument, arg);

	if (GET_LEVEL(ch) >= LVL_GOD)
		perform_immort_where(ch, arg);
	else
		perform_mortal_where(ch, arg);
	}


ACMD(do_diagnose) {
	Creature vict;

	one_argument(argument, buf);

	if (*buf) {
		if (!(vict = get_char_vis(ch, buf, FIND_CHAR_ROOM)))
			send_to_char(NOPERSON, ch);
		else
			diag_char_to_char(vict, ch);
		}
	else {
		if (FIGHTING(ch))
			diag_char_to_char(FIGHTING(ch), ch);
		else
			send_to_char("Diagnose whom?\r\n", ch);
		}
	}


void list_lore_to_char(Creature ch, Creature to) {
	void clean_lore(Creature ch);
	extern struct time_info_data *mud_time_passed(time_t t2, time_t t1);
	extern char *get_name_by_id(long id);
	extern long load_time();

	struct lore_data *lore;
	char daystring[MAX_INPUT_LENGTH];
	struct time_info_data t;
	int e;
	long beginning_of_time = load_time();

	msg_to_char(to, "%s's lore:\r\n", PERS(ch, ch, 1));
	clean_lore(ch);

	for (lore = GET_LORE(ch); lore; lore = lore->next) {
		t = *mud_time_passed((time_t) beginning_of_time, (time_t) lore->date);

		strcpy(buf, month_name[((int) t.month >= 0 ? (int) t.month : (int) -t.month)]);
		if (!strncmp(buf, "the ", 4))
			strcpy(buf1, buf + 4);
		else
			strcpy(buf1, buf);

		sprintf(daystring, "%d, %s, Year %d", (int) -t.day + 1, buf1, -t.year + YEAR_ADD * 2);

		switch (lore->type) {
			case LORE_FOUND_EMPIRE:
				if ((e = real_empire(lore->value)) != -1)
					msg_to_char(to, " Proudly founded %s%s&0 on %s.\r\n", empire[e].banner, empire[e].name, daystring);
				break;
			case LORE_JOIN_EMPIRE:
				if ((e = real_empire(lore->value)) != -1)
					msg_to_char(to, " Honorably accepted into %s%s&0 on %s.\r\n", empire[e].banner, empire[e].name, daystring);
				break;
			case LORE_DEFECT_EMPIRE:
				if ((e = real_empire(lore->value)) != -1)
					msg_to_char(to, " Defected from %s%s&0 on %s.\r\n", empire[e].banner, empire[e].name, daystring);
				break;
			case LORE_KICKED_EMPIRE:
				if ((e = real_empire(lore->value)) != -1)
					msg_to_char(to, " Dishonorably discharged from %s%s&0 on %s.\r\n", empire[e].banner, empire[e].name, daystring);
				break;
			case LORE_PLAYER_KILL:
				msg_to_char(to, " Killed %s in battle on %s.\r\n", get_name_by_id(lore->value) ? CAP(get_name_by_id(lore->value)) : "an unknown foe", daystring);
				break;
			case LORE_PLAYER_DEATH:
				msg_to_char(to, " Slain by %s in battle on %s.\r\n", get_name_by_id(lore->value) ? CAP(get_name_by_id(lore->value)) : "an unknown foe", daystring);
				break;
			case LORE_TOWER_DEATH:
				msg_to_char(to, " Killed by a guard tower on %s.\r\n", daystring);
				break;
			case LORE_DEATH:
				msg_to_char(to, " Died on %s.\r\n", daystring);
				break;
			case LORE_START_VAMPIRE:
				if (IS_VAMPIRE(to))
					msg_to_char(to, " Embraced prior to %s.\r\n", daystring);
				break;
			case LORE_EMBRACE_VAMPIRE:
				if (IS_VAMPIRE(to))
					msg_to_char(to, " Embraced by %s on %s.\r\n", get_name_by_id(lore->value) ? CAP(get_name_by_id(lore->value)) : "an unknown Cainite", daystring);
				break;
			case LORE_MAKE_VAMPIRE:
				if (IS_VAMPIRE(to))
					msg_to_char(to, " Sired %s on %s.\r\n", get_name_by_id(lore->value) ? CAP(get_name_by_id(lore->value)) : "an unknown Cainite", daystring);
				break;
			case LORE_MAKE_GHOUL:
				if (IS_VAMPIRE(to) || to == ch)
					msg_to_char(to, " Made a ghoul by %s on %s.\r\n", get_name_by_id(lore->value) ? CAP(get_name_by_id(lore->value)) : "an unknown Cainite", daystring);
				break;
			case LORE_TORPOR:
				if (IS_VAMPIRE(to))
					msg_to_char(to, " Entered into torpor on %s.\r\n", daystring);
				break;
			}
		}
	}


void whois_display(Creature ch, Creature vict) {
	extern const char *level_names[][2];

	int e;

	if (GET_EMAIL(vict) && (!PRF_FLAGGED(vict, PRF_HIDEEMAIL) || GET_LEVEL(ch) == LVL_TOP))
		sprintf(buf1, "E-mail: %s\r\n", GET_EMAIL(vict));
	else
		*buf1 = '\0';

	if (IS_VAMPIRE(ch) && IS_VAMPIRE(vict) && GET_CLAN(vict) == GET_CLAN(ch) && GET_GENERATION(vict) >= GET_GENERATION(ch))
		sprintf(buf2, "Clan: %s\r\n", clan[(int) GET_CLAN(vict)].name);
	else if (IS_WEREWOLF(ch) && IS_WEREWOLF(vict) && GET_TRIBE(vict) == GET_TRIBE(ch))
		sprintf(buf2, "Tribe: %s\r\n", tribe[(int) GET_TRIBE(vict)].name);
	else
		*buf2 = '\0';

	sprintf(buf, "%s%s&0\r\n"
				 "Status: %s%s\r\n"
				 "%s%s",
				 PERS(vict, vict, 1), GET_TITLE(vict), level_names[(int) GET_LEVEL(vict)][1], IS_HARDCORE(vict) ? ", &1HARDCORE&0" : "", buf1, buf2);

	if ((e = real_empire(GET_LOYALTY(vict))) != -1)
		sprintf(buf + strlen(buf), "%s of %s%s&0\r\n"
								   "Territory: %d, Members: %d\r\n",
								   empire[e].rank[GET_RANK(vict)-1], empire[e].banner, empire[e].name, empire[e].territory, empire[e].members);

	msg_to_char(ch, buf);

	if (GET_LORE(vict))
		list_lore_to_char(vict, ch);
	}

ACMD(do_whois) {
	void read_lore(Creature ch);

	Creature victim = 0;
	struct char_file_u tmp_store;

	skip_spaces(&argument);

	if (!*argument) {
		send_to_char("Who is whom?\r\n", ch);
		return;
		}
	CREATE(victim, struct char_data, 1);
	clear_char(victim);
	if (load_char(argument, &tmp_store) > -1) {
		store_to_char(&tmp_store, victim);
		if (!PLR_FLAGGED(victim, PLR_DELETED)) {
			read_lore(victim);
			whois_display(ch, victim);
			}
		else
			send_to_char("There is no such player.\r\n", ch);
		}
	else
		send_to_char("There is no such player.\r\n", ch);
	free(victim);
	}