swr2.0/
swr2.0/area/
swr2.0/boards/
swr2.0/clans/
swr2.0/doc/
swr2.0/planets/
swr2.0/spacecraft/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "mud.h"

/*
 * Local functions.
 */
void show_char_to_char_0( CHAR_DATA * victim, CHAR_DATA * ch );
void show_char_to_char_1( CHAR_DATA * victim, CHAR_DATA * ch );
void show_char_to_char( CHAR_DATA * list, CHAR_DATA * ch );
void show_ships_to_char( SHIP_DATA * ship, CHAR_DATA * ch );
bool check_blind( CHAR_DATA * ch );
void show_condition( CHAR_DATA * ch, CHAR_DATA * victim );
bool is_online( const char *argument );

char *format_obj_to_char( const OBJ_DATA * obj, CHAR_DATA * ch, bool fShort )
{
  static char buf[MAX_STRING_LENGTH];

  buf[0] = '\0';

  if( IS_OBJ_STAT( obj, ITEM_INVIS ) )
    strcat( buf, "(Invis) " );

  if( ( IS_AFFECTED( ch, AFF_DETECT_MAGIC ) || IS_IMMORTAL( ch ) )
      && IS_OBJ_STAT( obj, ITEM_MAGIC ) )
    strcat( buf, "&B(Blue Aura)&w " );

  if( IS_OBJ_STAT( obj, ITEM_GLOW ) )
    strcat( buf, "(Glowing) " );

  if( IS_OBJ_STAT( obj, ITEM_HUM ) )
    strcat( buf, "(Humming) " );

  if( IS_OBJ_STAT( obj, ITEM_HIDDEN ) )
    strcat( buf, "(Hidden) " );

  if( IS_OBJ_STAT( obj, ITEM_BURRIED ) )
    strcat( buf, "(Burried) " );

  if( IS_IMMORTAL( ch ) && IS_OBJ_STAT( obj, ITEM_PROTOTYPE ) )
    strcat( buf, "(PROTO) " );

  if( fShort )
  {
    if( obj->short_descr )
      strcat( buf, obj->short_descr );
  }
  else
  {
    if( obj->description )
      strcat( buf, obj->description );
  }

  return buf;
}

/*
 * Some increasingly freaky halucinated objects		-Thoric
 */
const char *halucinated_object( int ms, bool fShort )
{
  int sms = URANGE( 1, ( ms + 10 ) / 5, halucinated_object_short_size() );

  if( fShort )
  {
    return
      halucinated_object_short[number_range
      ( 6 - URANGE( 1, sms / 2, 5 ), sms )];
  }

  return
    halucinated_object_long[number_range( 6 - URANGE( 1, sms / 2, 5 ), sms )];
}

/*
 * Show a list to a character.
 * Can coalesce duplicated items.
 */
void show_list_to_char( const OBJ_DATA * list, CHAR_DATA * ch,
			bool fShort, bool fShowNothing )
{
  char **prgpstrShow = NULL;
  int *prgnShow = NULL;
  int *pitShow = NULL;
  char *pstrShow = NULL;
  const OBJ_DATA *obj;
  int nShow = 0;
  int iShow = 0;
  int count = 0, offcount = 0, tmp = 0, ms = 0, cnt = 0;
  bool fCombine = FALSE;

  if( !ch->desc )
    return;

  /*
   * if there's no list... then don't do all this crap!  -Thoric
   */
  if( !list )
  {
    if( fShowNothing )
    {
      if( IS_NPC( ch ) || IS_SET( ch->act, PLR_COMBINE ) )
	send_to_char( "     ", ch );
      send_to_char( "Nothing.\r\n", ch );
    }

    return;
  }

  /*
   * Alloc space for output lines.
   */
  count = 0;
  for( obj = list; obj; obj = obj->next_content )
    count++;

  ms = ( ch->mental_state ? ch->mental_state : 1 )
    *
    ( IS_NPC( ch ) ? 1
      : ( ch->pcdata->
	condition[COND_DRUNK] ? ( ch->pcdata->condition[COND_DRUNK] /
	  12 ) : 1 ) );

  /*
   * If not mentally stable...
   */
  if( abs( ms ) > 40 )
  {
    offcount = URANGE( -( count ), ( count * ms ) / 100, count * 2 );

    if( offcount < 0 )
      offcount += number_range( 0, abs( offcount ) );
    else if( offcount > 0 )
      offcount -= number_range( 0, offcount );
  }
  else
    offcount = 0;

  if( count + offcount <= 0 )
  {
    if( fShowNothing )
    {
      if( IS_NPC( ch ) || IS_SET( ch->act, PLR_COMBINE ) )
	send_to_char( "     ", ch );

      send_to_char( "Nothing.\r\n", ch );
    }

    return;
  }

  CREATE( prgpstrShow, char *, count + ( ( offcount > 0 ) ? offcount : 0 ) );
  CREATE( prgnShow, int, count + ( ( offcount > 0 ) ? offcount : 0 ) );
  CREATE( pitShow, int, count + ( ( offcount > 0 ) ? offcount : 0 ) );
  nShow = 0;
  tmp = ( offcount > 0 ) ? offcount : 0;
  cnt = 0;

  /*
   * Format the list of objects.
   */
  for( obj = list; obj; obj = obj->next_content )
  {
    if( offcount < 0 && ++cnt > ( count + offcount ) )
      break;

    if( tmp > 0 && number_bits( 1 ) == 0 )
    {
      prgpstrShow[nShow] = str_dup( halucinated_object( ms, fShort ) );
      prgnShow[nShow] = 1;
      pitShow[nShow] = number_range( 0, MAX_ITEM_TYPE );
      nShow++;
      --tmp;
    }

    if( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj ) )
    {
      pstrShow = format_obj_to_char( obj, ch, fShort );
      fCombine = FALSE;

      if( IS_NPC( ch ) || IS_SET( ch->act, PLR_COMBINE ) )
      {
	/*
	 * Look for duplicates, case sensitive.
	 * Matches tend to be near end so run loop backwords.
	 */
	for( iShow = nShow - 1; iShow >= 0; iShow-- )
	{
	  if( !strcmp( prgpstrShow[iShow], pstrShow ) )
	  {
	    prgnShow[iShow] += obj->count;
	    fCombine = TRUE;
	    break;
	  }
	}
      }

      pitShow[nShow] = obj->item_type;
      /*
       * Couldn't combine, or didn't want to.
       */
      if( !fCombine )
      {
	prgpstrShow[nShow] = str_dup( pstrShow );
	prgnShow[nShow] = obj->count;
	nShow++;
      }
    }
  }

  if( tmp > 0 )
  {
    int x = 0;

    for( x = 0; x < tmp; x++ )
    {
      prgpstrShow[nShow] = str_dup( halucinated_object( ms, fShort ) );
      prgnShow[nShow] = 1;
      pitShow[nShow] = number_range( 0, MAX_ITEM_TYPE );
      nShow++;
    }
  }

  /*
   * Output the formatted list.         -Color support by Thoric
   */
  for( iShow = 0; iShow < nShow; iShow++ )
  {
    switch ( pitShow[iShow] )
    {
      default:
	set_char_color( AT_OBJECT, ch );
	break;

      case ITEM_MONEY:
	set_char_color( AT_YELLOW, ch );
	break;

      case ITEM_FOOD:
	set_char_color( AT_HUNGRY, ch );
	break;

      case ITEM_DRINK_CON:
      case ITEM_FOUNTAIN:
	set_char_color( AT_THIRSTY, ch );
	break;
    }

    if( fShowNothing )
      send_to_char( "     ", ch );

    send_to_char( prgpstrShow[iShow], ch );
    /*        if ( IS_NPC(ch) || IS_SET(ch->act, PLR_COMBINE) ) */
    {
      if( prgnShow[iShow] != 1 )
	ch_printf( ch, " (%d)", prgnShow[iShow] );
    }

    send_to_char( "\r\n", ch );
    DISPOSE( prgpstrShow[iShow] );
  }

  if( fShowNothing && nShow == 0 )
  {
    if( IS_NPC( ch ) || IS_SET( ch->act, PLR_COMBINE ) )
      send_to_char( "     ", ch );

    send_to_char( "Nothing.\r\n", ch );
  }

  /*
   * Clean up.
   */
  DISPOSE( prgpstrShow );
  DISPOSE( prgnShow );
  DISPOSE( pitShow );
}

/*
 * Show fancy descriptions for certain spell affects		-Thoric
 */
void show_visible_affects_to_char( CHAR_DATA * victim, CHAR_DATA * ch )
{
  if( IS_AFFECTED( victim, AFF_SANCTUARY ) )
  {
    if( IS_GOOD( victim ) )
    {
      set_char_color( AT_WHITE, ch );
      ch_printf( ch, "%s glows with an aura of divine radiance.\r\n",
	  IS_NPC( victim ) ? capitalize( victim->
	    short_descr ) : ( victim->
	      name ) );
    }
    else if( IS_EVIL( victim ) )
    {
      set_char_color( AT_WHITE, ch );
      ch_printf( ch, "%s shimmers beneath an aura of dark energy.\r\n",
	  IS_NPC( victim ) ? capitalize( victim->
	    short_descr ) : ( victim->
	      name ) );
    }
    else
    {
      set_char_color( AT_WHITE, ch );
      ch_printf( ch, "%s is shrouded in flowing shadow and light.\r\n",
	  IS_NPC( victim ) ? capitalize( victim->
	    short_descr ) : ( victim->
	      name ) );
    }
  }

  if( IS_AFFECTED( victim, AFF_FIRESHIELD ) )
  {
    set_char_color( AT_FIRE, ch );
    ch_printf( ch, "%s is engulfed within a blaze of mystical flame.\r\n",
	IS_NPC( victim ) ? capitalize( victim->
	  short_descr ) : ( victim->
	    name ) );
  }

  if( IS_AFFECTED( victim, AFF_SHOCKSHIELD ) )
  {
    set_char_color( AT_BLUE, ch );
    ch_printf( ch, "%s is surrounded by cascading torrents of energy.\r\n",
	IS_NPC( victim ) ? capitalize( victim->
	  short_descr ) : ( victim->
	    name ) );
  }

  /*Scryn 8/13*/
  if( IS_AFFECTED( victim, AFF_ICESHIELD ) )
  {
    set_char_color( AT_LBLUE, ch );
    ch_printf( ch, "%s is ensphered by shards of glistening ice.\r\n",
	IS_NPC( victim ) ? capitalize( victim->
	  short_descr ) : ( victim->
	    name ) );
  }

  if( IS_AFFECTED( victim, AFF_CHARM ) )
  {
    set_char_color( AT_MAGIC, ch );
    ch_printf( ch, "%s looks ahead free of expression.\r\n",
	IS_NPC( victim ) ? capitalize( victim->
	  short_descr ) : ( victim->
	    name ) );
  }

  if( !IS_NPC( victim ) && !victim->desc
      && victim->switched && IS_AFFECTED( victim->switched, AFF_POSSESS ) )
  {
    set_char_color( AT_MAGIC, ch );
    ch_printf( ch, "%s appears to be in a deep trance...\r\n",
	       PERS( victim, ch ) );
  }
}

static void show_char_position_dead( BUFFER *buf )
{
  buffer_strcat( buf, " is DEAD!!" );
}

static void show_char_position_mortal( BUFFER *buf )
{
  buffer_strcat( buf, " is mortally wounded." );
}

static void show_char_position_incap( BUFFER *buf )
{
  buffer_strcat( buf, " is incapacitated." );
}

static void show_char_position_stunned( BUFFER *buf )
{
  buffer_strcat( buf, " is lying here stunned." );
}

static void show_char_position_sleeping( BUFFER *buf, const CHAR_DATA *ch )
{
  if( ch->position == POS_SITTING || ch->position == POS_RESTING )
    buffer_strcat( buf, " is sleeping nearby." );
  else
    buffer_strcat( buf, " is deep in slumber here." );
}

static void show_char_position_resting( BUFFER *buf, const CHAR_DATA * ch )
{
  if( ch->position == POS_RESTING )
    buffer_strcat( buf, " is sprawled out alongside you." );
  else if( ch->position == POS_MOUNTED )
    buffer_strcat( buf, " is sprawled out at the foot of your mount." );
  else
    buffer_strcat( buf, " is sprawled out here." );
}

static void show_char_position_sitting( BUFFER *buf, const CHAR_DATA * ch )
{
  if( ch->position == POS_SITTING )
    buffer_strcat( buf, " sits here with you." );
  else if( ch->position == POS_RESTING )
    buffer_strcat( buf, " sits nearby as you lie around." );
  else
    buffer_strcat( buf, " sits upright here." );
}

static void show_char_position_standing( BUFFER *buf, const CHAR_DATA * ch,
					 const CHAR_DATA * victim )
{
  if( IS_IMMORTAL( victim ) )
    buffer_strcat( buf, " is here before you." );
  else if( ( victim->in_room->sector_type == SECT_UNDERWATER )
	   && !IS_AFFECTED( victim, AFF_AQUA_BREATH ) && !IS_NPC( victim ) )
    buffer_strcat( buf, " is drowning here." );
  else if( victim->in_room->sector_type == SECT_UNDERWATER )
    buffer_strcat( buf, " is here in the water." );
  else if( ( victim->in_room->sector_type == SECT_OCEANFLOOR )
	   && !IS_AFFECTED( victim, AFF_AQUA_BREATH ) && !IS_NPC( victim ) )
    buffer_strcat( buf, " is drowning here." );
  else if( victim->in_room->sector_type == SECT_OCEANFLOOR )
    buffer_strcat( buf, " is standing here in the water." );
  else if( IS_AFFECTED( victim, AFF_FLOATING )
	   || IS_AFFECTED( victim, AFF_FLYING ) )
    buffer_strcat( buf, " is hovering here." );
  else
    buffer_strcat( buf, " is standing here." );
}

