circlemud_squared_0.5.153/cnf/
circlemud_squared_0.5.153/etc/
circlemud_squared_0.5.153/etc/etc/
circlemud_squared_0.5.153/etc/house/
circlemud_squared_0.5.153/etc/misc/
circlemud_squared_0.5.153/etc/plralias/A-E/
circlemud_squared_0.5.153/etc/plralias/F-J/
circlemud_squared_0.5.153/etc/plralias/K-O/
circlemud_squared_0.5.153/etc/plralias/P-T/
circlemud_squared_0.5.153/etc/plralias/U-Z/
circlemud_squared_0.5.153/etc/plralias/ZZZ/
circlemud_squared_0.5.153/etc/plrobjs/
circlemud_squared_0.5.153/etc/plrobjs/A-E/
circlemud_squared_0.5.153/etc/plrobjs/F-J/
circlemud_squared_0.5.153/etc/plrobjs/K-O/
circlemud_squared_0.5.153/etc/plrobjs/P-T/
circlemud_squared_0.5.153/etc/plrobjs/U-Z/
circlemud_squared_0.5.153/etc/plrobjs/ZZZ/
circlemud_squared_0.5.153/etc/text/
circlemud_squared_0.5.153/etc/text/help/
circlemud_squared_0.5.153/src/util/
circlemud_squared_0.5.153/src/util/worldconv/
/**
 * @file act_info.c
 * @ingroup common
 *
 * Player-level commands of an informative nature
 *
 * @author Part of CircleMUD
 *
 * @par Copyright:
 *   Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University<br>
 *   CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.
 *
 * @par
 *   All rights reserved.  See license.doc for complete information.
 *
 * @package cs
 * @version 1.0
 */

#include "structs.h"
#include "log.h"
#include "utils.h"
#include "comm.h"
#include "command.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "spells.h"
#include "screen.h"
#include "constants.h"
#include "base.h"
#include "zone.h"
#include "room.h"
#include "item.h"

/* extern variables */
extern int top_of_helpt;
extern helpData_t *help_table;
extern char *help;
extern timeInfoData_t time_info;

extern char *credits;
extern char *news;
extern char *info;
extern char *motd;
extern char *imotd;
extern char *wizlist;
extern char *immlist;
extern char *policies;
extern char *handbook;
extern char *class_abbrevs[];

/* extern functions */
bitvector_t find_class_bitvector(const char *arg);
int level_exp(int chclass, int level);
char *title_male(int chclass, int level);
char *title_female(int chclass, int level);
timeInfoData_t *real_time_passed(time_t t2, time_t t1);
int compute_armor_class(charData_t *ch);

/* local functions */
int sort_commands_helper(const void *a, const void *b);
void print_object_location(int num, itemData_t *obj, charData_t *ch, int recur);
void show_itemData_toChar(itemData_t *obj, charData_t *ch, int mode);
void list_itemData_toChar(itemData_t *list, charData_t *ch, int mode, int show);
void show_obj_modifiers(itemData_t *obj, charData_t *ch);
void perform_mortal_where(charData_t *ch, char *arg);
void perform_immort_where(charData_t *ch, char *arg);
void sort_commands(void);
void diag_char_to_char(charData_t *i, charData_t *ch);
void look_at_char(charData_t *i, charData_t *ch);
void list_one_char(charData_t *i, charData_t *ch);
void list_char_to_char(charData_t *list, charData_t *ch);
void do_auto_exits(charData_t *ch);
void look_in_direction(charData_t *ch, int dir);
void look_in_obj(charData_t *ch, char *arg);
char *find_exdesc(char *word, extraDescData_t *list);
void look_at_target(charData_t *ch, char *arg);

/* local globals */
int *cmd_sort_info;

/* For show_itemData_toChar 'mode'.	/-- arbitrary */
#define SHOW_ITEM_LONG		0
#define SHOW_ITEM_SHORT		1
#define SHOW_ITEM_ACTION		2

/**
 * Send a message to Character ch with information of Object obj
 * @param obj Object to show
 * @param ch Character to show the object to
 * @param mode Numeric value of which data to show
 * @returns none
 */
void show_itemData_toChar(itemData_t *obj, charData_t *ch, int mode)
{
  if (!obj || !ch) {
    log("SYSERR: NULL pointer in show_itemData_toChar(): obj=%p ch=%p", obj, ch);
    return;
  }

  switch (mode) {
  case SHOW_ITEM_LONG:
    send_to_char(ch, "%s", obj->description);
    break;

  case SHOW_ITEM_SHORT:
    send_to_char(ch, "%s", obj->shortDescription);
    break;

  case SHOW_ITEM_ACTION:
    switch (GET_ITEM_TYPE(obj)) {
    case ITEM_NOTE:
      if (obj->actionDescription) {
        char notebuf[MAX_NOTE_LENGTH + 64];

        snprintf(notebuf, sizeof(notebuf), "There is something written on it:\r\n\r\n%s", obj->actionDescription);
        page_string(ch->desc, notebuf, TRUE);
      } else
	send_to_char(ch, "It's blank.\r\n");
      return;

    case ITEM_DRINKCON:
      send_to_char(ch, "It looks like a drink container.");
      break;

    default:
      send_to_char(ch, "You see nothing special..");
      break;
    }
    break;

  default:
    log("SYSERR: Bad display mode (%d) in show_itemData_toChar().", mode);
    return;
  }

  show_obj_modifiers(obj, ch);
  send_to_char(ch, "\r\n");
}

/**
 * Check for and display extra object information based on object flags and character
 * @param obj The Object to be checked
 * @param ch The Character who's also being checked
 * @returns none
 */
void show_obj_modifiers(itemData_t *obj, charData_t *ch)
{
  if (ITEM_FLAGGED(obj, ITEM_INVISIBLE))
    send_to_char(ch, " (invisible)");

  if (ITEM_FLAGGED(obj, ITEM_BLESS) && AFF_FLAGGED(ch, AFF_DETECT_ALIGN))
    send_to_char(ch, " ..It glows blue!");

  if (ITEM_FLAGGED(obj, ITEM_MAGIC) && AFF_FLAGGED(ch, AFF_DETECT_MAGIC))
    send_to_char(ch, " ..It glows yellow!");

  if (ITEM_FLAGGED(obj, ITEM_GLOW))
    send_to_char(ch, " ..It has a soft glowing aura!");

  if (ITEM_FLAGGED(obj, ITEM_HUM))
    send_to_char(ch, " ..It emits a faint humming sound!");
}

/**
 * Pass every object in a list of objects to show_itemData_toChar
 * @param list List of objects
 * @param ch Character to list objects to
 * @param mode Numeric value of which data to show
 * @param show Send "Nothing" message to Character if no objects found
 * @returns none
 * @sa show_itemData_toChar
 */
void list_itemData_toChar(itemData_t *list, charData_t *ch, int mode, int show)
{
  itemData_t *i;
  bool found = FALSE;

  for (i = list; i; i = i->nextContent) {
    if (CAN_SEE_ITEM(ch, i)) {
      show_itemData_toChar(i, ch, mode);
      found = TRUE;
    }
  }
  if (!found && show)
    send_to_char(ch, " Nothing.\r\n");
}

/**
 * Give a Character general health status of another Character
 * @param i Character to check
 * @param ch Character to send the information to
 * @return none
 */
void diag_char_to_char(charData_t *i, charData_t *ch)
{
  struct {
    byte percent;
    const char *text;
  } diagnosis[] = {
    { 100, "is in excellent condition."			},
    {  90, "has a few scratches."			},
    {  75, "has some small wounds and bruises."		},
    {  50, "has quite a few wounds."			},
    {  30, "has some big nasty wounds and scratches."	},
    {  15, "looks pretty hurt."				},
    {   0, "is in awful condition."			},
    {  -1, "is bleeding awfully from big wounds."	},
  };
  int percent, ar_index;
  const char *pers = PERS(i, ch);

  if (GET_MAX_HIT(i) > 0)
    percent = (100 * GET_HIT(i)) / GET_MAX_HIT(i);
  else
    percent = -1;		/* How could MAX_HIT be < 1?? */

  for (ar_index = 0; diagnosis[ar_index].percent >= 0; ar_index++)
    if (percent >= diagnosis[ar_index].percent)
      break;

  send_to_char(ch, "%c%s %s\r\n", UPPER(*pers), pers + 1, diagnosis[ar_index].text);
}