static void show_char_position_shove( BUFFER *buf )
{
  buffer_strcat( buf, " is being shoved around." );
}

static void show_char_position_drag( BUFFER *buf )
{
  buffer_strcat( buf, " is being dragged around." );
}

static void show_char_position_mounted( BUFFER *buf, const CHAR_DATA * ch,
					const CHAR_DATA * victim )
{
  buffer_strcat( buf, " is here, upon " );

  if( !victim->mount )
    buffer_strcat( buf, "thin air???" );
  else if( victim->mount == ch )
    buffer_strcat( buf, "your back." );
  else if( victim->in_room == victim->mount->in_room )
  {
    buffer_strcat( buf, PERS( victim->mount, ch ) );
    buffer_strcat( buf, "." );
  }
  else
    buffer_strcat( buf, "someone who left??" );
}

static void show_char_position_fighting( BUFFER *buf, const CHAR_DATA * ch,
					 const CHAR_DATA * victim )
{
  buffer_strcat( buf, " is here, fighting " );

  if( !victim->fighting )
    buffer_strcat( buf, "thin air???" );
  else if( who_fighting( victim ) == ch )
    buffer_strcat( buf, "YOU!" );
  else if( victim->in_room == victim->fighting->who->in_room )
  {
    buffer_strcat( buf, PERS( victim->fighting->who, ch ) );
    buffer_strcat( buf, "." );
  }
  else
    buffer_strcat( buf, "someone who left??" );
}

void show_char_to_char_0( CHAR_DATA * victim, CHAR_DATA * ch )
{
  BUFFER *buf = buffer_new( 512 );

  /*if( IS_NPC( victim ) )
    buffer_strcat( buf, " " );*/

  if( !IS_NPC( victim ) && !victim->desc )
    {
      if( !victim->switched )
	buffer_strcat( buf, "(Link Dead) " );
      else if( !IS_AFFECTED( victim->switched, AFF_POSSESS ) )
	buffer_strcat( buf, "(Switched) " );
    }

  if( !IS_NPC( victim ) && IS_SET( victim->act, PLR_AFK ) )
    buffer_strcat( buf, "[AFK] " );

  if( ( !IS_NPC( victim ) && IS_SET( victim->act, PLR_WIZINVIS ) )
      || ( IS_NPC( victim ) && IS_SET( victim->act, ACT_MOBINVIS ) ) )
    {
      char buf1[MAX_STRING_LENGTH];

      if( !IS_NPC( victim ) )
	snprintf( buf1, MAX_STRING_LENGTH, "(Invis %d) ",
		  victim->pcdata->wizinvis );
      else
	snprintf( buf1, MAX_STRING_LENGTH, "(Mobinvis %d) ", victim->mobinvis);

      buffer_strcat( buf, buf1 );
    }

  if( IS_AFFECTED( victim, AFF_INVISIBLE ) )
    buffer_strcat( buf, "(Invis) " );

  if( IS_AFFECTED( victim, AFF_HIDE ) )
    buffer_strcat( buf, "(Hide) " );

  if( IS_AFFECTED( victim, AFF_PASS_DOOR ) )
    buffer_strcat( buf, "(Translucent) " );

  if( IS_AFFECTED( victim, AFF_FAERIE_FIRE ) )
    buffer_strcat( buf, "&P(Pink Aura)&w " );

  if( IS_EVIL( victim ) && IS_AFFECTED( ch, AFF_DETECT_EVIL ) )
    buffer_strcat( buf, "&R(Red Aura)&w " );

  if( !IS_NPC( victim ) && IS_SET( victim->act, PLR_LITTERBUG ) )
    buffer_strcat( buf, "(LITTERBUG) " );

  if( IS_NPC( victim ) && IS_IMMORTAL( ch )
      && IS_SET( victim->act, ACT_PROTOTYPE ) )
    buffer_strcat( buf, "(PROTO) " );

  if( victim->desc && victim->desc->connected == CON_EDITING )
    buffer_strcat( buf, "(Writing) " );

  set_char_color( AT_PERSON, ch );

  if( victim->position == victim->defposition
      && victim->long_descr[0] != '\0' )
    {
      buffer_strcat( buf, victim->long_descr );
      send_to_char( buf->data, ch );
      show_visible_affects_to_char( victim, ch );
    }
  else
    {
      /*   strcat( buf, PERS( victim, ch ) );       old system of titles
       *    removed to prevent prepending of name to title     -Kuran  
       *
       *    But added back bellow so that you can see mobs too :P   -Durga 
       */
      if( !IS_NPC( victim ) && !IS_SET( ch->act, PLR_BRIEF ) )
	{
	  buffer_strcat( buf, victim->pcdata->title );
	}
      else
	{
	  char tmp[100];
	  strncpy( tmp, PERS( victim, ch ), 100 );
	  tmp[0] = UPPER( tmp[0] );
	  buffer_strcat( buf, tmp );
	}

      switch ( victim->position )
	{
	case POS_DEAD:
	  show_char_position_dead( buf );
	  break;

	case POS_MORTAL:
	  show_char_position_mortal( buf );
	  break;

	case POS_INCAP:
	  show_char_position_incap( buf );
	  break;

	case POS_STUNNED:
	  show_char_position_stunned( buf );
	  break;

	case POS_SLEEPING:
	  show_char_position_sleeping( buf, ch );
	  break;

	case POS_RESTING:
	  show_char_position_resting( buf, ch );
	  break;

	case POS_SITTING:
	  show_char_position_sitting( buf, ch );
	  break;

	case POS_STANDING:
	  show_char_position_standing( buf, ch, victim );
	  break;

	case POS_SHOVE:
	  show_char_position_shove( buf );
	  break;

	case POS_DRAG:
	  show_char_position_drag( buf );
	  break;

	case POS_MOUNTED:
	  show_char_position_mounted( buf, ch, victim );
	  break;

	case POS_FIGHTING:
	  show_char_position_fighting( buf, ch, victim );
	  break;
	}

      buffer_strcat( buf, "\r\n" );
      buf->data[0] = UPPER( buf->data[0] );
      send_to_char( buf->data, ch );
      show_visible_affects_to_char( victim, ch );
    }

  buffer_free( buf );
}

void show_char_to_char_1( CHAR_DATA * victim, CHAR_DATA * ch )
{
  OBJ_DATA *obj = NULL;
  int iWear = 0;
  bool found = FALSE;

  if( can_see( victim, ch ) )
  {
    act( AT_ACTION, "$n looks at you.", ch, NULL, victim, TO_VICT );
    act( AT_ACTION, "$n looks at $N.", ch, NULL, victim, TO_NOTVICT );
  }

  if( victim->description[0] != '\0' )
  {
    send_to_char( victim->description, ch );
  }
  else
  {
    act( AT_PLAIN, "You see nothing special about $M.", ch, NULL, victim,
	TO_CHAR );
  }

  show_condition( ch, victim );

  found = FALSE;
  for( iWear = 0; iWear < MAX_WEAR; iWear++ )
  {
    if( ( obj = get_eq_char( victim, iWear ) ) != NULL
	&& can_see_obj( ch, obj ) )
    {
      if( !found )
      {
	send_to_char( "\r\n", ch );
	act( AT_PLAIN, "$N is using:", ch, NULL, victim, TO_CHAR );
	found = TRUE;
      }
      send_to_char( where_name[iWear], ch );
      send_to_char( format_obj_to_char( obj, ch, TRUE ), ch );
      send_to_char( "\r\n", ch );
    }
  }

  /*
   * Crash fix here by Thoric
   */
  if( IS_NPC( ch ) || victim == ch
      || ( IS_IMMORTAL( victim ) && ( victim->top_level > ch->top_level ) ) )
    return;

  if( number_percent() < character_skill_level( ch, gsn_peek ) )
  {
    send_to_char( "\r\nYou peek at the inventory:\r\n", ch );
    show_list_to_char( victim->first_carrying, ch, TRUE, TRUE );
    learn_from_success( ch, gsn_peek );
  }
  else if( character_skill_level( ch, gsn_peek ) )
  {
    learn_from_failure( ch, gsn_peek );
  }
}

void show_char_to_char( CHAR_DATA * list, CHAR_DATA * ch )
{
  CHAR_DATA *rch = NULL;

  for( rch = list; rch; rch = rch->next_in_room )
  {
    if( rch == ch )
      continue;

    if( can_see( ch, rch ) )
    {
      show_char_to_char_0( rch, ch );
    }
    else if( room_is_dark( ch->in_room )
	&& IS_AFFECTED( rch, AFF_INFRARED ) )
    {
      set_char_color( AT_BLOOD, ch );
      send_to_char( "The red form of a living creature is here.\r\n",
	  ch );
    }
  }
}

void show_ships_to_char( SHIP_DATA * ship, CHAR_DATA * ch )
{
  SHIP_DATA *rship;

  send_to_char( "&C", ch );

  for( rship = ship; rship; rship = rship->next_in_room )
    {
      if( rship->owner && rship->owner[0] != '\0' )
	{
	  if( get_clan( rship->owner ) || is_online( rship->owner )
	      || is_online( rship->pilot ) || is_online( rship->copilot ) )
	    {
	      ch_printf( ch, "%s\r\n", rship->name );
	    }
	}
    }
}

bool check_blind( CHAR_DATA * ch )
{
  if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_HOLYLIGHT ) )
    return TRUE;

  if( IS_AFFECTED( ch, AFF_TRUESIGHT ) )
    return TRUE;

  if( IS_AFFECTED( ch, AFF_BLIND ) )
  {
    send_to_char( "You can't see a thing!\r\n", ch );
    return FALSE;
  }

  return TRUE;
}

/*
 * Returns classical DIKU door direction based on text in arg	-Thoric
 */
int get_door( const char *arg )
{
  int door = DIR_SOMEWHERE;

  if( !str_cmp( arg, "n" ) || !str_cmp( arg, "north" ) )
    door = DIR_NORTH;
  else if( !str_cmp( arg, "e" ) || !str_cmp( arg, "east" ) )
    door = DIR_EAST;
  else if( !str_cmp( arg, "s" ) || !str_cmp( arg, "south" ) )
    door = DIR_SOUTH;
  else if( !str_cmp( arg, "w" ) || !str_cmp( arg, "west" ) )
    door = DIR_WEST;
  else if( !str_cmp( arg, "u" ) || !str_cmp( arg, "up" ) )
    door = DIR_UP;
  else if( !str_cmp( arg, "d" ) || !str_cmp( arg, "down" ) )
    door = DIR_DOWN;
  else if( !str_cmp( arg, "ne" ) || !str_cmp( arg, "northeast" ) )
    door = DIR_NORTHEAST;
  else if( !str_cmp( arg, "nw" ) || !str_cmp( arg, "northwest" ) )
    door = DIR_NORTHWEST;
  else if( !str_cmp( arg, "se" ) || !str_cmp( arg, "southeast" ) )
    door = DIR_SOUTHEAST;
  else if( !str_cmp( arg, "sw" ) || !str_cmp( arg, "southwest" ) )
    door = DIR_SOUTHWEST;
  else
    door = -1;

  return door;
}

void do_look( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char arg3[MAX_INPUT_LENGTH];
  EXIT_DATA *pexit = NULL;
  CHAR_DATA *victim = NULL;
  OBJ_DATA *obj = NULL;
  ROOM_INDEX_DATA *original = NULL;
  const char *pdesc = NULL;
  bool doexaprog = FALSE;
  short door = 0;
  int number = 0, cnt = 0;

  if( !ch->desc )
    return;

  if( ch->position < POS_SLEEPING )
  {
    send_to_char( "You can't see anything but stars!\r\n", ch );
    return;
  }

  if( ch->position == POS_SLEEPING )
  {
    send_to_char( "You can't see anything, you're sleeping!\r\n", ch );
    return;
  }

  if( !check_blind( ch ) )
    return;

  if( !IS_NPC( ch )
      && !IS_SET( ch->act, PLR_HOLYLIGHT )
      && !IS_AFFECTED( ch, AFF_TRUESIGHT ) && room_is_dark( ch->in_room ) )
  {
    set_char_color( AT_DGREY, ch );
    send_to_char( "It is pitch black ... \r\n", ch );
    show_char_to_char( ch->in_room->first_person, ch );
    return;
  }

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

  doexaprog = str_cmp( "noprog", arg2 ) && str_cmp( "noprog", arg3 );

  if( arg1[0] == '\0' || !str_cmp( arg1, "auto" ) )
  {
    SHIP_DATA *ship = NULL;

    /* 'look' or 'look auto' */
    set_char_color( AT_RMNAME, ch );
    ch_printf( ch, "%s (%s) ", ch->in_room->name,
	       get_sector_name(ch->in_room->sector_type) );

    if( !ch->desc->original )
    {
      if( ( IS_IMMORTAL( ch ) )
	  && ( IS_SET( ch->pcdata->flags, PCFLAG_ROOM ) ) )
      {
	set_char_color( AT_BLUE, ch );	/* Added 10/17 by Kuran of */
	send_to_char( "{", ch );	/* SWReality */
	ch_printf( ch, "%d", ch->in_room->vnum );
	send_to_char( "}", ch );
	set_char_color( AT_CYAN, ch );
	send_to_char( "[", ch );
	send_to_char( flag_string( ch->in_room->room_flags, r_flags ),
	    ch );
	send_to_char( "]", ch );
      }
    }

    send_to_char( "\r\n", ch );
    set_char_color( AT_RMDESC, ch );

    if( arg1[0] == '\0'
	|| ( !IS_NPC( ch ) && !IS_SET( ch->act, PLR_BRIEF ) ) )
      send_to_char( ch->in_room->description, ch );

    if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_AUTOEXIT ) )
      do_exits( ch, STRLIT_EMPTY );

    show_ships_to_char( ch->in_room->first_ship, ch );
    show_list_to_char( ch->in_room->first_content, ch, FALSE, FALSE );
    show_char_to_char( ch->in_room->first_person, ch );

    if( str_cmp( arg1, "auto" ) )
      if( ( ship = ship_from_cockpit( ch->in_room ) ) != NULL )
      {
	set_char_color( AT_WHITE, ch );
	ch_printf( ch,
	    "\r\nThrough the transparisteel windows you see:\r\n" );

	if( ship->starsystem )
	{
	  MISSILE_DATA *missile;
	  SHIP_DATA *target;
	  PLANET_DATA *planet;

	  set_char_color( AT_GREEN, ch );
	  if( ship->starsystem->star1
	      && str_cmp( ship->starsystem->star1, "" ) )
	    ch_printf( ch, "&YThe star, %s.\r\n",
		ship->starsystem->star1 );
	  if( ship->starsystem->star2
	      && str_cmp( ship->starsystem->star2, "" ) )
	    ch_printf( ch, "&YThe star, %s.\r\n",
		ship->starsystem->star2 );
	  for( planet = ship->starsystem->first_planet; planet;
	      planet = planet->next_in_system )
	    ch_printf( ch, "&GThe planet, %s.\r\n", planet->name );
	  for( target = ship->starsystem->first_ship; target;
	      target = target->next_in_starsystem )
	  {
	    if( target != ship )
	      ch_printf( ch, "&C%s\r\n", target->name );
	  }
	  for( missile = ship->starsystem->first_missile; missile;
	      missile = missile->next_in_starsystem )
	    ch_printf( ch, "&RA missile.\r\n" );
	}
	else if( ship->location == ship->lastdoc )
	{
	  ROOM_INDEX_DATA *to_room;

	  if( ( to_room = get_room_index( ship->location ) ) != NULL )
	  {
	    ch_printf( ch, "\r\n" );
	    original = ch->in_room;
	    char_from_room( ch );
	    char_to_room( ch, to_room );
	    do_glance( ch, STRLIT_EMPTY );
	    char_from_room( ch );
	    char_to_room( ch, original );
	  }
	}
      }

    return;
  }

  if( !str_cmp( arg1, "under" ) )
  {
    int count;

    /* 'look under' */
    if( arg2[0] == '\0' )
    {
      send_to_char( "Look beneath what?\r\n", ch );
      return;
    }

    if( ( obj = get_obj_here( ch, arg2 ) ) == NULL )
    {
      send_to_char( "You do not see that here.\r\n", ch );
      return;
    }
    if( ch->carry_weight + obj->weight > can_carry_w( ch ) )
    {
      send_to_char( "It's too heavy for you to look under.\r\n", ch );
      return;
    }
    count = obj->count;
    obj->count = 1;
    act( AT_PLAIN, "You lift $p and look beneath it:", ch, obj, NULL,
	TO_CHAR );
    act( AT_PLAIN, "$n lifts $p and looks beneath it:", ch, obj, NULL,
	TO_ROOM );
    obj->count = count;
    if( IS_OBJ_STAT( obj, ITEM_COVERING ) )
      show_list_to_char( obj->first_content, ch, TRUE, TRUE );
    else
      send_to_char( "Nothing.\r\n", ch );
    if( doexaprog )
      oprog_examine_trigger( ch, obj );
    return;
  }

  if( !str_cmp( arg1, "i" ) || !str_cmp( arg1, "in" ) )
  {
    int count;

    /* 'look in' */
    if( arg2[0] == '\0' )
    {
      send_to_char( "Look in what?\r\n", ch );
      return;
    }

    if( ( obj = get_obj_here( ch, arg2 ) ) == NULL )
    {
      send_to_char( "You do not see that here.\r\n", ch );
      return;
    }

    switch ( obj->item_type )
    {
      default:
	send_to_char( "That is not a container.\r\n", ch );
	break;

      case ITEM_DRINK_CON:
	if( obj->value[1] <= 0 )
	{
	  send_to_char( "It is empty.\r\n", ch );
	  if( doexaprog )
	    oprog_examine_trigger( ch, obj );
	  break;
	}

	ch_printf( ch, "It's %s full of a %s liquid.\r\n",
	    obj->value[1] < obj->value[0] / 4
	    ? "less than" :
	    obj->value[1] < 3 * obj->value[0] / 4
	    ? "about" : "more than",
	    liq_table[obj->value[2]].liq_color );

	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	break;

      case ITEM_CONTAINER:
      case ITEM_CORPSE_NPC:
      case ITEM_CORPSE_PC:
      case ITEM_DROID_CORPSE:
	if( IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	  send_to_char( "It is closed.\r\n", ch );
	  break;
	}

	count = obj->count;
	obj->count = 1;
	act( AT_PLAIN, "$p contains:", ch, obj, NULL, TO_CHAR );
	obj->count = count;
	show_list_to_char( obj->first_content, ch, TRUE, TRUE );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	break;
    }
    return;
  }

  if( ( pdesc =
	get_extra_descr( arg1, ch->in_room->first_extradesc ) ) != NULL )
  {
    send_to_char( pdesc, ch );
    return;
  }

  door = get_door( arg1 );
  if( ( pexit = find_door( ch, arg1, TRUE ) ) != NULL )
  {
    if( pexit->keyword )
    {
      if( IS_SET( pexit->exit_info, EX_CLOSED )
	  && !IS_SET( pexit->exit_info, EX_WINDOW ) )
      {
	if( IS_SET( pexit->exit_info, EX_SECRET ) && door != -1 )
	  send_to_char( "Nothing special there.\r\n", ch );
	else
	  act( AT_PLAIN, "The $d is closed.", ch, NULL, pexit->keyword,
	      TO_CHAR );
	return;
      }
      if( IS_SET( pexit->exit_info, EX_BASHED ) )
	act( AT_RED, "The $d has been bashed from its hinges!", ch, NULL,
	    pexit->keyword, TO_CHAR );
    }

    if( pexit->description && pexit->description[0] != '\0' )
      send_to_char( pexit->description, ch );
    else
      send_to_char( "Nothing special there.\r\n", ch );

    /*
     * Ability to look into the next room                     -Thoric
     */
    if( pexit->to_room )
    {
      if( room_is_private( ch, pexit->to_room ) && !IS_IMMORTAL( ch ) )
      {
	set_char_color( AT_WHITE, ch );
	send_to_char( "That room is private buster!\r\n", ch );
	return;
      }
      original = ch->in_room;
      char_from_room( ch );
      char_to_room( ch, pexit->to_room );
      do_look( ch, STRLIT_AUTO );
      do_scan( ch, arg1 );
      char_from_room( ch );
      char_to_room( ch, original );
    }
    return;
  }
  else if( door != -1 )
  {
    send_to_char( "Nothing special there.\r\n", ch );
    return;
  }

  if( ( victim = get_char_room( ch, arg1 ) ) != NULL )
  {
    show_char_to_char_1( victim, ch );
    return;
  }

  /* finally fixed the annoying look 2.obj desc bug   -Thoric */
  number = number_argument( arg1, arg );
  for( cnt = 0, obj = ch->last_carrying; obj; obj = obj->prev_content )
  {
    if( can_see_obj( ch, obj ) )
    {
      if( ( pdesc =
	    get_extra_descr( arg, obj->first_extradesc ) ) != NULL )
      {
	if( ( cnt += obj->count ) < number )
	  continue;
	send_to_char( pdesc, ch );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	return;
      }

      if( ( pdesc = get_extra_descr( arg, obj->pIndexData->first_extradesc ) ) != NULL )
      {
	if( ( cnt += obj->count ) < number )
	  continue;
	send_to_char( pdesc, ch );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	return;
      }

      if( nifty_is_name_prefix( arg, obj->name ) )
      {
	if( ( cnt += obj->count ) < number )
	  continue;
	pdesc =
	  get_extra_descr( obj->name,
	      obj->pIndexData->first_extradesc );
	if( !pdesc )
	  pdesc = get_extra_descr( obj->name, obj->first_extradesc );
	if( !pdesc )
	  send_to_char( "You see nothing special.\r\n", ch );
	else
	  send_to_char( pdesc, ch );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	return;
      }
    }
  }

  for( obj = ch->in_room->last_content; obj; obj = obj->prev_content )
  {
    if( can_see_obj( ch, obj ) )
    {
      if( ( pdesc = get_extra_descr( arg, obj->first_extradesc ) ) != NULL )
      {
	if( ( cnt += obj->count ) < number )
	  continue;
	send_to_char( pdesc, ch );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	return;
      }

      if( ( pdesc = get_extra_descr( arg, obj->pIndexData->first_extradesc )))
      {
	if( ( cnt += obj->count ) < number )
	  continue;
	send_to_char( pdesc, ch );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	return;
      }

      if( nifty_is_name_prefix( arg, obj->name ) )
      {
	if( ( cnt += obj->count ) < number )
	  continue;
	pdesc =
	  get_extra_descr( obj->name,
	      obj->pIndexData->first_extradesc );
	if( !pdesc )
	  pdesc = get_extra_descr( obj->name, obj->first_extradesc );
	if( !pdesc )
	  send_to_char( "You see nothing special.\r\n", ch );
	else
	  send_to_char( pdesc, ch );
	if( doexaprog )
	  oprog_examine_trigger( ch, obj );
	return;
      }
    }
  }

  send_to_char( "You do not see that here.\r\n", ch );
}

void show_condition( CHAR_DATA * ch, CHAR_DATA * victim )
{
  char buf[MAX_STRING_LENGTH];
  int percent = 0;

  if( victim->max_hit > 0 )
    percent =
      ( int ) ( ( 100.0 * ( double ) ( victim->hit ) ) /
		( double ) ( victim->max_hit ) );
  else
    percent = -1;

  strcpy( buf, PERS( victim, ch ) );

  if( IS_NPC( victim ) && IS_SET( victim->act, ACT_DROID ) )
  {
    if( percent >= 100 )
      strcat( buf, " is in perfect condition.\r\n" );
    else if( percent >= 90 )
      strcat( buf, " is slightly scratched.\r\n" );
    else if( percent >= 80 )
      strcat( buf, " has a few scrapes.\r\n" );
    else if( percent >= 70 )
      strcat( buf, " has some dents.\r\n" );
    else if( percent >= 60 )
      strcat( buf, " has a couple holes in its plating.\r\n" );
    else if( percent >= 50 )
      strcat( buf, " has a many broken pieces.\r\n" );
    else if( percent >= 40 )
      strcat( buf, " has many exposed circuits.\r\n" );
    else if( percent >= 30 )
      strcat( buf, " is leaking oil.\r\n" );
    else if( percent >= 20 )
      strcat( buf, " has smoke coming out of it.\r\n" );
    else if( percent >= 10 )
      strcat( buf, " is almost completely broken.\r\n" );
    else
      strcat( buf, " is about to EXPLODE.\r\n" );
  }
  else
  {
    if( percent >= 100 )
      strcat( buf, " is in perfect health.\r\n" );
    else if( percent >= 90 )
      strcat( buf, " is slightly scratched.\r\n" );
    else if( percent >= 80 )
      strcat( buf, " has a few bruises.\r\n" );
    else if( percent >= 70 )
      strcat( buf, " has some cuts.\r\n" );
    else if( percent >= 60 )
      strcat( buf, " has several wounds.\r\n" );
    else if( percent >= 50 )
      strcat( buf, " has many nasty wounds.\r\n" );
    else if( percent >= 40 )
      strcat( buf, " is bleeding freely.\r\n" );
    else if( percent >= 30 )
      strcat( buf, " is covered in blood.\r\n" );
    else if( percent >= 20 )
      strcat( buf, " is leaking guts.\r\n" );
    else if( percent >= 10 )
      strcat( buf, " is almost dead.\r\n" );
    else
      strcat( buf, " is DYING.\r\n" );
  }

  buf[0] = UPPER( buf[0] );
  send_to_char( buf, ch );
}

/* A much simpler version of look, this function will show you only
   the condition of a mob or pc, or if used without an argument, the
   same you would see if you enter the room and have config +brief.
   -- Narn, winter '96
   */