/**
 * Give a Character general information of another Character
 * This command shows a viewing Character both the description of the target
 * if any as well as any objects equipped.
 * 
 * Immortals can also see the inventory of the target Character
 * @param i Character being looked at
 * @param ch Character to send the information to
 * @returns none
 */
void look_at_char(charData_t *i, charData_t *ch)
{
  int j, found;
  itemData_t *tmp_obj;

  if (!ch->desc)
    return;

   if (i->player.description)
    send_to_char(ch, "%s", i->player.description);
  else
    act("You see nothing special about $m.", FALSE, i, 0, ch, TO_VICT);

  diag_char_to_char(i, ch);

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

  if (found) {
    send_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_ITEM(ch, GET_EQ(i, j))) {
	send_to_char(ch, "%s", wear_where[j]);
	show_itemData_toChar(GET_EQ(i, j), ch, SHOW_ITEM_SHORT);
      }
  }
  if (ch != i && (IS_THIEF(ch) || GET_AUTH(ch) >= AUTH_WIZARD)) {
    found = FALSE;
    act("\r\nYou attempt to peek at $s inventory:", FALSE, i, 0, ch, TO_VICT);
    for (tmp_obj = i->carrying; tmp_obj; tmp_obj = tmp_obj->nextContent) {
      if (CAN_SEE_ITEM(ch, tmp_obj) && (rand_number(0, 20) < GET_LEVEL(ch))) {
	show_itemData_toChar(tmp_obj, ch, SHOW_ITEM_SHORT);
	found = TRUE;
      }
    }

    if (!found)
      send_to_char(ch, "You can't see anything.\r\n");
  }
}

/**
 * Give a Character general information about another Character
 * @param i Character being listed
 * @param ch Character to send the information to
 * @returns none
 */
void list_one_char(charData_t *i, charData_t *ch)
{
  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 (IS_NPC(i) && i->player.long_descr && GET_POS(i) == GET_DEFAULT_POS(i)) {
    if (AFF_FLAGGED(i, AFF_INVISIBLE))
      send_to_char(ch, "*");

    if (AFF_FLAGGED(ch, AFF_DETECT_ALIGN)) {
      if (IS_EVIL(i))
	send_to_char(ch, "(Red Aura) ");
      else if (IS_GOOD(i))
	send_to_char(ch, "(Blue Aura) ");
    }
    send_to_char(ch, "%s", i->player.long_descr);

    if (AFF_FLAGGED(i, AFF_SANCTUARY))
      act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT);
    if (AFF_FLAGGED(i, AFF_BLIND))
      act("...$e is groping around blindly!", FALSE, i, 0, ch, TO_VICT);

    return;
  }

  if (IS_NPC(i))
    send_to_char(ch, "%c%s", UPPER(*i->player.short_descr), i->player.short_descr + 1);
  else
    send_to_char(ch, "%s %s", i->player.name, GET_TITLE(i));

  if (AFF_FLAGGED(i, AFF_INVISIBLE))
    send_to_char(ch, " (invisible)");
  if (AFF_FLAGGED(i, AFF_HIDE))
    send_to_char(ch, " (hidden)");
  if (!IS_NPC(i) && !i->desc)
    send_to_char(ch, " (linkless)");
  if (!IS_NPC(i) && PLR_FLAGGED(i, PLR_WRITING))
    send_to_char(ch, " (writing)");

  if (GET_POS(i) != POS_FIGHTING)
    send_to_char(ch, "%s", positions[(int) GET_POS(i)]);
  else {
    if (FIGHTING(i)) {
      send_to_char(ch, " is here, fighting ");
      if (FIGHTING(i) == ch)
	send_to_char(ch, "YOU!");
      else {
	if (IN_ROOM(i) == IN_ROOM(FIGHTING(i)))
	  send_to_char(ch, "%s!", PERS(FIGHTING(i), ch));
	else
	  send_to_char(ch,  "someone who has already left!");
      }
    } else			/* NIL fighting pointer */
      send_to_char(ch, " is here struggling with thin air.");
  }

  if (AFF_FLAGGED(ch, AFF_DETECT_ALIGN)) {
    if (IS_EVIL(i))
      send_to_char(ch, " (Red Aura)");
    else if (IS_GOOD(i))
      send_to_char(ch, " (Blue Aura)");
  }
  send_to_char(ch, "\r\n");

  if (AFF_FLAGGED(i, AFF_SANCTUARY))
    act("...$e glows with a bright light!", FALSE, i, 0, ch, TO_VICT);
}

/**
 * Check a list of Characters to see if the Character can see them, if so call list_one_char()
 * @param list List of Characters to check
 * @param ch Character to send the information to
 * @returns none
 * @sa list_one_char
 */
void list_char_to_char(charData_t *list, charData_t *ch)
{
  charData_t *i;

  for (i = list; i; i = i->next_in_room)
    if (ch != i) {
      if (CAN_SEE(ch, i))
	list_one_char(i, ch);
      else if (IS_DARK(IN_ROOM(ch)) && !CAN_SEE_IN_DARK(ch) &&
	       AFF_FLAGGED(i, AFF_INFRAVISION))
	send_to_char(ch, "You see a pair of glowing red eyes looking your way.\r\n");
    }
}

/**
 * Show Character which directions have exits
 * @param ch Character to list exits to
 * @returns none
 */
void do_auto_exits(charData_t *ch)
{
  int door, slen = 0;

  send_to_char(ch, "%s[ Exits: ", CCCYN(ch, C_NRM));

  for (door = 0; door < NUM_OF_DIRS; door++) {
    if (!EXIT(ch, door) || EXIT(ch, door)->toRoom == NULL)
      continue;
    if (EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED))
      continue;

    send_to_char(ch, "%c ", LOWER(*dirs[door]));
    slen++;
  }

  send_to_char(ch, "%s]%s\r\n", slen ? "" : "None!", CCNRM(ch, C_NRM));
}

/**
 * EXITS command
 *
 * This command lists exits in a room to the Character, if any
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_exits)
{
  int door, len = 0;

  if (AFF_FLAGGED(ch, AFF_BLIND)) {
    send_to_char(ch, "You can't see a damned thing, you're blind!\r\n");
    return;
  }

  send_to_char(ch, "Obvious exits:\r\n");

  for (door = 0; door < NUM_OF_DIRS; door++) {
    if (!EXIT(ch, door) || EXIT(ch, door)->toRoom == NULL)
      continue;
    if (EXIT_FLAGGED(EXIT(ch, door), EX_CLOSED))
      continue;

    len++;

    if (GET_AUTH(ch) >= AUTH_WIZARD)
      send_to_char(ch, "%-5s - [%s:%d] %s\r\n", 
                   dirs[door], 
                   EXIT(ch, door)->toRoom->zone->keyword, 
                   EXIT(ch, door)->toRoom->number, 
                   EXIT(ch, door)->toRoom->name);
    else
      send_to_char(ch, "%-5s - %s\r\n", dirs[door], IS_DARK(EXIT(ch, door)->toRoom) &&
		!CAN_SEE_IN_DARK(ch) ? "Too dark to tell." : EXIT(ch, door)->toRoom->name);
  }

  if (!len)
    send_to_char(ch, " None.\r\n");
}

/**
 * Give a Character general information about the room they're in
 * @param ch Character to send the information to
 * @param ingore_brief Boolean: Should the full room desc list even if Character has BRIEF on?
 * @returns none
 */