void do_glance( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  CHAR_DATA *victim;
  int save_act;

  if( !ch->desc )
    return;

  if( ch->position < POS_SLEEPING )
  {
    send_to_char( "You can't see anything but stars!\r\n", ch );
    return;
  }

  if( ch->position == POS_SLEEPING )
  {
    send_to_char( "You can't see anything, you're sleeping!\r\n", ch );
    return;
  }

  if( !check_blind( ch ) )
    return;

  argument = one_argument( argument, arg1 );

  if( arg1[0] == '\0' )
  {
    save_act = ch->act;
    SET_BIT( ch->act, PLR_BRIEF );
    do_look( ch, STRLIT_AUTO );
    ch->act = save_act;
    return;
  }

  if( ( victim = get_char_room( ch, arg1 ) ) == NULL )
  {
    send_to_char( "They're not here.", ch );
    return;
  }
  else
  {
    if( can_see( victim, ch ) )
    {
      act( AT_ACTION, "$n glances at you.", ch, NULL, victim, TO_VICT );
      act( AT_ACTION, "$n glances at $N.", ch, NULL, victim, TO_NOTVICT );
    }

    show_condition( ch, victim );
    return;
  }
}

static void examine_obj_armor( CHAR_DATA * ch, OBJ_DATA * obj )
{
  int dam = 0;
  char buf[MAX_STRING_LENGTH];
  *buf = '\0';

  if( obj->value[1] == 0 )
    obj->value[1] = obj->value[0];

  if( obj->value[1] == 0 )
    obj->value[1] = 1;

  dam = ( obj->value[0] * 10 ) / obj->value[1];

  strcpy( buf, "As you look more closely, you notice that it is " );

  if( dam >= 10 )
    strcat( buf, "in superb condition." );
  else if( dam == 9 )
    strcat( buf, "in very good condition." );
  else if( dam == 8 )
    strcat( buf, "in good shape." );
  else if( dam == 7 )
    strcat( buf, "showing a bit of wear." );
  else if( dam == 6 )
    strcat( buf, "a little run down." );
  else if( dam == 5 )
    strcat( buf, "in need of repair." );
  else if( dam == 4 )
    strcat( buf, "in great need of repair." );
  else if( dam == 3 )
    strcat( buf, "in dire need of repair." );
  else if( dam == 2 )
    strcat( buf, "very badly worn." );
  else if( dam == 1 )
    strcat( buf, "practically worthless." );
  else if( dam <= 0 )
    strcat( buf, "broken." );

  strcat( buf, "\r\n" );
  send_to_char( buf, ch );
}

static void examine_obj_weapon( CHAR_DATA * ch, OBJ_DATA * obj )
{
  int dam = INIT_WEAPON_CONDITION - obj->value[0];
  char buf[MAX_STRING_LENGTH];
  *buf = '\0';

  strcpy( buf, "As you look more closely, you notice that it is " );

  if( dam == 0 )
    strcat( buf, "in superb condition." );
  else if( dam == 1 )
    strcat( buf, "in excellent condition." );
  else if( dam == 2 )
    strcat( buf, "in very good condition." );
  else if( dam == 3 )
    strcat( buf, "in good shape." );
  else if( dam == 4 )
    strcat( buf, "showing a bit of wear." );
  else if( dam == 5 )
    strcat( buf, "a little run down." );
  else if( dam == 6 )
    strcat( buf, "in need of repair." );
  else if( dam == 7 )
    strcat( buf, "in great need of repair." );
  else if( dam == 8 )
    strcat( buf, "in dire need of repair." );
  else if( dam == 9 )
    strcat( buf, "very badly worn." );
  else if( dam == 10 )
    strcat( buf, "practically worthless." );
  else if( dam == 11 )
    strcat( buf, "almost broken." );
  else if( dam == 12 )
    strcat( buf, "broken." );

  strcat( buf, "\r\n" );

  send_to_char( buf, ch );

  if( obj->value[3] == WEAPON_BLASTER )
  {
    if( obj->blaster_setting == BLASTER_FULL )
      ch_printf( ch, "It is set on FULL power.\r\n" );
    else if( obj->blaster_setting == BLASTER_HIGH )
      ch_printf( ch, "It is set on HIGH power.\r\n" );
    else if( obj->blaster_setting == BLASTER_NORMAL )
      ch_printf( ch, "It is set on NORMAL power.\r\n" );
    else if( obj->blaster_setting == BLASTER_HALF )
      ch_printf( ch, "It is set on HALF power.\r\n" );
    else if( obj->blaster_setting == BLASTER_LOW )
      ch_printf( ch, "It is set on LOW power.\r\n" );
    else if( obj->blaster_setting == BLASTER_STUN )
      ch_printf( ch, "It is set on STUN.\r\n" );
    ch_printf( ch, "It has from %d to %d shots remaining.\r\n",
	obj->value[4] / 5, obj->value[4] );
  }
  else if( ( obj->value[3] == WEAPON_LIGHTSABER ||
	obj->value[3] == WEAPON_VIBRO_BLADE ) )
  {
    ch_printf( ch, "It has %d/%d units of charge remaining.\r\n",
	obj->value[4], obj->value[5] );
  }
}

static void examine_obj_food( CHAR_DATA * ch, OBJ_DATA * obj )
{
  int dam = 0;
  char buf[MAX_STRING_LENGTH];
  *buf = '\0';

  if( obj->timer > 0 && obj->value[1] > 0 )
    dam = ( obj->timer * 10 ) / obj->value[1];
  else
    dam = 10;

  strcpy( buf, "As you examine it carefully you notice that it " );

  if( dam >= 10 )
    strcat( buf, "is fresh." );
  else if( dam == 9 )
    strcat( buf, "is nearly fresh." );
  else if( dam == 8 )
    strcat( buf, "is perfectly fine." );
  else if( dam == 7 )
    strcat( buf, "looks good." );
  else if( dam == 6 )
    strcat( buf, "looks ok." );
  else if( dam == 5 )
    strcat( buf, "is a little stale." );
  else if( dam == 4 )
    strcat( buf, "is a bit stale." );
  else if( dam == 3 )
    strcat( buf, "smells slightly off." );
  else if( dam == 2 )
    strcat( buf, "smells quite rank." );
  else if( dam == 1 )
    strcat( buf, "smells revolting." );
  else if( dam <= 0 )
    strcat( buf, "is crawling with maggots." );

  strcat( buf, "\r\n" );
  send_to_char( buf, ch );
}

static void examine_obj_corpse( CHAR_DATA * ch, OBJ_DATA * obj )
{
  short timerfrac = obj->timer;
  char buf[MAX_STRING_LENGTH];

  if( obj->item_type == ITEM_CORPSE_PC )
    timerfrac = ( int ) obj->timer / 8 + 1;

  switch ( timerfrac )
  {
    default:
      send_to_char( "This corpse has recently been slain.\r\n", ch );
      break;
    case 4:
      send_to_char( "This corpse was slain a little while ago.\r\n", ch );
      break;
    case 3:
      send_to_char
	( "A foul smell rises from the corpse, and it is covered in flies.\r\n",
	  ch );
      break;
    case 2:
      send_to_char
	( "A writhing mass of maggots and decay, you can barely go near this corpse.\r\n",
	  ch );
      break;
    case 1:
    case 0:
      send_to_char
	( "Little more than bones, there isn't much left of this corpse.\r\n",
	  ch );
      break;
  }

  if( IS_OBJ_STAT( obj, ITEM_COVERING ) )
    return;

  send_to_char( "When you look inside, you see:\r\n", ch );
  snprintf( buf, MAX_STRING_LENGTH, "in '%s' noprog", obj->name );
  do_look( ch, buf );
}

static void examine_obj_droid_corpse( CHAR_DATA * ch, OBJ_DATA * obj )
{
  short timerfrac = obj->timer;
  char buf[MAX_STRING_LENGTH];

  switch ( timerfrac )
  {
    default:
      send_to_char( "These remains are still smoking.\r\n", ch );
      break;

    case 4:
      send_to_char
	( "The parts of this droid have cooled down completely.\r\n", ch );
      break;

    case 3:
      send_to_char( "The broken droid components are beginning to rust.\r\n",
	  ch );
      break;

    case 2:
      send_to_char( "The pieces are completely covered in rust.\r\n", ch );
      break;

    case 1:
    case 0:
      send_to_char( "All that remains of it is a pile of crumbling rust.\r\n",
	  ch );
      break;
  }

  if( IS_OBJ_STAT( obj, ITEM_COVERING ) )
    return;

  send_to_char( "When you look inside, you see:\r\n", ch );
  snprintf( buf, MAX_STRING_LENGTH, "in '%s' noprog", obj->name );
  do_look( ch, buf );
}

static void examine_obj_drink_container( CHAR_DATA * ch, OBJ_DATA * obj )
{
  char buf[MAX_STRING_LENGTH];
  send_to_char( "When you look inside, you see:\r\n", ch );
  snprintf( buf, MAX_STRING_LENGTH, "in '%s' noprog", obj->name );
  do_look( ch, buf );
}

void do_examine( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char arg[MAX_INPUT_LENGTH];
  OBJ_DATA *obj = NULL;
  BOARD_DATA *board = NULL;

  if( !argument )
  {
    bug( "do_examine: null argument.", 0 );
    return;
  }

  if( !ch )
  {
    bug( "do_examine: null ch.", 0 );
    return;
  }

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "Examine what?\r\n", ch );
    return;
  }

  snprintf( buf, MAX_STRING_LENGTH, "%s noprog", arg );
  do_look( ch, buf );

  /*
   * Support for looking at boards, checking equipment conditions,
   * and support for trigger positions by Thoric
   */
  if( ( obj = get_obj_here( ch, arg ) ) != NULL )
  {
    if( ( board = get_board( obj ) ) != NULL )
    {
      if( board->num_posts )
	ch_printf( ch,
	    "There are about %d notes posted here.  Type 'note list' to list them.\r\n",
	    board->num_posts );
      else
	send_to_char( "There aren't any notes posted here.\r\n", ch );
    }

    switch ( obj->item_type )
    {
      default:
	break;

      case ITEM_ARMOR:
	examine_obj_armor( ch, obj );
	break;

      case ITEM_WEAPON:
	examine_obj_weapon( ch, obj );
	break;

      case ITEM_FOOD:
	examine_obj_food( ch, obj );
	break;

      case ITEM_CORPSE_PC:
      case ITEM_CORPSE_NPC:
	examine_obj_corpse( ch, obj );
	break;

      case ITEM_DROID_CORPSE:
	examine_obj_droid_corpse( ch, obj );
	break;

      case ITEM_CONTAINER:
	if( IS_OBJ_STAT( obj, ITEM_COVERING ) )
	  break;

      case ITEM_DRINK_CON:
	examine_obj_drink_container( ch, obj );
	break;
    }

    if( IS_OBJ_STAT( obj, ITEM_COVERING ) )
    {
      snprintf( buf, MAX_STRING_LENGTH, "under %s noprog", arg );
      do_look( ch, buf );
    }

    oprog_examine_trigger( ch, obj );
    if( char_died( ch ) || obj_extracted( obj ) )
      return;
  }
}

void do_exits( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  EXIT_DATA *pexit = NULL;
  bool found = FALSE;
  bool fAuto = FALSE;

  set_char_color( AT_EXITS, ch );
  buf[0] = '\0';
  fAuto = !str_cmp( argument, "auto" );

  if( !check_blind( ch ) )
    return;

  strcpy( buf, fAuto ? "Exits:" : "Obvious exits:\r\n" );

  for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
  {
    if( pexit->to_room && !IS_SET( pexit->exit_info, EX_HIDDEN ) )
    {
      found = TRUE;
      if( !fAuto )
      {
	if( IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	  snprintf( buf + strlen( buf ), MAX_STRING_LENGTH - strlen( buf ),
	      "%-5s - (closed)\r\n",
	      capitalize( dir_name[pexit->vdir] ) );
	}
	else if( IS_SET( pexit->exit_info, EX_WINDOW ) )
	{
	  snprintf( buf + strlen( buf ), MAX_STRING_LENGTH - strlen( buf ),
	      "%-5s - (window)\r\n",
	      capitalize( dir_name[pexit->vdir] ) );
	}
	else if( IS_SET( pexit->exit_info, EX_xAUTO ) )
	{
	  snprintf( buf + strlen( buf ), MAX_STRING_LENGTH - strlen( buf ),
	      "%-5s - %s\r\n",
	      capitalize( pexit->keyword ),
	      room_is_dark( pexit->to_room )
	      ? "Too dark to tell" : pexit->to_room->name );
	}
	else
	  snprintf( buf + strlen( buf ), MAX_STRING_LENGTH - strlen( buf ),
	      "%-5s - %s\r\n",
	      capitalize( dir_name[pexit->vdir] ),
	      room_is_dark( pexit->to_room )
	      ? "Too dark to tell" : pexit->to_room->name );
      }
      else
      {
	snprintf( buf + strlen( buf ), MAX_STRING_LENGTH - strlen( buf ), " %s",
	    capitalize( dir_name[pexit->vdir] ) );
      }
    }
  }

  if( !found )
    strcat( buf, fAuto ? " none.\r\n" : "None.\r\n" );
  else if( fAuto )
    strcat( buf, ".\r\n" );
  send_to_char( buf, ch );
}