void look_at_room(charData_t *ch, int ignore_brief)
{
  if (!ch->desc)
    return;

  if (IS_DARK(IN_ROOM(ch)) && !CAN_SEE_IN_DARK(ch)) {
    send_to_char(ch, "It is pitch black...\r\n");
    return;
  } else if (AFF_FLAGGED(ch, AFF_BLIND)) {
    send_to_char(ch, "You see nothing but infinite darkness...\r\n");
    return;
  }
  send_to_char(ch, "%s", CCCYN(ch, C_NRM));
  if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_ROOMFLAGS)) {
    char buf[MAX_STRING_LENGTH];

    sprintbit(ROOM_FLAGS(IN_ROOM(ch)), room_bits, buf, sizeof(buf));
    send_to_char(ch, "[%s:%d] %s [ %s]", ch->room->zone->keyword, ch->room->number, ch->room->name, buf);
  } else
    send_to_char(ch, "%s", ch->room->name);

  send_to_char(ch, "%s\r\n", CCNRM(ch, C_NRM));

  if ((!IS_NPC(ch) && !PRF_FLAGGED(ch, PRF_BRIEF)) || ignore_brief ||
      ROOM_FLAGGED(IN_ROOM(ch), ROOM_DEATH))
    send_to_char(ch, "%s", ch->room->description);

  /* autoexits */
  if (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTOEXIT))
    do_auto_exits(ch);

  /* now list characters & objects */
  send_to_char(ch, "%s", CCGRN(ch, C_NRM));
  list_itemData_toChar(ch->room->contents, ch, SHOW_ITEM_LONG, FALSE);
  send_to_char(ch, "%s", CCYEL(ch, C_NRM));
  list_char_to_char(ch->room->people, ch);
  send_to_char(ch, "%s", CCNRM(ch, C_NRM));
}

/** 
 * Give a Character general information about a specific direction
 * @param ch Character to send the information to
 * @param dir Direction to check
 * @returns none
 */
void look_in_direction(charData_t *ch, int dir)
{
  if (EXIT(ch, dir)) {
    if (EXIT(ch, dir)->generalDescription)
      send_to_char(ch, "%s", EXIT(ch, dir)->generalDescription);
    else
      send_to_char(ch, "You see nothing special.\r\n");

    if (EXIT_FLAGGED(EXIT(ch, dir), EX_CLOSED) && EXIT(ch, dir)->keyword)
      send_to_char(ch, "The %s is closed.\r\n", fname(EXIT(ch, dir)->keyword));
    else if (EXIT_FLAGGED(EXIT(ch, dir), EX_ISDOOR) && EXIT(ch, dir)->keyword)
      send_to_char(ch, "The %s is open.\r\n", fname(EXIT(ch, dir)->keyword));
  } else
    send_to_char(ch, "Nothing special there...\r\n");
}

/**
 * List the contents of an object specified by name to a Character
 * @param ch Character to send the information to
 * @param arg Name of the object to check
 * @returns none
 */
void look_in_obj(charData_t *ch, char *arg)
{
  itemData_t *obj = NULL;
  charData_t *dummy = NULL;
  int amt, bits;

  if (!*arg)
    send_to_char(ch, "Look in what?\r\n");
  else if (!(bits = generic_find(arg, FIND_ITEM_INV | FIND_ITEM_ROOM |
				 FIND_ITEM_EQUIP, ch, &dummy, &obj))) {
    send_to_char(ch, "There doesn't seem to be %s %s here.\r\n", AN(arg), arg);
  } else if ((GET_ITEM_TYPE(obj) != ITEM_DRINKCON) &&
	     (GET_ITEM_TYPE(obj) != ITEM_FOUNTAIN) &&
	     (GET_ITEM_TYPE(obj) != ITEM_CONTAINER))
    send_to_char(ch, "There's nothing inside that!\r\n");
  else {
    if (GET_ITEM_TYPE(obj) == ITEM_CONTAINER) {
      if (ITEMVAL_FLAGGED(obj, CONT_CLOSED))
	send_to_char(ch, "It is closed.\r\n");
      else {
	send_to_char(ch, "%s", fname(obj->name));
	switch (bits) {
	case FIND_ITEM_INV:
	  send_to_char(ch, " (carried): \r\n");
	  break;
	case FIND_ITEM_ROOM:
	  send_to_char(ch, " (here): \r\n");
	  break;
	case FIND_ITEM_EQUIP:
	  send_to_char(ch, " (used): \r\n");
	  break;
	}

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

/**
 * Find an extraDescription_t description by keyword from a list
 * @param word Keyword of extraDescription_t to find
 * @param list List of extraDescription_t entries
 * @returns Description or NULL
 * @sa extraDescription_t
 */
char *find_exdesc(char *word, extraDescData_t *list)
{
  extraDescData_t *i;

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

  return (NULL);
}

/**
 * Show the Character requested information specified by name 
 * @param ch Character to send the information to
 * @param arg name value of what to look at
 * @returns none
 *
 * 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(charData_t *ch, char *arg)
{
  int bits, found = FALSE, j, fnum, i = 0;
  charData_t *found_char = NULL;
  itemData_t *obj, *found_obj = NULL;
  char *desc;

  if (!ch->desc)
    return;

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

  bits = generic_find(arg, FIND_ITEM_INV | FIND_ITEM_ROOM | FIND_ITEM_EQUIP |
		      FIND_CHAR_ROOM, ch, &found_char, &found_obj);

  /* Is the target a character? */
  if (found_char != NULL) {
    look_at_char(found_char, ch);
    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(ch, "Look at what?\r\n");
    return;
  }

  /* Does the argument match an extra desc in the room? */
  if ((desc = find_exdesc(arg, ch->room->exDescription)) != NULL && ++i == fnum) {
    page_string(ch->desc, desc, FALSE);
    return;
  }

  /* 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_ITEM(ch, GET_EQ(ch, j)))
      if ((desc = find_exdesc(arg, GET_EQ(ch, j)->exDescription)) != NULL && ++i == fnum) {
	send_to_char(ch, "%s", desc);
	found = TRUE;
      }

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

  /* Does the argument match an extra desc of an object in the room? */
  for (obj = ch->room->contents; obj && !found; obj = obj->nextContent)
    if (CAN_SEE_ITEM(ch, obj))
      if ((desc = find_exdesc(arg, obj->exDescription)) != NULL && ++i == fnum) {
	send_to_char(ch, "%s", desc);
	found = TRUE;
      }

  /* If an object was found back in generic_find */
  if (bits) {
    if (!found)
      show_itemData_toChar(found_obj, ch, SHOW_ITEM_ACTION);
    else {
      show_obj_modifiers(found_obj, ch);
      send_to_char(ch, "\r\n");
    }
  } else if (!found)
    send_to_char(ch, "You do not see that here.\r\n");
}

/**
 * LOOK command
 *
 * This command allows a Player to inspect their environment/items/other players
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_look)
{
  int look_type;

  if (!ch->desc)
    return;

  if (GET_POS(ch) < POS_SLEEPING)
    send_to_char(ch, "You can't see anything but stars!\r\n");
  else if (AFF_FLAGGED(ch, AFF_BLIND))
    send_to_char(ch, "You can't see a damned thing, you're blind!\r\n");
  else if (IS_DARK(IN_ROOM(ch)) && !CAN_SEE_IN_DARK(ch)) {
    send_to_char(ch, "It is pitch black...\r\n");
    list_char_to_char(ch->room->people, ch);	/* glowing red eyes */
  } else {
    char arg[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];

    half_chop(argument, arg, arg2);

    if (CMD_IS("read")) {
      if (!*arg)
	send_to_char(ch, "Read what?\r\n");
      else
	look_at_target(ch, arg);
      return;
    }
    if (!*arg)			/* "look" alone, without an argument at all */
      look_at_room(ch, 1);
    else if (is_abbrev(arg, "in"))
      look_in_obj(ch, arg2);
    /* did the char type 'look <direction>?' */
    else if ((look_type = search_block(arg, dirs, FALSE)) >= 0)
      look_in_direction(ch, look_type);
    else if (is_abbrev(arg, "at"))
      look_at_target(ch, arg2);
    else
      look_at_target(ch, arg);
  }
}

/**
 * EXAMINE command
 *
 * This command gives more in-depth information than LOOK.
 * @sa do_look
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_examine)
{
  charData_t *tmp_char;
  itemData_t *tmp_object;
  char tempsave[MAX_INPUT_LENGTH], arg[MAX_INPUT_LENGTH];

  one_argument(argument, arg);

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

  /* look_at_target() eats the number. */
  look_at_target(ch, strcpy(tempsave, arg));	/* strcpy: OK */

  generic_find(arg, FIND_ITEM_INV | FIND_ITEM_ROOM | FIND_CHAR_ROOM |
		      FIND_ITEM_EQUIP, ch, &tmp_char, &tmp_object);

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

/**
 * GOLD command
 *
 * This command lists how much gold the player has.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_gold)
{
  if (GET_GOLD(ch) == 0)
    send_to_char(ch, "You're broke!\r\n");
  else if (GET_GOLD(ch) == 1)
    send_to_char(ch, "You have one miserable little gold coin.\r\n");
  else
    send_to_char(ch, "You have %d gold coins.\r\n", GET_GOLD(ch));
}

/**
 * SCORE command
 *
 * This command gives the player a summation of information about their character.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_score)
{
  timeInfoData_t playing_time;

  if (IS_NPC(ch))
    return;

  send_to_char(ch, "You are %d years old.\r\n", GET_AGE(ch));

  if (age(ch)->month == 0 && age(ch)->day == 0)
    send_to_char(ch, "  It's your birthday today.\r\n");
  else
    send_to_char(ch, "\r\n");

  send_to_char(ch, "You have %d(%d) hit, %d(%d) mana and %d(%d) movement points.\r\n",
	  GET_HIT(ch), GET_MAX_HIT(ch), GET_MANA(ch), GET_MAX_MANA(ch),
	  GET_MOVE(ch), GET_MAX_MOVE(ch));

  send_to_char(ch, "Your armor class is %d/10, and your alignment is %d.\r\n",
	  compute_armor_class(ch), GET_ALIGNMENT(ch));

  send_to_char(ch, "You have scored %d exp, and have %d gold coins.\r\n",
	  GET_EXP(ch), GET_GOLD(ch));

  if (GET_AUTH(ch) != AUTH_PLAYER) {
    char buf[MAX_INPUT_LENGTH];
    sprinttype(GET_AUTH(ch), auth_types, buf, sizeof(buf));
    send_to_char(ch, "Your authorization level is %s.\r\n", buf);
  }
  if (GET_LEVEL(ch) < NUM_LEVELS)
    send_to_char(ch, "You need %d exp to reach your next level.\r\n",
	level_exp(GET_CLASS(ch), GET_LEVEL(ch) + 1) - GET_EXP(ch));

  playing_time = *real_time_passed((time(0) - ch->player.time.logon) +
				  ch->player.time.played, 0);
  send_to_char(ch, "You have been playing for %d day%s and %d hour%s.\r\n",
     playing_time.day, playing_time.day == 1 ? "" : "s",
     playing_time.hours, playing_time.hours == 1 ? "" : "s");

  send_to_char(ch, "This ranks you as %s %s (level %d).\r\n",
	  GET_NAME(ch), GET_TITLE(ch), GET_LEVEL(ch));

  switch (GET_POS(ch)) {
  case POS_DEAD:
    send_to_char(ch, "You are DEAD!\r\n");
    break;
  case POS_MORTALLYW:
    send_to_char(ch, "You are mortally wounded!  You should seek help!\r\n");
    break;
  case POS_INCAP:
    send_to_char(ch, "You are incapacitated, slowly fading away...\r\n");
    break;
  case POS_STUNNED:
    send_to_char(ch, "You are stunned!  You can't move!\r\n");
    break;
  case POS_SLEEPING:
    send_to_char(ch, "You are sleeping.\r\n");
    break;
  case POS_RESTING:
    send_to_char(ch, "You are resting.\r\n");
    break;
  case POS_SITTING:
    send_to_char(ch, "You are sitting.\r\n");
    break;
  case POS_FIGHTING:
    send_to_char(ch, "You are fighting %s.\r\n", FIGHTING(ch) ? PERS(FIGHTING(ch), ch) : "thin air");
    break;
  case POS_STANDING:
    send_to_char(ch, "You are standing.\r\n");
    break;
  default:
    send_to_char(ch, "You are floating.\r\n");
    break;
  }

  if (GET_COND(ch, DRUNK) > 10)
    send_to_char(ch, "You are intoxicated.\r\n");

  if (GET_COND(ch, FULL) == 0)
    send_to_char(ch, "You are hungry.\r\n");

  if (GET_COND(ch, THIRST) == 0)
    send_to_char(ch, "You are thirsty.\r\n");

  if (AFF_FLAGGED(ch, AFF_BLIND))
    send_to_char(ch, "You have been blinded!\r\n");

  if (AFF_FLAGGED(ch, AFF_INVISIBLE))
    send_to_char(ch, "You are invisible.\r\n");

  if (AFF_FLAGGED(ch, AFF_DETECT_INVIS))
    send_to_char(ch, "You are sensitive to the presence of invisible things.\r\n");

  if (AFF_FLAGGED(ch, AFF_SANCTUARY))
    send_to_char(ch, "You are protected by Sanctuary.\r\n");

  if (AFF_FLAGGED(ch, AFF_POISON))
    send_to_char(ch, "You are poisoned!\r\n");

  if (AFF_FLAGGED(ch, AFF_CHARM))
    send_to_char(ch, "You have been charmed!\r\n");

  if (affected_by_spell(ch, SPELL_ARMOR))
    send_to_char(ch, "You feel protected.\r\n");

  if (AFF_FLAGGED(ch, AFF_INFRAVISION))
    send_to_char(ch, "Your eyes are glowing red.\r\n");

  if (PRF_FLAGGED(ch, PRF_SUMMONABLE))
    send_to_char(ch, "You are summonable by other players.\r\n");
}

/**
 * INVENTORY command
 *
 * This command shows a player their character's inventory.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_inventory)
{
  send_to_char(ch, "You are carrying:\r\n");
  list_itemData_toChar(ch->carrying, ch, SHOW_ITEM_SHORT, TRUE);
}

/**
 * EQUIPMENT command
 *
 * This command shows a player the items their character has equipped/worn/wielded.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_equipment)
{
  int i, found = 0;

  send_to_char(ch, "You are using:\r\n");
  for (i = 0; i < NUM_WEARS; i++) {
    if (GET_EQ(ch, i)) {
      if (CAN_SEE_ITEM(ch, GET_EQ(ch, i))) {
	send_to_char(ch, "%s", wear_where[i]);
	show_itemData_toChar(GET_EQ(ch, i), ch, SHOW_ITEM_SHORT);
	found = TRUE;
      } else {
	send_to_char(ch, "%s", wear_where[i]);
	send_to_char(ch, "Something.\r\n");
	found = TRUE;
      }
    }
  }
  if (!found)
    send_to_char(ch, " Nothing.\r\n");
}

/**
 * TIME command
 *
 * Shows the in-game date and time
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_time)
{
  const char *suf;
  int weekday, day;

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

  /* 35 days in a month, 7 days a week */
  weekday = ((35 * time_info.month) + day) % 7;

  send_to_char(ch, "It is %d o'clock %s, on %s.\r\n",
	  (time_info.hours % 12 == 0) ? 12 : (time_info.hours % 12),
	  time_info.hours >= 12 ? "pm" : "am", weekdays[weekday]);

  /*
   * Peter Ajamian <peter@PAJAMIAN.DHS.ORG> supplied the following as a fix
   * for a bug introduced in the ordinal display that caused 11, 12, and 13
   * to be incorrectly displayed as 11st, 12nd, and 13rd.  Nate Winters
   * <wintersn@HOTMAIL.COM> had already submitted a fix, but it hard-coded a
   * limit on ordinal display which I want to avoid.	-dak
   */

  suf = "th";

  if (((day % 100) / 10) != 1) {
    switch (day % 10) {
    case 1:
      suf = "st";
      break;
    case 2:
      suf = "nd";
      break;
    case 3:
      suf = "rd";
      break;
    }
  }

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

/**
 * WEATHER command
 *
 * This command gives general information about the in-game weather.
 * Immortals see more specific information.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_weather)
{
  const char *sky_look[] = {
    "cloudless",
    "cloudy",
    "rainy",
    "lit by flashes of lightning"
  };

  if (OUTSIDE(ch))
    {
    send_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 (GET_AUTH(ch) >= AUTH_WIZARD)
      send_to_char(ch, "Pressure: %d (change: %d), Sky: %d (%s)\r\n",
                 weather_info.pressure,
                 weather_info.change,
                 weather_info.sky,
                 sky_look[weather_info.sky]);
    }
  else
    send_to_char(ch, "You have no feeling about the weather at all.\r\n");
}

/**
 * HELP command
 *
 * This command lets the user access the help entries.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_help)
{
  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(ch, "No help available.\r\n");
    return;
  }

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

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

    if (bot > top) {
      send_to_char(ch, "There is no help on that word.\r\n");
      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--;
      page_string(ch->desc, help_table[mid].entry, 0);
      return;
    } else {
      if (chk > 0)
        bot = mid + 1;
      else
        top = mid - 1;
    }
  }
}

#define WHO_FORMAT \
"format: who [minlev[-maxlev]] [-n name] [-c classlist] [-s] [-o] [-q] [-r] [-z]\r\n"

/**
 * WHO command
 *
 * This lists the characters in-game which the player can see
 *
 * @todo FIXME: This whole function needs to be redone.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_who)
{
  descriptorData_t *d;
  charData_t *tch;
  char name_search[MAX_INPUT_LENGTH], buf[MAX_INPUT_LENGTH];
  char mode;
  int low = 0, high = NUM_LEVELS, localwho = 0, questwho = 0;
  int showclass = 0, short_list = 0, outlaws = 0, num_can_see = 0;
  int who_room = 0;

  skip_spaces(&argument);
  strcpy(buf, argument);	/* strcpy: OK (sizeof: argument == buf) */
  name_search[0] = '\0';

  while (*buf) {
    char arg[MAX_INPUT_LENGTH], buf1[MAX_INPUT_LENGTH];

    half_chop(buf, arg, buf1);
    if (isdigit(*arg)) {
      sscanf(arg, "%d-%d", &low, &high);
      strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
    } else if (*arg == '-') {
      mode = *(arg + 1);       /* just in case; we destroy arg in the switch */
      switch (mode) {
      case 'o':
      case 'k':
	outlaws = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	break;
      case 'z':
	localwho = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	break;
      case 's':
	short_list = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	break;
      case 'q':
	questwho = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	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 'r':
	who_room = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	break;
      case 'c':
	half_chop(buf1, arg, buf);
	showclass = find_class_bitvector(arg);
	break;
      default:
	send_to_char(ch, "%s", WHO_FORMAT);
	return;
      }				/* end of switch */

    } else {			/* endif */
      send_to_char(ch, "%s", WHO_FORMAT);
      return;
    }
  }				/* end while (parser) */

  send_to_char(ch, "Players\r\n-------\r\n");

  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 && str_cmp(GET_NAME(tch), name_search) &&
	!strstr(GET_TITLE(tch), name_search))
      continue;
    if (!CAN_SEE(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
      continue;
    if (outlaws && !PLR_FLAGGED(tch, PLR_KILLER) &&
	!PLR_FLAGGED(tch, PLR_THIEF))
      continue;
    if (questwho && !PRF_FLAGGED(tch, PRF_QUEST))
      continue;
    if (localwho && ch->room->zone != tch->room->zone)
      continue;
    if (who_room && (IN_ROOM(tch) != IN_ROOM(ch)))
      continue;
    if (showclass && !(showclass & (1 << GET_CLASS(tch))))
      continue;
    if (short_list) {
      send_to_char(ch, "%s[%2d %s] %-12.12s%s%s",
	      (GET_AUTH(tch) >= AUTH_WIZARD ? CCYEL(ch, C_SPR) : ""),
	      GET_LEVEL(tch), CLASS_ABBR(tch), GET_NAME(tch),
	      (GET_AUTH(tch) >= AUTH_WIZARD ? CCNRM(ch, C_SPR) : ""),
	      ((!(++num_can_see % 4)) ? "\r\n" : ""));
    } else {
      num_can_see++;
      send_to_char(ch, "%s[%2d %s] %s %s",
	      (GET_AUTH(tch) >= AUTH_WIZARD ? CCYEL(ch, C_SPR) : ""),
	       GET_LEVEL(tch), CLASS_ABBR(tch), GET_NAME(tch),
	       GET_TITLE(tch));

      if (GET_INVIS_AUTH(tch)) {
        sprinttype(GET_INVIS_AUTH(tch), auth_types, buf, sizeof(buf));
	send_to_char(ch, " (i%s)", buf);
      } else if (AFF_FLAGGED(tch, AFF_INVISIBLE))
	send_to_char(ch, " (invis)");

      if (PLR_FLAGGED(tch, PLR_MAILING))
	send_to_char(ch, " (mailing)");
      else if (PLR_FLAGGED(tch, PLR_WRITING))
	send_to_char(ch, " (writing)");

      if (PRF_FLAGGED(tch, PRF_DEAF))
	send_to_char(ch, " (deaf)");
      if (PRF_FLAGGED(tch, PRF_NOTELL))
	send_to_char(ch, " (notell)");
      if (PRF_FLAGGED(tch, PRF_QUEST))
	send_to_char(ch, " (quest)");
      if (PLR_FLAGGED(tch, PLR_THIEF))
	send_to_char(ch, " (THIEF)");
      if (PLR_FLAGGED(tch, PLR_KILLER))
	send_to_char(ch, " (KILLER)");
      if (GET_AUTH(tch) >= AUTH_WIZARD)
	send_to_char(ch, CCNRM(ch, C_SPR));
      send_to_char(ch, "\r\n");
    }				/* endif shortlist */
  }				/* end of for */
  if (short_list && (num_can_see % 4))
    send_to_char(ch, "\r\n");
  if (num_can_see == 0)
    send_to_char(ch, "\r\nNobody at all!\r\n");
  else if (num_can_see == 1)
    send_to_char(ch, "\r\nOne lonely character displayed.\r\n");
  else
    send_to_char(ch, "\r\n%d characters displayed.\r\n", num_can_see);
}


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