void do_time( CHAR_DATA * ch, char *argument )
{
  extern char str_boot_time[];
  extern char reboot_time[];
  const char *suf;
  int day = time_info.day + 1;

  if( day > 4 && day < 20 )
    suf = "th";
  else if( day % 10 == 1 )
    suf = "st";
  else if( day % 10 == 2 )
    suf = "nd";
  else if( day % 10 == 3 )
    suf = "rd";
  else
    suf = "th";

  set_char_color( AT_YELLOW, ch );
  ch_printf( ch,
      "It is %d o'clock %s, Day of %s, %d%s the Month of %s.\r\n"
      "The mud started up at:    %s\r"
      "The system time (E.S.T.): %s\r"
      "Next Reboot is set for:   %s\r",
      ( time_info.hour % 12 == 0 ) ? 12 : time_info.hour % 12,
      time_info.hour >= 12 ? "pm" : "am",
      day_name[day % 7],
      day, suf,
      month_name[time_info.month],
      str_boot_time, ( char * ) ctime( &current_time ), reboot_time );
}

void do_weather( CHAR_DATA * ch, char *argument )
{
  static const char *const sky_look[4] = {
    "cloudless",
    "cloudy",
    "rainy",
    "lit by flashes of lightning"
  };

  if( !IS_OUTSIDE( ch ) )
  {
    send_to_char( "You can't see the sky from here.\r\n", ch );
    return;
  }

  set_char_color( AT_BLUE, ch );
  ch_printf( ch, "The sky is %s and %s.\r\n",
      sky_look[weather_info.sky],
      weather_info.change >= 0
      ? "a warm southerly breeze blows"
      : "a cold northern gust blows" );
}


/*
 * Moved into a separate function so it can be used for other things
 * ie: online help editing				-Thoric
 */
HELP_DATA *get_help( CHAR_DATA *ch, const char *orig_argument )
{
  char argall[MAX_INPUT_LENGTH];
  char argone[MAX_INPUT_LENGTH];
  char argnew[MAX_INPUT_LENGTH];
  char argument_buf[MAX_INPUT_LENGTH];
  char *argument = argument_buf;
  HELP_DATA *pHelp = NULL;
  int lev = 0;

  snprintf( argument_buf, MAX_INPUT_LENGTH, "%s", orig_argument );

  if( argument[0] == '\0' )
    snprintf( argument_buf, MAX_INPUT_LENGTH, "%s", "summary" );

  if( isdigit( ( int ) argument[0] ) )
  {
    lev = number_argument( argument, argnew );
    argument = argnew;
  }
  else
    lev = -2;
  /*
   * Tricky argument handling so 'help a b' doesn't match a.
   */
  argall[0] = '\0';
  while( argument[0] != '\0' )
  {
    argument = one_argument( argument, argone );
    if( argall[0] != '\0' )
      strcat( argall, " " );
    strcat( argall, argone );
  }

  for( pHelp = first_help; pHelp; pHelp = pHelp->next )
  {
    if( pHelp->level > get_trust( ch ) )
      continue;
    if( lev != -2 && pHelp->level != lev )
      continue;

    if( is_name( argall, pHelp->keyword ) )
      return pHelp;
  }

  return NULL;
}


/*
 * Now this is cleaner
 */
void do_help( CHAR_DATA * ch, char *argument )
{
  HELP_DATA *pHelp = NULL;

  if( ( pHelp = get_help( ch, argument ) ) == NULL )
  {
    send_to_char( "No help on that word.\r\n", ch );
    return;
  }

  if( pHelp->level >= 0 && str_cmp( argument, "imotd" ) )
  {
    send_to_pager( pHelp->keyword, ch );
    send_to_pager( "\r\n", ch );
  }

  if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_SOUND ) )
    send_to_pager( "!!SOUND(help)", ch );

  /*
   * Strip leading '.' to allow initial blanks.
   */
  if( pHelp->text[0] == '.' )
    send_to_pager_color( pHelp->text + 1, ch );
  else
    send_to_pager_color( pHelp->text, ch );
}

/*
 * Help editor							-Thoric
 */
void do_hedit( CHAR_DATA * ch, char *argument )
{
  HELP_DATA *pHelp = NULL;

  if( !ch->desc )
  {
    send_to_char( "You have no descriptor.\r\n", ch );
    return;
  }

  switch ( ch->substate )
  {
    default:
      break;

    case SUB_HELP_EDIT:
      if( ( pHelp = ( HELP_DATA * ) ch->dest_buf ) == NULL )
      {
	bug( "hedit: sub_help_edit: NULL ch->dest_buf", 0 );
	stop_editing( ch );
	return;
      }

      STRFREE( pHelp->text );
      pHelp->text = copy_buffer( ch );
      stop_editing( ch );
      return;
  }

  if( ( pHelp = get_help( ch, argument ) ) == NULL )	/* new help */
  {
    HELP_DATA *tHelp = NULL;
    char argnew[MAX_INPUT_LENGTH];
    int lev = 0;
    bool new_help = TRUE;

    for( tHelp = first_help; tHelp; tHelp = tHelp->next )
      if( !str_cmp( argument, tHelp->keyword ) )
      {
	pHelp = tHelp;
	new_help = FALSE;
	break;
      }

    if( new_help )
    {
      if( isdigit( ( int ) argument[0] ) )
      {
	lev = number_argument( argument, argnew );
	argument = argnew;
      }
      else
	lev = get_trust( ch );

      CREATE( pHelp, HELP_DATA, 1 );
      pHelp->keyword = STRALLOC( strupper( argument ) );
      pHelp->text = STRALLOC( "" );
      pHelp->level = lev;
      add_help( pHelp );
    }
  }

  ch->substate = SUB_HELP_EDIT;
  ch->dest_buf = pHelp;
  start_editing( ch, pHelp->text );
}

/*
 * Stupid leading space muncher fix				-Thoric
 */
const char *help_fix( const char *text )
{
  char *fixed = NULL;

  if( !text )
    return "";

  fixed = strip_cr( text );

  if( fixed[0] == ' ' )
    fixed[0] = '.';

  return fixed;
}

void do_hset( CHAR_DATA * ch, char *argument )
{
  HELP_DATA *pHelp;
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];

  smash_tilde( argument );
  argument = one_argument( argument, arg1 );
  if( arg1[0] == '\0' )
  {
    send_to_char( "Syntax: hset <field> [value] [help page]\r\n", ch );
    send_to_char( "\r\n", ch );
    send_to_char( "Field being one of:\r\n", ch );
    send_to_char( "  level keyword remove save\r\n", ch );
    return;
  }

  if( !str_cmp( arg1, "save" ) )
  {
    FILE *fpout = NULL;
    char bak_filename[MAX_STRING_LENGTH];
    snprintf( bak_filename, MAX_STRING_LENGTH, "%s.bak", HELP_FILE );

    log_string_plus( "Saving help.dat...", LOG_NORMAL );

    rename( HELP_FILE, bak_filename );

    if( ( fpout = fopen( HELP_FILE, "w" ) ) == NULL )
    {
      bug( "hset save: fopen", 0 );
      perror( "help.dat" );
      return;
    }

    for( pHelp = first_help; pHelp; pHelp = pHelp->next )
      fprintf( fpout, "%d %s~\n%s~\n\n",
	  pHelp->level, pHelp->keyword, help_fix( pHelp->text ) );

    fprintf( fpout, "0 $~\n\n\n#$\n" );
    fclose( fpout );
    send_to_char( "Saved.\r\n", ch );
    return;
  }
  if( str_cmp( arg1, "remove" ) )
    argument = one_argument( argument, arg2 );

  if( ( pHelp = get_help( ch, argument ) ) == NULL )
  {
    send_to_char( "Cannot find help on that subject.\r\n", ch );
    return;
  }
  if( !str_cmp( arg1, "remove" ) )
  {
    UNLINK( pHelp, first_help, last_help, next, prev );
    free_help( pHelp );
    send_to_char( "Removed.\r\n", ch );
    return;
  }
  if( !str_cmp( arg1, "level" ) )
  {
    int lev;

    if( !is_number( arg2 ) )
    {
      send_to_char( "Level field must be numeric.\r\n", ch );
      return;
    }

    lev = atoi( arg2 );

    if( lev < -1 || lev > get_trust( ch ) )
    {
      send_to_char( "You can't set the level to that.\r\n", ch );
      return;
    }

    pHelp->level = lev;
    send_to_char( "Done.\r\n", ch );
    return;
  }

  if( !str_cmp( arg1, "keyword" ) )
  {
    STRFREE( pHelp->keyword );
    pHelp->keyword = STRALLOC( strupper( arg2 ) );
    send_to_char( "Done.\r\n", ch );
    return;
  }

  do_hset( ch, STRLIT_EMPTY );
}

/*
 * Show help topics in a level range				-Thoric
 * Idea suggested by Gorog
 */
void do_hlist( CHAR_DATA * ch, char *argument )
{
  int min = 0, max = 0, cnt = 0;
  char arg[MAX_INPUT_LENGTH];
  HELP_DATA *help = NULL;
  int maxlimit = get_trust( ch );
  int minlimit = IS_IMMORTAL( ch ) ? -1 : 0;
  argument = one_argument( argument, arg );

  if( arg[0] != '\0' )
  {
    min = URANGE( minlimit, atoi( arg ), maxlimit );

    if( argument[0] != '\0' )
      max = URANGE( min, atoi( argument ), maxlimit );
    else
      max = maxlimit;
  }
  else
  {
    min = minlimit;
    max = maxlimit;
  }

  set_pager_color( AT_GREEN, ch );
  pager_printf( ch, "Help Topics in level range %d to %d:\r\n\r\n", min,
      max );

  for( cnt = 0, help = first_help; help; help = help->next )
    if( help->level >= min && help->level <= max )
    {
      pager_printf( ch, "  %3d %s\r\n", help->level, help->keyword );
      ++cnt;
    }

  if( cnt )
    pager_printf( ch, "\r\n%d pages found.\r\n", cnt );
  else
    send_to_char( "None found.\r\n", ch );
}

/* 
 * New do_who	
 *
 * with WHO REQUEST, clan and homepage support.  -Thoric
 *
 * Latest version of do_who eliminates redundant code by using linked lists.
 * Shows imms separately, indicates guest and retired immortals.
 * Narn, Oct/96
 */