/**
 * USERS command
 *
 * This command allows immortals to list all connections and states of Characters.
 *
 * @todo FIXME: This whole function needs to be redone.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_users)
{
  char line[200], line2[220], idletime[10], classname[20];
  char state[30], *timeptr, mode;
  char name_search[MAX_INPUT_LENGTH], host_search[MAX_INPUT_LENGTH];
  charData_t *tch;
  descriptorData_t *d;
  int low = 0, high = NUM_LEVELS, num_can_see = 0;
  int showclass = 0, outlaws = 0, playing = 0, deadweight = 0;
  char authValue[2];
  char buf[MAX_INPUT_LENGTH], arg[MAX_INPUT_LENGTH];

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

  strcpy(buf, argument);	/* strcpy: OK (sizeof: argument == buf) */
  while (*buf) {
    char buf1[MAX_INPUT_LENGTH];

    half_chop(buf, arg, buf1);
    if (*arg == '-') {
      mode = *(arg + 1);  /* just in case; we destroy arg in the switch */
      switch (mode) {
      case 'o':
      case 'k':
	outlaws = 1;
	playing = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	break;
      case 'p':
	playing = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	break;
      case 'd':
	deadweight = 1;
	strcpy(buf, buf1);	/* strcpy: OK (sizeof: buf1 == buf) */
	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;
      case 'c':
	playing = 1;
	half_chop(buf1, arg, buf);
	showclass = find_class_bitvector(arg);
	break;
      default:
	send_to_char(ch, "%s", USERS_FORMAT);
	return;
      }				/* end of switch */

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

  one_argument(argument, arg);

  for (d = descriptor_list; d; d = d->next) {
    if (STATE(d) != CON_PLAYING && playing)
      continue;
    if (STATE(d) == CON_PLAYING && deadweight)
      continue;
    if (STATE(d) == CON_PLAYING) {
      if (d->original)
	tch = d->original;
      else if (!(tch = d->character))
	continue;

      if (*host_search && !strstr(d->host, host_search))
	continue;
      if (*name_search && str_cmp(GET_NAME(tch), name_search))
	continue;
      if (!CAN_SEE(ch, tch) || GET_LEVEL(tch) < low || GET_LEVEL(tch) > high)
	continue;
      if (outlaws && !PLR_FLAGGED(tch, PLR_KILLER) &&
	  !PLR_FLAGGED(tch, PLR_THIEF))
	continue;
      if (showclass && !(showclass & (1 << GET_CLASS(tch))))
	continue;
      if (GET_INVIS_AUTH(ch) > GET_AUTH(ch))
	continue;

      if (d->original)
	sprintf(classname, "%2d %s", GET_LEVEL(d->original),
		CLASS_ABBR(d->original));
      else
	sprintf(classname, "%2d %s", GET_LEVEL(d->character),
		CLASS_ABBR(d->character));
    } else
      strcpy(classname, "  -  ");

    timeptr = asctime(localtime(&d->login_time));
    timeptr += 11;
    *(timeptr + 8) = '\0';

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

    if (d->character && STATE(d) == CON_PLAYING && GET_AUTH(d->character) < AUTH_WIZARD)
      sprintf(idletime, "%3d", d->character->char_specials.timer *
	      SECS_PER_MUD_HOUR / SECS_PER_REAL_MIN);
    else
      strcpy(idletime, "");

    switch(GET_AUTH(d->character)) {
      case AUTH_OWNER:
        snprintf(authValue, sizeof(authValue), "O");
        break;
      case AUTH_WIZARD:
        snprintf(authValue, sizeof(authValue), "W");
        break;
      case AUTH_PLAYER:
        snprintf(authValue, sizeof(authValue), "P");
        break;
      case AUTH_GUEST:
        snprintf(authValue, sizeof(authValue), "G");
        break;
      default:
        snprintf(authValue, sizeof(authValue), "-");
        break;
    }

    sprintf(line, "%3d %-5s %s %-12s %-14s %-3s %-8s ", 
        d->desc_num, classname, authValue, 
	d->original && d->original->player.name ? d->original->player.name :
	d->character && d->character->player.name ? d->character->player.name :
	"UNDEFINED",
	state, idletime, timeptr);

    if (d->host && *d->host)
      sprintf(line + strlen(line), "[%s]\r\n", d->host);
    else
      strcat(line, "[Hostname unknown]\r\n");

    if (STATE(d) != CON_PLAYING) {
      sprintf(line2, "%s%s%s", CCGRN(ch, C_SPR), line, CCNRM(ch, C_SPR));
      strcpy(line, line2);
    }
    if (STATE(d) != CON_PLAYING ||
		(STATE(d) == CON_PLAYING && CAN_SEE(ch, d->character))) {
      send_to_char(ch, "%s", line);
      num_can_see++;
    }
  }

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

/**
 * CREDITS, NEWS, INFO, WIZLIST, IMMLIST, HANDBOOK, POLICIES, MOTD, IMOTD, CLEAR, VERSION, and WHOAMI commands
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_gen_ps)
{

  if (CMD_IS("credits"))       page_string(ch->desc, credits, 0);
  else if (CMD_IS("news"))     page_string(ch->desc, news, 0);
  else if (CMD_IS("info"))     page_string(ch->desc, info, 0);
  else if (CMD_IS("wizlist"))  page_string(ch->desc, wizlist, 0);
  else if (CMD_IS("immlist"))  page_string(ch->desc, immlist, 0);
  else if (CMD_IS("handbook")) page_string(ch->desc, handbook, 0);
  else if (CMD_IS("policy"))   page_string(ch->desc, policies, 0);
  else if (CMD_IS("motd"))     page_string(ch->desc, motd, 0);
  else if (CMD_IS("imotd"))    page_string(ch->desc, imotd, 0);
  else if (CMD_IS("clear"))    send_to_char(ch, "\033[H\033[J");
  else if (CMD_IS("version"))  send_to_char(ch, "%s\r\n", circlemud_version);
  else if (CMD_IS("whoami"))   send_to_char(ch, "%s\r\n", GET_NAME(ch));
  else                                         log("SYSERR: Unhandled command in do_gen_ps.  (%s)", CMD_NAME);
}

/**
 * List other character(s) in the same zone as Character
 * @param ch Character to send the information to
 * @param arg Specific character to look for or NULL
 * @returns none
 */
void perform_mortal_where(charData_t *ch, char *arg)
{
  charData_t *i;
  descriptorData_t *d;

  if (!*arg) {
    send_to_char(ch, "Players in your Zone\r\n--------------------\r\n");
    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 (IN_ROOM(i) == NULL || !CAN_SEE(ch, i))
	continue;
      if (ch->room->zone != i->room->zone)
	continue;
      send_to_char(ch, "%-20s - %s\r\n", GET_NAME(i), i->room->name);
    }
  } else {			/* print only FIRST char, not all. */
    for (i = character_list; i; i = i->next) {
      if (IN_ROOM(i) == NULL || i == ch)
	continue;
      if (!CAN_SEE(ch, i) || i->room->zone != ch->room->zone)
	continue;
      if (!isname(arg, i->player.name))
	continue;
      send_to_char(ch, "%-25s - %s\r\n", GET_NAME(i), i->room->name);
      return;
    }
    send_to_char(ch, "Nobody around by that name.\r\n");
  }
}

/**
 * List an object and where it's found
 * @param num Number of the item which exist
 * @param obj Object to list
 * @param ch Character to send the information to
 * @param recur Boolean: Recurse inside other objects
 * @return none
 */
void print_object_location(int num, itemData_t *obj, charData_t *ch, int recur)
{
  if (num > 0)
    send_to_char(ch, "O%3d. %-25s - ", num, obj->shortDescription);
  else
    send_to_char(ch, "%33s", " - ");

  if (IN_ROOM(obj) != NULL)
    send_to_char(ch, "[%s:%d] %s\r\n", IN_ROOM(obj)->zone->keyword, IN_ROOM(obj)->number, obj->room->name);
  else if (obj->carriedBy)
    send_to_char(ch, "carried by %s\r\n", PERS(obj->carriedBy, ch));
  else if (obj->wornBy)
    send_to_char(ch, "worn by %s\r\n", PERS(obj->wornBy, ch));
  else if (obj->inObj) {
    send_to_char(ch, "inside %s%s\r\n", obj->inObj->shortDescription, (recur ? ", which is" : " "));
    if (recur)
      print_object_location(0, obj->inObj, ch, recur);
  } else
    send_to_char(ch, "in an unknown location\r\n");
}

/**
 * Locate object, mobile, or character by name
 * @param ch Character to send the information to
 * @param arg What to look for, by name
 * @returns none
 */
void perform_immort_where(charData_t *ch, char *arg)
{
  charData_t *i;
  itemData_t *k;
  descriptorData_t *d;
  int num = 0, found = 0;

  if (!*arg) {
    send_to_char(ch, "Players\r\n-------\r\n");
    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) && (IN_ROOM(i) != NULL)) {
	  if (d->original)
	    send_to_char(ch, "%-20s - [%s:%d] %s (in %s)\r\n",
		GET_NAME(i), d->character->room->zone->keyword, d->character->room->number,
		d->character->room->name, GET_NAME(d->character));
	  else
	    send_to_char(ch, "%-20s - [%s:%5d] %s\r\n", GET_NAME(i), i->room->zone->keyword, i->room->number, i->room->name);
	}
      }
  } else {
    for (i = character_list; i; i = i->next)
      if (CAN_SEE(ch, i) && IN_ROOM(i) != NULL && isname(arg, i->player.name)) {
	found = 1;
	send_to_char(ch, "M%3d. %-25s - [%s:%d] %s\r\n", ++num, GET_NAME(i), i->room->zone->keyword, i->room->number, i->room->name);
      }
    for (num = 0, k = object_list; k; k = k->next)
      if (CAN_SEE_ITEM(ch, k) && isname(arg, k->name)) {
	found = 1;
	print_object_location(++num, k, ch, TRUE);
      }
    if (!found)
      send_to_char(ch, "Couldn't find any such thing.\r\n");
  }
}

/**
 * WHERE command
 *
 * This command is the top level interface for mortals an immortals
 * @sa perform_immort_where
 * @sa perform_mortal_where
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_where)
{
  char arg[MAX_INPUT_LENGTH];

  one_argument(argument, arg);

  if (GET_AUTH(ch) >= AUTH_WIZARD)
    perform_immort_where(ch, arg);
  else
    perform_mortal_where(ch, arg);
}

/**
 * LEVELS command
 *
 * This command lists the levels the Character can progress through
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_levels)
{
  char buf[MAX_STRING_LENGTH];
  size_t i, len = 0;
  int nlen;

  if (IS_NPC(ch)) {
    send_to_char(ch, "You ain't nothin' but a hound-dog.\r\n");
    return;
  }

  for (i = 1; i <= NUM_LEVELS; i++) {
    nlen = snprintf(buf + len, sizeof(buf) - len, "[%2d] %8d-%-8d : ", i,
		level_exp(GET_CLASS(ch), i), level_exp(GET_CLASS(ch), i + 1) - 1);
    if (len + nlen >= sizeof(buf) || nlen < 0)
      break;
    len += nlen;

    switch (GET_SEX(ch)) {
    case SEX_MALE:
    case SEX_NEUTRAL:
      nlen = snprintf(buf + len, sizeof(buf) - len, "%s\r\n", title_male(GET_CLASS(ch), i));
      break;
    case SEX_FEMALE:
      nlen = snprintf(buf + len, sizeof(buf) - len, "%s\r\n", title_female(GET_CLASS(ch), i));
      break;
    default:
      nlen = snprintf(buf + len, sizeof(buf) - len, "Oh dear.  You seem to be sexless.\r\n");
      break;
    }
    if (len + nlen >= sizeof(buf) || nlen < 0)
      break;
    len += nlen;
  }
  page_string(ch->desc, buf, TRUE);
}

/**
 * CONSIDER command
 *
 * This command compares the level of a Character and their intended target to see
 * how likely the Character would be to win in comabt.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_consider)
{
  char buf[MAX_INPUT_LENGTH];
  charData_t *victim;
  int diff;

  one_argument(argument, buf);

  if (!(victim = get_char_vis(ch, buf, NULL, FIND_CHAR_ROOM))) {
    send_to_char(ch, "Consider killing who?\r\n");
    return;
  }
  if (victim == ch) {
    send_to_char(ch, "Easy!  Very easy indeed!\r\n");
    return;
  }
  if (!IS_NPC(victim)) {
    send_to_char(ch, "Would you like to borrow a cross and a shovel?\r\n");
    return;
  }
  diff = (GET_LEVEL(victim) - GET_LEVEL(ch));

  if (diff <= -10)
    send_to_char(ch, "Now where did that chicken go?\r\n");
  else if (diff <= -5)
    send_to_char(ch, "You could do it with a needle!\r\n");
  else if (diff <= -2)
    send_to_char(ch, "Easy.\r\n");
  else if (diff <= -1)
    send_to_char(ch, "Fairly easy.\r\n");
  else if (diff == 0)
    send_to_char(ch, "The perfect match!\r\n");
  else if (diff <= 1)
    send_to_char(ch, "You would need some luck!\r\n");
  else if (diff <= 2)
    send_to_char(ch, "You would need a lot of luck!\r\n");
  else if (diff <= 3)
    send_to_char(ch, "You would need a lot of luck and great equipment!\r\n");
  else if (diff <= 5)
    send_to_char(ch, "Do you feel lucky, punk?\r\n");
  else if (diff <= 10)
    send_to_char(ch, "Are you mad!?\r\n");
  else if (diff <= 100)
    send_to_char(ch, "You ARE mad!\r\n");
}

/**
 * DIAGNOSE command
 *
 * This command lets a user check the health of themselves or another Character in the room.
 * @sa daig_char_to_char
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_diagnose)
{
  char buf[MAX_INPUT_LENGTH];
  charData_t *vict;

  one_argument(argument, buf);

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

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

/**
 * COLOR command
 *
 * This command lets the Player see and change their color level.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_color)
{
  char arg[MAX_INPUT_LENGTH];
  int tp;

  if (IS_NPC(ch))
    return;

  one_argument(argument, arg);

  if (!*arg) {
    send_to_char(ch, "Your current color level is %s.\r\n", ctypes[COLOR_LEV(ch)]);
    return;
  }
  if (((tp = search_block(arg, ctypes, FALSE)) == -1)) {
    send_to_char(ch, "Usage: color { Off | Sparse | Normal | Complete }\r\n");
    return;
  }
  REMOVE_BIT(PRF_FLAGS(ch), PRF_COLOR_1 | PRF_COLOR_2);
  SET_BIT(PRF_FLAGS(ch), (PRF_COLOR_1 * (tp & 1)) | (PRF_COLOR_2 * (tp & 2) >> 1));

  send_to_char(ch, "Your %scolor%s is now %s.\r\n", CCRED(ch, C_SPR), CCNRM(ch, C_OFF), ctypes[tp]);
}

/**
 * Macro for the Toggle Functions
 */