void do_who( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char clan_name[MAX_INPUT_LENGTH];
  char invis_str[MAX_INPUT_LENGTH];
  char char_name[MAX_INPUT_LENGTH];
  char extra_title[MAX_STRING_LENGTH];
  DESCRIPTOR_DATA *d = NULL;
  int iLevelLower = 0;
  int iLevelUpper = 200;
  int nNumber = 0;
  int nMatch = 0;
  bool fImmortalOnly = FALSE;
  bool fClanMatch = FALSE;	/* SB who clan */
  CLAN_DATA *pClan = 0;

  WHO_DATA *cur_who = NULL;
  WHO_DATA *next_who = NULL;
  WHO_DATA *first_mortal = NULL;
  WHO_DATA *first_imm = NULL;

  /*
   * Parse arguments.
   */
  for( ;; )
  {
    char arg[MAX_STRING_LENGTH];

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' )
      break;

    if( is_number( arg ) )
    {
      switch ( ++nNumber )
      {
	case 1:
	  iLevelLower = atoi( arg );
	  break;
	case 2:
	  iLevelUpper = atoi( arg );
	  break;
	default:
	  send_to_char( "Only two level numbers allowed.\r\n", ch );
	  return;
      }
    }
    else
    {
      if( strlen( arg ) < 3 )
      {
	send_to_char( "Be a little more specific please.\r\n", ch );
	return;
      }

      /*
       * Look for classes to turn on.
       */

      if( !str_cmp( arg, "imm" ) || !str_cmp( arg, "gods" ) )
      {
	fImmortalOnly = TRUE;
      }
      else if( ( pClan = get_clan( arg ) ) )
      {
	fClanMatch = TRUE;
      }
      else
      {
	send_to_char( "That's not an organization.\r\n", ch );
	return;
      }
    }
  }

  /*
   * Now find matching chars.
   */
  nMatch = 0;
  buf[0] = '\0';

  set_pager_color( AT_GREEN, ch );

  /* start from last to first to get it in the proper order */
  for( d = last_descriptor; d; d = d->prev )
  {
    CHAR_DATA *wch = NULL;

    if( ( d->connected != CON_PLAYING && d->connected != CON_EDITING )
	|| ( !can_see( ch, d->character ) && IS_IMMORTAL( d->character ) )
	|| d->original )
      continue;

    wch = d->original ? d->original : d->character;

    if( wch->top_level < iLevelLower
	|| wch->top_level > iLevelUpper
	|| ( fImmortalOnly && !IS_IMMORTAL( wch ) )
	|| ( fClanMatch && ( pClan != wch->pcdata->clan ) ) /* SB */  )
      continue;

    nMatch++;

    strcpy( char_name, "" );

    if( !nifty_is_name( wch->name, wch->pcdata->title )
	&& ch->top_level > wch->top_level )
      snprintf( extra_title, MAX_STRING_LENGTH, " [%s]", wch->name );
    else
      strcpy( extra_title, "" );

    if( wch->pcdata->clan )
    {
      CLAN_DATA *pclan = wch->pcdata->clan;
      strcpy( clan_name, " (" );

      if( clan_char_is_leader( pclan, wch ) )
	strcat( clan_name, "Leader, " );

      strcat( clan_name, pclan->name );
      strcat( clan_name, ")" );
    }
    else
      clan_name[0] = '\0';

    if( IS_SET( wch->act, PLR_WIZINVIS ) )
      snprintf( invis_str, MAX_STRING_LENGTH, "(%d) ", wch->pcdata->wizinvis );
    else
      invis_str[0] = '\0';

    if( wch->desc->connected == CON_EDITING )
      strcat( invis_str, "[Writing] " );

    snprintf( buf, MAX_STRING_LENGTH, "%s%s%s%s %s%s%s\r\n",
	invis_str,
	IS_SET( wch->act, PLR_AFK ) ? "[AFK] " : "",
	char_name,
	wch->pcdata->title,
	extra_title,
	clan_name,
	IS_IMMORTAL( wch ) ? "&R [&WCoder&R/&WImmortal&R]&W" : "&W" );

    /*  
     * This is where the old code would display the found player to the ch.
     * What we do instead is put the found data into a linked list
     */

    /* First make the structure. */
    CREATE( cur_who, WHO_DATA, 1 );
    cur_who->text = str_dup( buf );

    if( IS_OFFICIAL( wch ) )
      cur_who->type = WT_IMM;
    else
      cur_who->type = WT_MORTAL;

    /* Then put it into the appropriate list. */
    switch ( cur_who->type )
    {
      case WT_MORTAL:
	cur_who->next = first_mortal;
	first_mortal = cur_who;
	break;
      case WT_IMM:
	cur_who->next = first_imm;
	first_imm = cur_who;
	break;
    }
  }

  /* Ok, now we have three separate linked lists and what remains is to 
   * display the information and clean up.
   */

  /* Deadly list removed for swr ... now only 2 lists */
  if( first_mortal )
  {
    send_to_pager( "\r\n&R--------------------------------[ &YGalactic Citizens&R ]-------------------------&W\r\n\r\n", ch );
  }

  for( cur_who = first_mortal; cur_who; cur_who = next_who )
  {
    send_to_pager( cur_who->text, ch );

    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  if( first_imm )
  {
    send_to_pager( "\r\n&R--------------------------------[ &YElected Officials&R ]-------------------------&W\r\n\r\n", ch );
  }

  for( cur_who = first_imm; cur_who; cur_who = next_who )
  {
    send_to_pager( cur_who->text, ch );

    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  set_char_color( AT_YELLOW, ch );
  ch_printf( ch, "\r\n%d player%s.\r\n", nMatch, nMatch == 1 ? "" : "s" );
}

void do_compare( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  OBJ_DATA *obj1 = NULL;
  OBJ_DATA *obj2 = NULL;
  int value1 = 0;
  int value2 = 0;
  const char *msg = NULL;

  argument = one_argument( argument, arg1 );
  argument = one_argument( argument, arg2 );

  if( arg1[0] == '\0' )
  {
    send_to_char( "Compare what to what?\r\n", ch );
    return;
  }

  if( ( obj1 = get_obj_carry( ch, arg1 ) ) == NULL )
  {
    send_to_char( "You do not have that item.\r\n", ch );
    return;
  }

  if( arg2[0] == '\0' )
  {
    for( obj2 = ch->first_carrying; obj2; obj2 = obj2->next_content )
    {
      if( obj2->wear_loc != WEAR_NONE
	  && can_see_obj( ch, obj2 )
	  && obj1->item_type == obj2->item_type
	  && ( obj1->wear_flags & obj2->wear_flags & ~ITEM_TAKE ) != 0 )
	break;
    }

    if( !obj2 )
    {
      send_to_char( "You aren't wearing anything comparable.\r\n", ch );
      return;
    }
  }
  else
  {
    if( ( obj2 = get_obj_carry( ch, arg2 ) ) == NULL )
    {
      send_to_char( "You do not have that item.\r\n", ch );
      return;
    }
  }

  if( obj1 == obj2 )
  {
    msg = "You compare $p to itself.  It looks about the same.";
  }
  else if( obj1->item_type != obj2->item_type )
  {
    msg = "You can't compare $p and $P.";
  }
  else
  {
    switch ( obj1->item_type )
    {
      default:
	msg = "You can't compare $p and $P.";
	break;

      case ITEM_ARMOR:
	value1 = obj1->value[0];
	value2 = obj2->value[0];
	break;

      case ITEM_WEAPON:
	value1 = obj1->value[1] + obj1->value[2];
	value2 = obj2->value[1] + obj2->value[2];
	break;
    }
  }

  if( !msg )
  {
    if( value1 == value2 )
      msg = "$p and $P look about the same.";
    else if( value1 > value2 )
      msg = "$p looks better than $P.";
    else
      msg = "$p looks worse than $P.";
  }

  act( AT_PLAIN, msg, ch, obj1, obj2, TO_CHAR );
}

void do_where( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim = NULL;
  DESCRIPTOR_DATA *d = NULL;
  bool found = FALSE;

  if( !IS_IMMORTAL( ch ) )
  {
    send_to_char( "If only life were really that simple...\r\n", ch );
    return;
  }

  one_argument( argument, arg );

  set_pager_color( AT_PERSON, ch );
  if( arg[0] == '\0' )
  {
    send_to_pager( "Players logged in:\r\n", ch );
    found = FALSE;
    for( d = first_descriptor; d; d = d->next )
      if( ( d->connected == CON_PLAYING || d->connected == CON_EDITING )
	  && ( victim = d->character ) != NULL
	  && !IS_NPC( victim ) && victim->in_room && can_see( ch, victim ) )
      {
	found = TRUE;
	pager_printf( ch, "%-28s %s\r\n",
	    victim->name, victim->in_room->name );
      }
    if( !found )
      send_to_char( "None\r\n", ch );
  }
  else
  {
    found = FALSE;
    for( victim = first_char; victim; victim = victim->next )
      if( victim->in_room
	  && !IS_AFFECTED( victim, AFF_HIDE )
	  && !IS_AFFECTED( victim, AFF_SNEAK )
	  && can_see( ch, victim ) && is_name( arg, victim->name ) )
      {
	found = TRUE;
	pager_printf( ch, "%-28s %s\r\n",
	    PERS( victim, ch ), victim->in_room->name );
	break;
      }
    if( !found )
      act( AT_PLAIN, "You didn't find any $T.", ch, NULL, arg, TO_CHAR );
  }
}

void do_consider( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  CHAR_DATA *victim = NULL;
  const char *msg = "";
  int diff = 0;

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    send_to_char( "Consider killing whom?\r\n", ch );
    return;
  }

  if( ( victim = get_char_room( ch, arg ) ) == NULL )
  {
    send_to_char( "They're not here.\r\n", ch );
    return;
  }

  diff = ( victim->hitroll - ch->hitroll ) * 5;
  diff += ( int ) ( victim->max_hit - ch->max_hit );

  if( diff <= -200 )
    msg = "$N looks like a feather!";
  else if( diff <= -150 )
    msg = "Hey! Where'd $N go?";
  else if( diff <= -100 )
    msg = "Easy as picking off womp rats at beggers canyon!";
  else if( diff <= -50 )
    msg = "$N is a wimp.";
  else if( diff <= 0 )
    msg = "$N looks weaker than you.";
  else if( diff <= 50 )
    msg = "$N looks about as strong as you.";
  else if( diff <= 100 )
    msg = "It would take a bit of luck...";
  else if( diff <= 150 )
    msg = "It would take a lot of luck, and a really big blaster!";
  else if( diff <= 200 )
    msg = "Why don't you just attack a star destoyer with a vibroblade?";
  else
    msg = "$N is built like an AT-AT!";

  act( AT_CONSIDER, msg, ch, NULL, victim, TO_CHAR );
}

void do_forget( CHAR_DATA * ch, char *argument )
{
  int sn = 0;

  if( IS_NPC( ch ) || !ch->pcdata )
    return;

  if( argument[0] == '\0' )
  {
    send_to_char( "Forget what?\r\n", ch );
    return;
  }

  sn = skill_lookup( argument );

  if( sn == -1 )
  {
    send_to_char( "No such skill...\r\n", ch );
    return;
  }

  if( character_skill_level( ch, sn ) <= 0 )
  {
    send_to_char( "You don't know that skill...\r\n", ch );
    return;
  }

  if( character_skill_level( ch, sn ) > 50 )
  {
    send_to_char( "You cannot forget something you know so well...\r\n",
	ch );
    return;
  }

  send_to_char( "Forget what?... *chuckle*\r\n", ch );
  set_skill_level( ch, sn, 0 );
  ch->pcdata->num_skills--;
}

void do_teach( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  int sn = 0;
  char arg[MAX_INPUT_LENGTH];

  if( IS_NPC( ch ) )
    return;

  argument = one_argument( argument, arg );

  if( argument[0] == '\0' )
  {
    send_to_char( "Teach who, what?\r\n", ch );
    return;
  }
  else
  {
    CHAR_DATA *victim = NULL;
    int adept = 20;

    if( !IS_AWAKE( ch ) )
    {
      send_to_char( "In your dreams, or what?\r\n", ch );
      return;
    }

    if( ( victim = get_char_room( ch, arg ) ) == NULL )
    {
      send_to_char( "They don't seem to be here...\r\n", ch );
      return;
    }

    if( IS_NPC( victim ) )
    {
      send_to_char( "You can't teach that to them!\r\n", ch );
      return;
    }

    sn = skill_lookup( argument );

    if( sn == -1 )
    {
      act( AT_TELL, "You have no idea what that is.",
	  victim, NULL, ch, TO_VICT );
      return;
    }

    if( character_skill_level( victim, sn ) >= adept )
    {
      act( AT_TELL, "$n must practice that on their own.", victim, NULL,
	  ch, TO_VICT );
      return;
    }

    if( victim->pcdata->num_skills >= get_curr_int( victim ) )
    {
      act( AT_TELL,
	  "$n is not intelligent enough to learn any more skills.",
	  victim, NULL, ch, TO_VICT );
      return;
    }

    if( ( victim->pcdata->num_skills - victim->pcdata->adept_skills ) >= 5 )
    {
      act( AT_TELL,
	  "$N needs to perfect some of the skills $E has before learning new ones.",
	  ch, NULL, victim, TO_CHAR );
      return;
    }

    if( skill_table[sn]->type == SKILL_SPELL
	&& ( get_curr_frc( victim ) <= 0 || victim->max_mana <= 0 ) )
    {
      act( AT_TELL, "$n is not force capable.", victim, NULL, ch,
	  TO_VICT );
      return;
    }

    if( ( sn == gsn_lightsaber_crafting || sn == gsn_parry
	  || sn == gsn_lightsabers ) && ( get_curr_frc( victim ) <= 0
	    || victim->max_mana <= 0 ) )
    {
      act( AT_TELL, "$n is not a jedi.", victim, NULL, ch, TO_VICT );
      return;
    }

    if( get_curr_frc( victim ) > 0 && victim->max_mana > 0
	&& skill_table[sn]->type != SKILL_SPELL )
      if( sn != gsn_lightsaber_crafting && sn != gsn_parry
	  && sn != gsn_lightsabers && sn != gsn_aid && sn != gsn_dodge
	  && sn != gsn_rescue && sn != gsn_grip && sn != gsn_sneak
	  && sn != gsn_hide && sn != gsn_spacecraft && sn != gsn_disarm
	  && sn != gsn_spacecombat && sn != gsn_weaponsystems )
      {
	act( AT_TELL,
	    "$n needs to spend time learning and refining jedi abilities instead.",
	    victim, NULL, ch, TO_VICT );
	return;
      }

    if( character_skill_level( ch, sn ) < 100 )
    {
      act( AT_TELL,
	  "You must perfect that yourself before teaching others.",
	  victim, NULL, ch, TO_VICT );
      return;
    }
    else
    {
      if( character_skill_level( victim, sn ) <= 0 )
	victim->pcdata->num_skills++;
      modify_skill_level( victim, sn, int_app[get_curr_int( ch )].learn );
      snprintf( buf, MAX_STRING_LENGTH, "You teach %s $T.", victim->name );
      act( AT_ACTION, buf, ch, NULL, skill_table[sn]->name, TO_CHAR );
      snprintf( buf, MAX_STRING_LENGTH, "%s teaches you $T.", ch->name );
      act( AT_ACTION, buf, victim, NULL, skill_table[sn]->name, TO_CHAR );
    }
  }
}

void do_wimpy( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];
  int wimpy = 0;

  one_argument( argument, arg );

  if( arg[0] == '\0' )
    wimpy = ( int ) ch->max_hit / 5;
  else
    wimpy = atoi( arg );

  if( wimpy < 0 )
  {
    send_to_char( "Your courage exceeds your wisdom.\r\n", ch );
    return;
  }

  if( wimpy > ch->max_hit )
  {
    send_to_char( "Such cowardice ill becomes you.\r\n", ch );
    return;
  }

  ch->wimpy = wimpy;
  ch_printf( ch, "Wimpy set to %d percent.\r\n", wimpy );
}

void do_password( CHAR_DATA * ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char *pArg = arg1;
  char *pwdnew = NULL;
  char *p = NULL;
  char cEnd = ' ';

  if( IS_NPC( ch ) )
    return;

  /*
   * Can't use one_argument here because it smashes case.
   * So we just steal all its code.  Bleagh.
   */

  while( isspace( ( int ) *argument ) )
    argument++;

  if( *argument == '\'' || *argument == '"' )
    cEnd = *argument++;

  while( *argument != '\0' )
  {
    if( *argument == cEnd )
    {
      argument++;
      break;
    }
    *pArg++ = *argument++;
  }
  *pArg = '\0';

  pArg = arg2;
  while( isspace( ( int ) *argument ) )
    argument++;

  cEnd = ' ';
  if( *argument == '\'' || *argument == '"' )
    cEnd = *argument++;

  while( *argument != '\0' )
  {
    if( *argument == cEnd )
    {
      argument++;
      break;
    }
    *pArg++ = *argument++;
  }
  *pArg = '\0';

  if( arg1[0] == '\0' || arg2[0] == '\0' )
  {
    send_to_char( "Syntax: password <old> <new>.\r\n", ch );
    return;
  }

  if( strcmp( encode_string( arg1 ), ch->pcdata->pwd ) )
  {
    WAIT_STATE( ch, 40 );
    send_to_char( "Wrong password. Wait 10 seconds.\r\n", ch );
    return;
  }

  if( strlen( arg2 ) < 5 )
  {
    send_to_char( "New password must be at least five characters long.\r\n",
	ch );
    return;
  }

  /*
   * No tilde allowed because of player file format.
   */
  pwdnew = encode_string( arg2 );

  for( p = pwdnew; *p != '\0'; p++ )
  {
    if( *p == '~' )
    {
      send_to_char( "New password not acceptable, try again.\r\n", ch );
      return;
    }
  }

  DISPOSE( ch->pcdata->pwd );
  ch->pcdata->pwd = str_dup( pwdnew );

  if( IS_SET( sysdata.save_flags, SV_PASSCHG ) )
    save_char_obj( ch );

  send_to_char( "Ok.\r\n", ch );
}

void do_socials( CHAR_DATA * ch, char *argument )
{
  int iHash = 0;
  int col = 0;
  SOCIALTYPE *social = NULL;

  set_pager_color( AT_PLAIN, ch );
  for( iHash = 0; iHash < 27; iHash++ )
    for( social = social_index[iHash]; social; social = social->next )
    {
      pager_printf( ch, "%-12s", social->name );
      if( ++col % 6 == 0 )
	send_to_pager( "\r\n", ch );
    }

  if( col % 6 != 0 )
    send_to_pager( "\r\n", ch );
}

void do_commands( CHAR_DATA * ch, char *argument )
{
  int col = 0;
  bool found = FALSE;
  int hash = 0;
  CMDTYPE *command = NULL;

  set_pager_color( AT_PLAIN, ch );

  if( argument[0] == '\0' )
  {
    for( hash = 0; hash < 126; hash++ )
      for( command = command_hash[hash]; command; command = command->next )
	if( command->level <= get_trust( ch )
	    && ( command->name[0] != 'm' && command->name[1] != 'p' ) )
	{
	  pager_printf( ch, "%-12s", command->name );
	  if( ++col % 6 == 0 )
	    send_to_pager( "\r\n", ch );
	}
    if( col % 6 != 0 )
      send_to_pager( "\r\n", ch );
  }
  else
  {
    found = FALSE;
    for( hash = 0; hash < 126; hash++ )
      for( command = command_hash[hash]; command; command = command->next )
	if( command->level <= get_trust( ch )
	    && !str_prefix( argument, command->name )
	    && ( command->name[0] != 'm' && command->name[1] != 'p' ) )
	{
	  pager_printf( ch, "%-12s", command->name );
	  found = TRUE;
	  if( ++col % 6 == 0 )
	    send_to_pager( "\r\n", ch );
	}

    if( col % 6 != 0 )
      send_to_pager( "\r\n", ch );
    if( !found )
      ch_printf( ch, "No command found under %s.\r\n", argument );
  }
}

void do_channels( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];

  one_argument( argument, arg );

  if( arg[0] == '\0' )
  {
    if( !IS_NPC( ch ) && IS_SET( ch->act, PLR_SILENCE ) )
    {
      send_to_char( "You are silenced.\r\n", ch );
      return;
    }

    send_to_char( "Channels:", ch );

    if( get_trust( ch ) > 2 && !NOT_AUTHED( ch ) )
    {
      send_to_char( !IS_SET( ch->deaf, CHANNEL_AUCTION )
	  ? " +AUCTION" : " -auction", ch );
    }

    send_to_char( !IS_SET( ch->deaf, CHANNEL_OOC )
	? " +OOC" : " -ooc", ch );

    send_to_char( !IS_SET( ch->deaf, CHANNEL_PNET )
	? " +pnet" : " -pnet", ch );

    send_to_char( !IS_SET( ch->deaf, CHANNEL_GNET )
	? " +gnet" : " -gnet", ch );

    if( !IS_NPC( ch ) && ch->pcdata->clan )
    {
      send_to_char( !IS_SET( ch->deaf, CHANNEL_CLAN )
	  ? " +CLAN" : " -clan", ch );
    }

    send_to_char( !IS_SET( ch->deaf, CHANNEL_TELLS )
	? " +TELLS" : " -tells", ch );

    if( IS_IMMORTAL( ch ) )
    {
      send_to_char( !IS_SET( ch->deaf, CHANNEL_IMMTALK )
	  ? " +IMMTALK" : " -immtalk", ch );
    }

    send_to_char( !IS_SET( ch->deaf, CHANNEL_YELL )
	? " +YELL" : " -yell", ch );

    if( IS_IMMORTAL( ch ) )
    {
      send_to_char( !IS_SET( ch->deaf, CHANNEL_MONITOR )
	  ? " +MONITOR" : " -monitor", ch );
    }

    send_to_char( !IS_SET( ch->deaf, CHANNEL_NEWBIE )
	? " +NEWBIE" : " -newbie", ch );

    if( IS_IMMORTAL( ch ) )
    {
      send_to_char( !IS_SET( ch->deaf, CHANNEL_LOG )
	  ? " +LOG" : " -log", ch );

      send_to_char( !IS_SET( ch->deaf, CHANNEL_BUILD )
	  ? " +BUILD" : " -build", ch );

      send_to_char( !IS_SET( ch->deaf, CHANNEL_COMM )
	  ? " +COMM" : " -comm", ch );
    }
    send_to_char( ".\r\n", ch );
  }
  else
  {
    bool fClear = FALSE;
    bool ClearAll = FALSE;
    int bit = 0;

    if( arg[0] == '+' )
      fClear = TRUE;
    else if( arg[0] == '-' )
      fClear = FALSE;
    else
    {
      send_to_char( "Channels -channel or +channel?\r\n", ch );
      return;
    }

    if( !str_cmp( arg + 1, "auction" ) )
      bit = CHANNEL_AUCTION;
    else if( !str_cmp( arg + 1, "ooc" ) )
      bit = CHANNEL_OOC;
    else if( !str_cmp( arg + 1, "clan" ) )
      bit = CHANNEL_CLAN;
    else if( !str_cmp( arg + 1, "tells" ) )
      bit = CHANNEL_TELLS;
    else if( !str_cmp( arg + 1, "immtalk" ) )
      bit = CHANNEL_IMMTALK;
    else if( !str_cmp( arg + 1, "log" ) )
      bit = CHANNEL_LOG;
    else if( !str_cmp( arg + 1, "build" ) )
      bit = CHANNEL_BUILD;
    else if( !str_cmp( arg + 1, "monitor" ) )
      bit = CHANNEL_MONITOR;
    else if( !str_cmp( arg + 1, "newbie" ) )
      bit = CHANNEL_NEWBIE;
    else if( !str_cmp( arg + 1, "yell" ) )
      bit = CHANNEL_YELL;
    else if( !str_cmp( arg + 1, "comm" ) )
      bit = CHANNEL_COMM;
    else if( !str_cmp( arg + 1, "pnet" ) )
      bit = CHANNEL_PNET;
    else if( !str_cmp( arg + 1, "gnet" ) )
      bit = CHANNEL_GNET;
    else if( !str_cmp( arg + 1, "all" ) )
      ClearAll = TRUE;
    else
    {
      send_to_char( "Set or clear which channel?\r\n", ch );
      return;
    }

    if( ( fClear ) && ( ClearAll ) )
    {
      REMOVE_BIT( ch->deaf, CHANNEL_AUCTION );
      REMOVE_BIT( ch->deaf, CHANNEL_YELL );

      if( IS_IMMORTAL( ch ) )
	REMOVE_BIT( ch->deaf, CHANNEL_COMM );

    }
    else if( ( !fClear ) && ( ClearAll ) )
    {
      SET_BIT( ch->deaf, CHANNEL_AUCTION );
      SET_BIT( ch->deaf, CHANNEL_YELL );

      if( IS_IMMORTAL( ch ) )
	SET_BIT( ch->deaf, CHANNEL_COMM );

    }
    else if( fClear )
    {
      REMOVE_BIT( ch->deaf, bit );
    }
    else
    {
      SET_BIT( ch->deaf, bit );
    }

    send_to_char( "Ok.\r\n", ch );
  }
}

void do_wizlist( CHAR_DATA * ch, char *argument )
{
  ch_printf( ch, "\r\n" );
  ch_printf( ch, "&WPlanets: The Quest For Galactic Domination\r\n" );
  ch_printf( ch, "\r\n" );
  ch_printf( ch, "  SWR 2.0-alpha2 by\r\n" );
  ch_printf( ch, "  Sean Cooper (aka Durga)\r\n" );
  ch_printf( ch, "  specs@golden.net\r\n" );
  ch_printf( ch, "\r\n" );
  ch_printf( ch, "Elected Officials:\r\n" );
  ch_printf( ch, "\r\n" );
  ch_printf( ch, "  %s\r\n", sysdata.officials );
  ch_printf( ch, "\r\n" );
}

/*
 * Contributed by Grodyn.
 */