#define TFUNC(name) long (name)(charData_t *ch, int mode, int opt)

#define TOGGLE_CHANGE   0               /**< Change a toggle's value        */
#define TOGGLE_QUERY    1               /**< Return a toggle's value        */

/**
 * Toggle a PRF_ flag
 */
TFUNC(tog_pref) {

  if (mode == TOGGLE_QUERY)
    return PRF_FLAGGED(ch, opt);

  return PRF_TOG_CHK(ch, opt);
}

/**
 * Reverse PRF_ toggle.  This is for the PRF_NO... flags which are opposite
 */
TFUNC(tog_revPref) {

  if (mode == TOGGLE_QUERY)
    return !PRF_FLAGGED(ch, opt);

  return PRF_TOG_CHK(ch, opt);
}

/* Other TFUNC()s can be added for toggling other types of vars */

/**
 * TOGGLE command
 *
 * Lists the Character's toggleable options, and allow them to change them
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_toggle) {
  char output[MAX_STRING_LENGTH] = {'\0'}; /* Buffer for paged output */
  int n = 0, i = 0;
  long r = 0;

  /* *INDENT-OFF* */
  struct _togData_t {
    const char *keyword;
    const char *desc;
    long (*func)(charData_t *ch, int mode, int opt);
    int option;
    int minimum_auth;
    const char *disable;
    const char *enable;
  } td[] = {
    { "NOHASSLE",
      "No Hassle",
      tog_revPref,
      PRF_NOHASSLE,
      AUTH_WIZARD,
      "Nohassle enabled.\r\n",
      "Nohassle disabled.\r\n" },
    { "ROOMFLAGS",
      "Room Flags",
      tog_pref,
      PRF_ROOMFLAGS,
      AUTH_WIZARD,
      "You will no longer see the room flags.\r\n",
      "You will now see the room flags.\r\n" },
    { "HOLYLIGHT",
      "Holy Light",
      tog_pref,
      PRF_HOLYLIGHT,
      AUTH_WIZARD,
      "HolyLight mode off.\r\n",
      "HolyLight mode on.\r\n" },
    { "WIZNET",
      "Wiznet Channel",
      tog_revPref,
      PRF_NOWIZ,
      AUTH_WIZARD,
      "You can now hear the Wiz-channel.\r\n",
      "You are now deaf to the Wiz-channel.\r\n" },
    { "NOSUMMON",
      "Summon Protect",
      tog_revPref,
      PRF_SUMMONABLE,
      AUTH_PLAYER,
      "You are now safe from summoning by other players.\r\n",
      "You may now be summoned by other players.\r\n" },
    { "BRIEF",
      "Brief Mode",
      tog_pref,
      PRF_BRIEF,
      AUTH_NONE,
      "Brief mode off.\r\n",
      "Brief mode on.\r\n" },
    { "COMPACT",
      "Compact Mode",
      tog_pref,
      PRF_COMPACT,
      AUTH_NONE,
      "Compact mode off.\r\n",
      "Compact mode on.\r\n" },
    { "QUEST",
      "On Quest",
      tog_pref,
      PRF_QUEST,
      AUTH_PLAYER,
      "You are no longer part of the Quest.\r\n",
      "Okay, you are part of the Quest!\r\n" },
    { "TELLS",
      "Hear Tells",
      tog_revPref,
      PRF_NOTELL,
      AUTH_GUEST,
      "You can now hear tells.\r\n",
      "You are now deaf to tells.\r\n" },
    { "REPEAT",
      "Repeat Comm",
      tog_revPref,
      PRF_NOREPEAT,
      AUTH_NONE,
      "You will now have your communication repeated.\r\n",
      "You will no longer have your communication repeated.\r\n" },
    { "AUTOEXITS",
      "Auto Show Exit",
      tog_pref,
      PRF_AUTOEXIT,
      AUTH_NONE,
      "Autoexits disabled.\r\n",
      "Autoexits enabled.\r\n" },
    { "SHOUT",
      "Hear Shouts",
      tog_revPref,
      PRF_DEAF,
      AUTH_GUEST,
      "You can now hear shouts.\r\n",
      "You are now deaf to shouts.\r\n" },
    { "GOSSIP",
      "Gossip Channel",
      tog_revPref,
      PRF_NOGOSS,
      AUTH_GUEST,
      "You can now hear gossip.\r\n",
      "You are now deaf to gossip.\r\n" },
    { "AUCTION",
      "Auction Channel",
      tog_revPref,
      PRF_NOAUCT,
      AUTH_PLAYER,
      "You can now hear auctions.\r\n",
      "You are now deaf to auctions.\r\n" },
    { "GRATS",
      "Grats Channel",
      tog_revPref,
      PRF_NOGRATZ,
      AUTH_PLAYER,
      "You can now hear the congratulation messages.\r\n",
      "You are now deaf to the congratulation messages.\r\n" },
    /* New entries can be added above, this entry is last */
    { "\n" }
  };
  /* *INDENT-ON* */

  /* Skip any whitespace. */
  skip_spaces(&argument);

  /* Nothing specified.  Show the current settings */
  if (*argument == '\0') {
    snprintf(output, sizeof(output), "%sToggle%s\r\n------\r\n\r\n",
      CCWHT(ch, C_NRM), CCNRM(ch, C_NRM));
    /* Iterate over the available toggles. */
    for (i = 0, n = 0; strcmp(td[i].keyword, "\n") != 0; i++) {
      /* Skip entries the player shouldn't be allows to see. */
      if (td[i].minimum_auth > 0 && GET_AUTH(ch) < td[i].minimum_auth) {
        continue;
      }
      r = (*td[i].func)(ch, TOGGLE_QUERY, td[i].option);
      snprintf(output + strlen(output), sizeof(output),
           "  %s%-10s- %15s: [%s%s%s]%s",
           CCNRM(ch, C_NRM), td[i].keyword, td[i].desc, 
           r != 0 ? CCGRN(ch, C_NRM) : CCRED(ch, C_NRM), 
           r != 0 ? "X" : "-", 
           CCNRM(ch, C_NRM), (++n % 2) == 0 ? "\r\n" : "  ");
    }
    if (n > 0 && (n % 2) != 0) {
      snprintf(output + strlen(output), sizeof(output), "\r\n");
    }
    page_string(ch->desc, output, TRUE);
  } else {
    /* Save off the length of the argument. */
    const size_t argumentlen = strlen(argument);

    /* Iterate over the available toggles. */
    for (i = 0; strcasecmp(td[i].keyword, "\n") != 0; i++) {
      if (GET_AUTH(ch) >= td[i].minimum_auth) {
        if (strncasecmp(argument, td[i].keyword, argumentlen) == 0) {
          break;
        }
      }
    }
    if (strcmp(td[i].keyword, "\n") == 0) {
      send_to_char(ch, "There's no such toggle as '%s'\r\n", argument);
    } else {
      if ((*td[i].func)(ch, TOGGLE_CHANGE, td[i].option)) {
        send_to_char(ch, "%s", td[i].enable);
      } else {
        send_to_char(ch, "%s", td[i].disable);
      }
    }
  }
}