void do_config( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];

  if( IS_NPC( ch ) )
    return;

  one_argument( argument, arg );

  set_char_color( AT_WHITE, ch );
  if( arg[0] == '\0' )
  {
    send_to_char( "[ Keyword  ] Option\r\n", ch );

    send_to_char( IS_SET( ch->act, PLR_FLEE )
	? "[+FLEE     ] You flee if you get attacked.\r\n"
	: "[-flee     ] You fight back if you get attacked.\r\n",
	ch );

    send_to_char( IS_SET( ch->pcdata->flags, PCFLAG_NORECALL )
	?
	"[+NORECALL ] You fight to the death, link-dead or not.\r\n"
	:
	"[-norecall ] You try to recall if fighting link-dead.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_AUTOEXIT )
	? "[+AUTOEXIT ] You automatically see exits.\r\n"
	: "[-autoexit ] You don't automatically see exits.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_AUTOLOOT )
	? "[+AUTOLOOT ] You automatically loot corpses.\r\n"
	:
	"[-autoloot ] You don't automatically loot corpses.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_AUTOSAC )
	? "[+AUTOSAC  ] You automatically sacrifice corpses.\r\n"
	:
	"[-autosac  ] You don't automatically sacrifice corpses.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_AUTOGOLD )
	?
	"[+AUTOCRED ] You automatically split credits from kills in groups.\r\n"
	:
	"[-autocred ] You don't automatically split credits from kills in groups.\r\n",
	ch );

    send_to_char( IS_SET( ch->pcdata->flags, PCFLAG_GAG )
	? "[+GAG      ] You see only necessary battle text.\r\n"
	: "[-gag      ] You see full battle text.\r\n", ch );

    send_to_char( IS_SET( ch->pcdata->flags, PCFLAG_PAGERON )
	? "[+PAGER    ] Long output is page-paused.\r\n"
	: "[-pager    ] Long output scrolls to the end.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_BLANK )
	?
	"[+BLANK    ] You have a blank line before your prompt.\r\n"
	:
	"[-blank    ] You have no blank line before your prompt.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_BRIEF )
	? "[+BRIEF    ] You see brief descriptions.\r\n"
	: "[-brief    ] You see long descriptions.\r\n", ch );

    send_to_char( IS_SET( ch->act, PLR_COMBINE )
	?
	"[+COMBINE  ] You see object lists in combined format.\r\n"
	:
	"[-combine  ] You see object lists in single format.\r\n",
	ch );

    send_to_char( IS_SET( ch->pcdata->flags, PCFLAG_NOINTRO )
	?
	"[+NOINTRO  ] You don't see the ascii intro screen on login.\r\n"
	:
	"[-nointro  ] You see the ascii intro screen on login.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_PROMPT )
	? "[+PROMPT   ] You have a prompt.\r\n"
	: "[-prompt   ] You don't have a prompt.\r\n", ch );

    send_to_char( IS_SET( ch->act, PLR_TELNET_GA )
	? "[+TELNETGA ] You receive a telnet GA sequence.\r\n"
	:
	"[-telnetga ] You don't receive a telnet GA sequence.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_ANSI )
	? "[+ANSI     ] You receive ANSI color sequences.\r\n"
	:
	"[-ansi     ] You don't receive receive ANSI colors.\r\n",
	ch );

    send_to_char( IS_SET( ch->act, PLR_SOUND )
	? "[+SOUND     ] You have MSP support.\r\n"
	: "[-sound     ] You don't have MSP support.\r\n", ch );


    send_to_char( IS_SET( ch->act, PLR_SHOVEDRAG )
	?
	"[+SHOVEDRAG] You allow yourself to be shoved and dragged around.\r\n"
	:
	"[-shovedrag] You'd rather not be shoved or dragged around.\r\n",
	ch );

    send_to_char( IS_SET( ch->pcdata->flags, PCFLAG_NOSUMMON )
	?
	"[+NOSUMMON ] You do not allow other players to summon you.\r\n"
	:
	"[-nosummon ] You allow other players to summon you.\r\n",
	ch );

    if( IS_IMMORTAL( ch ) )
      send_to_char( IS_SET( ch->act, PLR_ROOMVNUM )
	  ? "[+VNUM     ] You can see the VNUM of a room.\r\n"
	  : "[-vnum     ] You do not see the VNUM of a room.\r\n",
	  ch );

    if( IS_IMMORTAL( ch ) )
      send_to_char( IS_SET( ch->act, PLR_AUTOMAP )	/* maps */
	  ? "[+MAP      ] You can see the MAP of a room.\r\n"
	  : "[-map      ] You do not see the MAP of a room.\r\n",
	  ch );

    if( IS_IMMORTAL( ch ) )	/* Added 10/16 by Kuran of SWR */
      send_to_char( IS_SET( ch->pcdata->flags, PCFLAG_ROOM )
	  ? "[+ROOMFLAGS] You will see room flags.\r\n"
	  : "[-roomflags] You will not see room flags.\r\n", ch );

    send_to_char( IS_SET( ch->act, PLR_SILENCE )
	? "[+SILENCE  ] You are silenced.\r\n" : "", ch );

    send_to_char( !IS_SET( ch->act, PLR_NO_EMOTE )
	? "" : "[-emote    ] You can't emote.\r\n", ch );

    send_to_char( !IS_SET( ch->act, PLR_NO_TELL )
	? "" : "[-tell     ] You can't use 'tell'.\r\n", ch );

    send_to_char( !IS_SET( ch->act, PLR_LITTERBUG )
	? ""
	:
	"[-litter  ] A convicted litterbug. You cannot drop anything.\r\n",
	ch );
  }
  else
  {
    bool fSet = FALSE;
    int bit = 0;

    if( arg[0] == '+' )
      fSet = TRUE;
    else if( arg[0] == '-' )
      fSet = FALSE;
    else
    {
      send_to_char( "Config -option or +option?\r\n", ch );
      return;
    }

    if( !str_prefix( arg + 1, "autoexit" ) )
      bit = PLR_AUTOEXIT;
    else if( !str_prefix( arg + 1, "autoloot" ) )
      bit = PLR_AUTOLOOT;
    else if( !str_prefix( arg + 1, "autosac" ) )
      bit = PLR_AUTOSAC;
    else if( !str_prefix( arg + 1, "autocred" ) )
      bit = PLR_AUTOGOLD;
    else if( !str_prefix( arg + 1, "blank" ) )
      bit = PLR_BLANK;
    else if( !str_prefix( arg + 1, "brief" ) )
      bit = PLR_BRIEF;
    else if( !str_prefix( arg + 1, "combine" ) )
      bit = PLR_COMBINE;
    else if( !str_prefix( arg + 1, "prompt" ) )
      bit = PLR_PROMPT;
    else if( !str_prefix( arg + 1, "telnetga" ) )
      bit = PLR_TELNET_GA;
    else if( !str_prefix( arg + 1, "ansi" ) )
      bit = PLR_ANSI;
    else if( !str_prefix( arg + 1, "sound" ) )
      bit = PLR_SOUND;
    else if( !str_prefix( arg + 1, "flee" ) )
      bit = PLR_FLEE;
    else if( !str_prefix( arg + 1, "nice" ) )
      bit = PLR_NICE;
    else if( !str_prefix( arg + 1, "shovedrag" ) )
      bit = PLR_SHOVEDRAG;
    else if( IS_IMMORTAL( ch ) && !str_prefix( arg + 1, "vnum" ) )
      bit = PLR_ROOMVNUM;
    else if( IS_IMMORTAL( ch ) && !str_prefix( arg + 1, "map" ) )
      bit = PLR_AUTOMAP;	/* maps */

    if( bit )
    {
      if( fSet )
	SET_BIT( ch->act, bit );
      else
	REMOVE_BIT( ch->act, bit );
      send_to_char( "Ok.\r\n", ch );
      return;
    }
    else
    {
      if( !str_prefix( arg + 1, "norecall" ) )
	bit = PCFLAG_NORECALL;
      else if( !str_prefix( arg + 1, "nointro" ) )
	bit = PCFLAG_NOINTRO;
      else if( !str_prefix( arg + 1, "nosummon" ) )
	bit = PCFLAG_NOSUMMON;
      else if( !str_prefix( arg + 1, "gag" ) )
	bit = PCFLAG_GAG;
      else if( !str_prefix( arg + 1, "pager" ) )
	bit = PCFLAG_PAGERON;
      else if( !str_prefix( arg + 1, "roomflags" )
	  && ( IS_IMMORTAL( ch ) ) )
	bit = PCFLAG_ROOM;
      else
      {
	send_to_char( "Config which option?\r\n", ch );
	return;
      }

      if( fSet )
	SET_BIT( ch->pcdata->flags, bit );
      else
	REMOVE_BIT( ch->pcdata->flags, bit );

      send_to_char( "Ok.\r\n", ch );
      return;
    }
  }
}

void do_credits( CHAR_DATA * ch, char *argument )
{
  char credits[MAX_INPUT_LENGTH];
  snprintf( credits, MAX_INPUT_LENGTH, "%s", "credits" );
  do_help( ch, credits );
}

extern int top_area;

/* 
 * New do_areas with soft/hard level ranges 
 */

void do_areas( CHAR_DATA * ch, char *argument )
{
  set_pager_color( AT_PLAIN, ch );
  pager_printf( ch, "AREAS\r\n\r\n" );
  pager_printf( ch,
      "All areas are collectively built and modified by the\r\norganizations that control them.\r\n" );
  pager_printf( ch, "\r\nSee PLANETS and ORGANIZATIONS.\r\n" );
}

void do_afk( CHAR_DATA * ch, char *argument )
{
  if( IS_NPC( ch ) )
    return;

  if( IS_SET( ch->act, PLR_AFK ) )
  {
    REMOVE_BIT( ch->act, PLR_AFK );
    send_to_char( "You are no longer afk.\r\n", ch );
    act( AT_GREY, "$n is no longer afk.", ch, NULL, NULL, TO_ROOM );
  }
  else
  {
    SET_BIT( ch->act, PLR_AFK );
    send_to_char( "You are now afk.\r\n", ch );
    act( AT_GREY, "$n is now afk.", ch, NULL, NULL, TO_ROOM );
    return;
  }

}

void do_slist( CHAR_DATA * ch, char *argument )
{
}

void do_whois( CHAR_DATA * ch, char *argument )
{
  CHAR_DATA *victim;
  char buf[MAX_STRING_LENGTH];
  char buf2[MAX_STRING_LENGTH];

  buf[0] = '\0';

  if( IS_NPC( ch ) )
    return;

  if( argument[0] == '\0' )
  {
    send_to_char( "You must input the name of a player online.\r\n", ch );
    return;
  }

  strcat( buf, "0." );
  strcat( buf, argument );
  if( ( ( victim = get_char_world( ch, buf ) ) == NULL ) )
  {
    send_to_char( "No such player online.\r\n", ch );
    return;
  }

  if( IS_NPC( victim ) )
  {
    send_to_char( "That's not a player!\r\n", ch );
    return;
  }

  ch_printf( ch, "%s is a %s character",
      victim->name,
      victim->sex == SEX_MALE ? "male" :
      victim->sex == SEX_FEMALE ? "female" : "neutral" );
  if( IS_IMMORTAL( ch ) )
    ch_printf( ch, " in room %d.\r\n", victim->in_room->vnum );
  else
    ch_printf( ch, ".\r\n" );

  if( victim->pcdata->clan )
  {
    send_to_char( ", and belongs to organization ", ch );
    send_to_char( victim->pcdata->clan->name, ch );
  }
  send_to_char( ".\r\n", ch );

  if( victim->pcdata->homepage && victim->pcdata->homepage[0] != '\0' )
    ch_printf( ch, "%s's homepage can be found at %s.\r\n",
	victim->name, victim->pcdata->homepage );

  if( victim->pcdata->bio && victim->pcdata->bio[0] != '\0' )
    ch_printf( ch, "%s's personal bio:\r\n%s",
	victim->name, victim->pcdata->bio );

  if( IS_IMMORTAL( ch ) )
  {
    send_to_char
      ( "----------------------------------------------------\r\n", ch );

    send_to_char( "Info for immortals:\r\n", ch );

    if( victim->pcdata->authed_by && victim->pcdata->authed_by[0] != '\0' )
      ch_printf( ch, "%s was authorized by %s.\r\n",
	  victim->name, victim->pcdata->authed_by );

    if( victim->pcdata->pkills || victim->pcdata->pdeaths )
      ch_printf( ch,
	  "%s has killed %d players, and been killed by a player %d times.\r\n",
	  victim->name, victim->pcdata->pkills,
	  victim->pcdata->pdeaths );

    ch_printf( ch, "%s is %shelled at the moment.\r\n",
	victim->name,
	( victim->pcdata->release_date == 0 ) ? "not " : "" );

    if( victim->pcdata->release_date != 0 )
      ch_printf( ch,
	  "%s was helled by %s, and will be released on %24.24s.\r\n",
	  victim->sex == SEX_MALE ? "He" : victim->sex ==
	  SEX_FEMALE ? "She" : "It", victim->pcdata->helled_by,
	  ctime( &victim->pcdata->release_date ) );

    if( get_trust( victim ) < get_trust( ch ) )
    {
      snprintf( buf2, MAX_STRING_LENGTH, "list %s", buf );
    }

    if( IS_SET( victim->act, PLR_SILENCE )
	|| IS_SET( victim->act, PLR_NO_EMOTE )
	|| IS_SET( victim->act, PLR_NO_TELL ) )
    {
      snprintf( buf2, MAX_STRING_LENGTH,
	  "This player has the following flags set:" );
      if( IS_SET( victim->act, PLR_SILENCE ) )
	strcat( buf2, " silence" );
      if( IS_SET( victim->act, PLR_NO_EMOTE ) )
	strcat( buf2, " noemote" );
      if( IS_SET( victim->act, PLR_NO_TELL ) )
	strcat( buf2, " notell" );
      strcat( buf2, ".\r\n" );
      send_to_char( buf2, ch );
    }

    if( victim->desc && victim->desc->host[0] != '\0' )	/* added by Gorog */
    {
      snprintf( buf2, MAX_STRING_LENGTH, "%s's IP info: %s ", victim->name,
	  victim->desc->host );

      if( IS_IMMORTAL( ch ) )
      {
	strcat( buf2, victim->desc->host );
      }

      strcat( buf2, "\r\n" );
      send_to_char( buf2, ch );

      snprintf( buf2, MAX_STRING_LENGTH, "Terminal type: %s  MCCP: %s\r\n",
		victim->desc->terminal_type,
		victim->desc->mccp ? "Yes" : "No" );
      send_to_char( buf2, ch );
    }

    if( IS_IMMORTAL( ch ) && get_trust( ch ) >= get_trust( victim )
	&& victim->pcdata )
    {
      snprintf( buf2, MAX_STRING_LENGTH, "Email: %s\r\n",
	  victim->pcdata->email );
      send_to_char( buf2, ch );
    }
  }
}

void do_pager( CHAR_DATA * ch, char *argument )
{
  char arg[MAX_INPUT_LENGTH];

  if( IS_NPC( ch ) )
    return;

  argument = one_argument( argument, arg );

  if( !*arg )
  {
    char pager_arg[MAX_INPUT_LENGTH];

    if( IS_SET( ch->pcdata->flags, PCFLAG_PAGERON ) )
      {
	snprintf( pager_arg, MAX_INPUT_LENGTH, "%s", "-pager" );
      }
    else
      {
	snprintf( pager_arg, MAX_INPUT_LENGTH, "%s", "+pager" );
      }

    do_config( ch, pager_arg );
    return;
  }
  if( !is_number( arg ) )
  {
    send_to_char( "Set page pausing to how many lines?\r\n", ch );
    return;
  }
  ch->pcdata->pagerlen = atoi( arg );
  if( ch->pcdata->pagerlen < 5 )
    ch->pcdata->pagerlen = 5;
  ch_printf( ch, "Page pausing set to %d lines.\r\n", ch->pcdata->pagerlen );
}

bool is_online( const char *argument )
{
  DESCRIPTOR_DATA *d = NULL;

  if( argument[0] == '\0' )
    return FALSE;

  for( d = last_descriptor; d; d = d->prev )
  {
    CHAR_DATA *wch = NULL;

    if( ( d->connected != CON_PLAYING && d->connected != CON_EDITING )
	|| d->original )
      continue;
    wch = d->original ? d->original : d->character;

    if( !str_cmp( argument, wch->name ) )
      return TRUE;
  }

  return FALSE;
}