/**
 * Compare two commands and return them in order
 * @param a First command to compare
 * @param b Second command to compare
 * @returns strcmp of the two commands
 */
int sort_commands_helper(const void *a, const void *b)
{
  return strcmp(cmd_info[*(const int *)a].command, cmd_info[*(const int *)b].command);
}

/**
 * Sort the commands in the command table
 * @param none
 * @returns none
 * @sa sort_commands_helper
 */
void sort_commands(void)
{
  int a, num_of_cmds = 0;

  while (cmd_info[num_of_cmds].command[0] != '\n')
    num_of_cmds++;
  num_of_cmds++;	/* \n */

  CREATE(cmd_sort_info, int, num_of_cmds);

  for (a = 0; a < num_of_cmds; a++)
    cmd_sort_info[a] = a;

  /* Don't sort the RESERVED or \n entries. */
  qsort(cmd_sort_info + 1, num_of_cmds - 2, sizeof(int), sort_commands_helper);
}

/**
 * COMMANDS command
 * 
 * This command lists the commands the Character can use.
 *
 * @param ch the character performing the command
 * @param argument the additional text passed after the command
 * @param cmd the command object that was performed
 * @returns none
 */
ACMD(do_commands)
{
  int no, i, cmd_num;
  int wizhelp = 0, socials = 0;
  charData_t *vict;
  char arg[MAX_INPUT_LENGTH];

  one_argument(argument, arg);

  if (*arg) {
    if (!(vict = get_char_vis(ch, arg, NULL, FIND_CHAR_WORLD)) || IS_NPC(vict)) {
      send_to_char(ch, "Who is that?\r\n");
      return;
    }
    if (GET_LEVEL(ch) < GET_LEVEL(vict)) {
      send_to_char(ch, "You can't see the commands of people above your level.\r\n");
      return;
    }
  } else
    vict = ch;

  if (CMD_IS("socials"))
    socials = 1;
  else if (CMD_IS("wizhelp"))
    wizhelp = 1;

  send_to_char(ch, "The following %s%s are available to %s:\r\n",
	  wizhelp ? "privileged " : "",
	  socials ? "socials" : "commands",
	  vict == ch ? "you" : GET_NAME(vict));

  /* cmd_num starts at 1, not 0, to remove 'RESERVED' */
  for (no = 1, cmd_num = 1; cmd_info[cmd_sort_info[cmd_num]].command[0] != '\n'; cmd_num++) {
    i = cmd_sort_info[cmd_num];

    if (cmd_info[i].minimum_auth > AUTH_NONE && GET_AUTH(vict) < cmd_info[i].minimum_auth)
      continue;

    if ((cmd_info[i].minimum_auth >= AUTH_WIZARD) != wizhelp)
      continue;

    if (!wizhelp && socials != (cmd_info[i].command_pointer == do_action || cmd_info[i].command_pointer == do_insult))
      continue;

    send_to_char(ch, "%-11s%s", cmd_info[i].command, no++ % 7 == 0 ? "\r\n" : "");
  }

  if (no % 7 != 1)
    send_to_char(ch, "\r\n");
}