eldhamud/boards/
eldhamud/clans/
eldhamud/classes/
eldhamud/councils/
eldhamud/deity/
eldhamud/doc/
eldhamud/doc/DIKU/
eldhamud/doc/MERC/
eldhamud/doc/mudprogs/
eldhamud/houses/
eldhamud/src/o/CVS/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			     Informational module			    *
 ****************************************************************************/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include "mud.h"

/*
* Needed in the do_ignore function. -Orion
*/
// bool    check_parse_name        args( ( char *name, bool newchar ) );

/*
 * Keep players from defeating examine progs -Druid
 * False = do not trigger
 * True = Trigger
 */
bool EXA_prog_trigger = TRUE;

/* Had to add unknowns because someone added new positions and didn't
 * update them.  Just a band-aid till I have time to fix it right.
 * This was found thanks to mud@mini.axcomp.com pointing it out :)
 * --Shaddai
 */

char *	const	where_name	[] =
  {
    "<used as light>     ",
    "<worn on finger>    ",
    "<worn on finger>    ",
    "<worn around neck>  ",
    "<worn around neck>  ",
    "<worn on body>      ",
    "<worn on head>      ",
    "<worn on legs>      ",
    "<worn on feet>      ",
    "<worn on hands>     ",
    "<worn on arms>      ",
    "<worn as shield>    ",
    "<worn about body>   ",
    "<worn about waist>  ",
    "<worn around wrist> ",
    "<worn around wrist> ",
    "<wielded>           ",
    "<held>              ",
    "<dual wielded>      ",
    "<worn on ears>      ",
    "<worn on eyes>      ",
    "<missile wielded>   ",
    "<worn on back>      ",
    "<worn over face>    ",
    "<worn around ankle> ",
    "<worn around ankle> ",
    "<BUG Inform Nivek>  ",
    "<BUG Inform Nivek>  ",
    "<BUG Inform Nivek>  "
  };


/*
 * Local functions.
 */
void	show_char_to_char_0	args( ( CHAR_DATA *victim, CHAR_DATA *ch ) );
void	show_char_to_char_1	args( ( CHAR_DATA *victim, CHAR_DATA *ch ) );
void	show_char_to_char	args( ( CHAR_DATA *list, CHAR_DATA *ch ) );
bool	check_blind		args( ( CHAR_DATA *ch ) );
void    show_condition          args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );
//Similar Helpfile Snippet Declarations
sh_int str_similarity( const char *astr, const char *bstr );
sh_int str_prefix_level( const char *astr, const char *bstr );
void similar_help_files(CHAR_DATA *ch, char *argument);



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

  if ( !IS_NPC(ch) && ch->pcdata->questobj > 0 && obj->pIndexData->vnum == ch->pcdata->questobj)
    strcat( buf, "[TARGET] "     );
  /* can see glowing invis items in the dark */
  if ( IS_OBJ_STAT(obj, ITEM_GLOW) && IS_OBJ_STAT(obj, ITEM_INVIS)
       &&  !IS_AFFECTED(ch, AFF_TRUESIGHT) && !IS_AFFECTED(ch, AFF_DETECT_INVIS) )
    glowsee = TRUE;

  buf[0] = '\0';
  if ( IS_OBJ_STAT(obj, ITEM_INVIS))
    strcat( buf, "(Invis) "     );
  if (  (IS_AFFECTED(ch, AFF_DETECT_EVIL) && IS_OBJ_STAT(obj, ITEM_EVIL)))
    strcat( buf, "(Red Aura) "  );
  if ( IS_AFFECTED(ch, AFF_DETECT_MAGIC) && IS_OBJ_STAT(obj, ITEM_MAGIC)  )
    strcat( buf, "&Y(&OMagical&Y)&D "   );
  if ( !glowsee && IS_OBJ_STAT(obj, ITEM_GLOW) )
    strcat( buf, "&Y(&WGlowing&Y)&D "   );
  if ( IS_OBJ_STAT(obj, ITEM_HUM)       )
    strcat( buf, "&Y(&pHumming&Y)&D "   );
  if ( IS_OBJ_STAT(obj, ITEM_HIDDEN)	  )
    strcat( buf, "(Hidden) "	  );
  if ( IS_OBJ_STAT(obj, ITEM_BURIED)	  )
    strcat( buf, "(Buried) "	  );
  if ( IS_IMMORTAL(ch)
       && IS_OBJ_STAT(obj, ITEM_PROTOTYPE) )
    strcat( buf, "&Y(&RPROTO&Y)&D "	  );
  if ( IS_AFFECTED(ch, AFF_DETECTTRAPS)
       && is_trapped(obj)   )
    strcat( buf, "(Trap) "  );

  if ( fShort )
  {
    if ( glowsee && !IS_IMMORTAL(ch) )
      strcat( buf, "the faint glow of something" );
    else
      if ( obj->short_descr )
        strcat( buf, obj->short_descr );
  }
  else
  {
    if ( glowsee )
      strcat( buf, "You see the faint glow of something nearby." );
    if ( obj->description )
      strcat( buf, obj->description );
  }

  return buf;
}


/*
 * Some increasingly freaky hallucinated objects		-Thoric
 * (Hats off to Albert Hoffman's "problem child")
 */
char *hallucinated_object( int ms, bool fShort )
{
  int sms = URANGE( 1, (ms+10)/5, 20 );

  if ( fShort )
    switch( number_range( 6-URANGE(1,sms/2,5), sms ) )
    {
    case  1:
      return "a sword";
    case  2:
      return "a stick";
    case  3:
      return "something shiny";
    case  4:
      return "something";
    case  5:
      return "something interesting";
    case  6:
      return "something colorful";
    case  7:
      return "something that looks cool";
    case  8:
      return "a nifty thing";
    case  9:
      return "a cloak of flowing colors";
    case 10:
      return "a mystical flaming sword";
    case 11:
      return "a swarm of insects";
    case 12:
      return "a deathbane";
    case 13:
      return "a figment of your imagination";
    case 14:
      return "your gravestone";
    case 15:
      return "the long lost boots of Ranger Thoric";
    case 16:
      return "a glowing tome of arcane knowledge";
    case 17:
      return "a long sought secret";
    case 18:
      return "the meaning of it all";
    case 19:
      return "the answer";
    case 20:
      return "the key to life, the universe and everything";
    }
  switch( number_range( 6-URANGE(1,sms/2,5), sms ) )
  {
  case  1:
    return "A nice looking sword catches your eye.";
  case  2:
    return "The ground is covered in small sticks.";
  case  3:
    return "Something shiny catches your eye.";
  case  4:
    return "Something catches your attention.";
  case  5:
    return "Something interesting catches your eye.";
  case  6:
    return "Something colorful flows by.";
  case  7:
    return "Something that looks cool calls out to you.";
  case  8:
    return "A nifty thing of great importance stands here.";
  case  9:
    return "A cloak of flowing colors asks you to wear it.";
  case 10:
    return "A mystical flaming sword awaits your grasp.";
  case 11:
    return "A swarm of insects buzzes in your face!";
  case 12:
    return "The extremely rare Deathbane lies at your feet.";
  case 13:
    return "A figment of your imagination is at your command.";
  case 14:
    return "You notice a gravestone here... upon closer examination, it reads your name.";
  case 15:
    return "The long lost boots of Ranger Thoric lie off to the side.";
  case 16:
    return "A glowing tome of arcane knowledge hovers in the air before you.";
  case 17:
    return "A long sought secret of all mankind is now clear to you.";
  case 18:
    return "The meaning of it all, so simple, so clear... of course!";
  case 19:
    return "The answer.  One.  It's always been One.";
  case 20:
    return "The key to life, the universe and everything awaits your hand.";
  }
  return "Whoa!!!";
}


/* This is the punct snippet from Desden el Chaman Tibetano - Nov 1998
   Email: jlalbatros@mx2.redestb.es
*/
char *num_punct(int foo)
{
  int nindex, index_new, rest;
  char buf[16];
  static char buf_new[16];

  sprintf(buf,"%d",foo);
  rest = strlen(buf)%3;

  for (nindex=index_new=0;nindex<strlen(buf);nindex++,index_new++)
  {
    if (nindex!=0 && (nindex-rest)%3==0 )
    {
      buf_new[index_new]=',';
      index_new++;
      buf_new[index_new]=buf[nindex];
    }
    else
      buf_new[index_new] = buf[nindex];
  }
  buf_new[index_new]='\0';
  return buf_new;
}


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

  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) || xIS_SET(ch->act, PLR_COMBINE) )
        send_to_char( "     ", ch );
      set_char_color( AT_OBJECT, ch );
      send_to_char( "Nothing.\n\r", 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) || xIS_SET(ch->act, PLR_COMBINE) )
        send_to_char( "     ", ch );
      set_char_color( AT_OBJECT, ch );
      send_to_char( "Nothing.\n\r", 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( hallucinated_object(ms, fShort) );
      prgnShow	[nShow] = 1;
      pitShow	[nShow] = number_range( ITEM_LIGHT, ITEM_BOOK );
      nShow++;
      --tmp;
    }
    if ( obj->wear_loc == WEAR_NONE
         && can_see_obj( ch, obj )
         && (obj->item_type != ITEM_TRAP || IS_AFFECTED(ch, AFF_DETECTTRAPS) ) )
    {
      pstrShow = format_obj_to_char( obj, ch, fShort );
      fCombine = FALSE;

      if ( IS_NPC(ch) || xIS_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;
    for ( x = 0; x < tmp; x++ )
    {
      prgpstrShow [nShow] = str_dup( hallucinated_object(ms, fShort) );
      prgnShow	[nShow] = 1;
      pitShow	[nShow] = number_range( ITEM_LIGHT, ITEM_BOOK );
      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_BLOOD:
      set_char_color( AT_BLOOD, ch );
      break;
    case ITEM_MONEY:
    case ITEM_TREASURE:
      set_char_color( AT_YELLOW, ch );
      break;
    case ITEM_COOK:
    case ITEM_FOOD:
      set_char_color( AT_HUNGRY, ch );
      break;
    case ITEM_DRINK_CON:
    case ITEM_FOUNTAIN:
      set_char_color( AT_THIRSTY, ch );
      break;
    case ITEM_FIRE:
      set_char_color( AT_FIRE, ch );
      break;
    case ITEM_SCROLL:
    case ITEM_WAND:
    case ITEM_STAFF:
      set_char_color( AT_MAGIC, ch );
      break;
    }
    if ( fShowNothing )
      send_to_char( "     ", ch );
    send_to_char( prgpstrShow[iShow], ch );
    /*	if ( IS_NPC(ch) || xIS_SET(ch->act, PLR_COMBINE) ) */
    {
      if ( prgnShow[iShow] != 1 )
        ch_printf( ch, " (%d)", prgnShow[iShow] );
    }

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

  if ( fShowNothing && nShow == 0 )
  {
    if ( IS_NPC(ch) || xIS_SET(ch->act, PLR_COMBINE) )
      send_to_char( "     ", ch );
    set_char_color( AT_OBJECT, ch );
    send_to_char( "Nothing.\n\r", ch );
  }

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


/*
 * Show fancy descriptions for certain spell affects		-Thoric
 */
void show_visible_affects_to_char( CHAR_DATA *victim, CHAR_DATA *ch )
{
  char buf[MAX_STRING_LENGTH];
  char name[MAX_STRING_LENGTH];

  if ( IS_NPC( victim ) )
    strcpy( name, victim->short_descr );
  else
    strcpy( name, victim->name);
  name[0] = toupper(name[0]);

  if ( IS_AFFECTED(victim, AFF_SANCTUARY) )
  {
    set_char_color( AT_WHITE, ch );
    if ( IS_GOOD(victim) )
      ch_printf( ch, "%s glows with an aura of divine radiance.\n\r", name );
    else if ( IS_EVIL(victim) )
      ch_printf( ch, "%s shimmers beneath an aura of dark energy.\n\r", name );
    else
      ch_printf( ch, "%s is shrouded in flowing shadow and light.\n\r", 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.\n\r", name );
  }
  if ( IS_AFFECTED(victim, AFF_SHOCKSHIELD) )
  {
    set_char_color( AT_BLUE, ch );
    ch_printf( ch, "%s is surrounded by cascading torrents of energy.\n\r", name );
  }
  if ( IS_AFFECTED(victim, AFF_ACIDMIST) )
  {
    set_char_color( AT_GREEN, ch );
    ch_printf( ch, "%s is visible through a cloud of churning mist.\n\r", 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.\n\r", name );
  }
  if ( IS_AFFECTED(victim, AFF_CHARM)       )
  {
    set_char_color( AT_MAGIC, ch );
    ch_printf( ch, "%s wanders in a dazed, zombie-like state.\n\r", name );
  }
  if ( !IS_NPC(victim) && !victim->desc
       &&    victim->switched && IS_AFFECTED(victim->switched, AFF_POSSESS) )
  {
    set_char_color( AT_MAGIC, ch );
    strcpy( buf, PERS( victim, ch, FALSE ) );
    strcat( buf, " appears to be in a deep trance...\n\r" );
  }
}

void show_char_to_char_0( CHAR_DATA *victim, CHAR_DATA *ch )
{
  char buf[MAX_STRING_LENGTH];
  char buf1[MAX_STRING_LENGTH];
  char message[MAX_STRING_LENGTH];

  buf[0] = '\0';

  set_char_color( AT_PERSON, ch );
  if ( !IS_NPC(victim) && !victim->desc )
  {
    if ( !victim->switched )
      send_to_char_color( "&P[(Link Dead)] ", ch );
    else if ( !IS_AFFECTED(victim, AFF_POSSESS) )
      strcat( buf, "(Switched) " );
  }
  if ( IS_NPC(victim) && IS_AFFECTED(victim, AFF_POSSESS) && IS_IMMORTAL(ch)
       && victim->desc )
  {
    sprintf( buf1, "(%s)",victim->desc->original->name );
    strcat( buf, buf1 );
  }
  if ( !IS_NPC(victim)
       &&   xIS_SET(victim->act, PLR_AFK) )
    strcat( buf, "[AFK] ");

  if ( (!IS_NPC(victim) && xIS_SET(victim->act, PLR_WIZINVIS))
       || (IS_NPC(victim) && xIS_SET(victim->act, ACT_MOBINVIS)) )
  {
    if (!IS_NPC(victim))
      sprintf( buf1,"(Invis %d) ", victim->pcdata->wizinvis );
    else
      sprintf( buf1,"(Mobinvis %d) ", victim->mobinvis);
    strcat( buf, buf1 );
  }

  if (IS_NPC(victim) && !IS_NPC(ch) && ch->pcdata->questmob > 0 && victim->pIndexData->vnum == ch->pcdata->questmob)
    strcat( buf, "[TARGET] ");

  if ( !IS_NPC( victim) )
  {
    if ( IS_IMMORTAL( victim ) && victim->level > 50 )
      send_to_char_color( "&P(&WImmortal&P) ", ch );
    if ( victim->pcdata->clan
         &&   IS_SET( victim->pcdata->flags, PCFLAG_DEADLY )
         &&   victim->pcdata->clan->badge
         && ( victim->pcdata->clan->clan_type != CLAN_ORDER
              &&   victim->pcdata->clan->clan_type != CLAN_GUILD ) )
      ch_printf_color( ch, "%s ", victim->pcdata->clan->badge );
    else if ( CAN_PKILL( victim ) && victim->level < 51 )
      send_to_char_color( "&P(&wUnclanned&P) ", ch );
  }

  set_char_color( AT_PERSON, ch );

  if ( IS_AFFECTED(victim, AFF_INVISIBLE)   )
    strcat( buf, "(Invis) "      );
  if ( IS_AFFECTED(victim, AFF_HIDE)        )
    strcat( buf, "(Hide) "       );
  if ( IS_AFFECTED(victim, AFF_PASS_DOOR)   )
    strcat( buf, "(Translucent) ");
  if ( IS_AFFECTED(victim, AFF_FAERIE_FIRE) )
    strcat( buf, "(Pink Aura) "  );
  if ( IS_EVIL(victim)  &&   (  IS_AFFECTED(ch, AFF_DETECT_EVIL) ))
    strcat( buf, "(Red Aura) "   );
  if ( IS_NEUTRAL(victim) && (  IS_AFFECTED(ch, AFF_DETECT_EVIL)  ))
    strcat( buf, "(Grey Aura) "   );
  if ( IS_GOOD(victim) &&   (  IS_AFFECTED(ch, AFF_DETECT_EVIL)  ))
    strcat( buf, "(White Aura) "   );


  if ( IS_AFFECTED(victim, AFF_BERSERK)     )
    strcat( buf, "(B) "  );
  if ( !IS_NPC(victim) && xIS_SET(victim->act, PLR_ATTACKER ) )
    strcat( buf, "(A) "   );
  if ( !IS_NPC(victim) && xIS_SET(victim->act, PLR_KILLER ) )
    strcat( buf, "(K) "     );
  if ( !IS_NPC(victim) && xIS_SET(victim->act, PLR_THIEF  ) )
    strcat( buf, "(T) "      );
  if ( !IS_NPC(victim) && xIS_SET(victim->act, PLR_LITTERBUG  ) )
    strcat( buf, "(L) "  );
  if ( IS_NPC(victim) && IS_IMMORTAL(ch)
       && xIS_SET(victim->act, ACT_PROTOTYPE) )
    strcat( buf, "(P) "      );
  if ( IS_NPC(victim) && ch->mount && ch->mount == victim
       && ch->in_room == ch->mount->in_room )
    strcat( buf, "(Mount) "      );
  if ( victim->desc && victim->desc->connected == CON_EDITING )
    strcat( buf, "(W) "    );
  if ( victim->morph != NULL )
    strcat (buf, "(Morphed) ");

  set_char_color( AT_PERSON, ch );
  if ((victim->position == victim->defposition && victim->long_descr[0] != '\0')
      || ( victim->morph && victim->morph->morph &&
           victim->morph->morph->defpos == victim->position ) )
  {
    if ( victim->morph != NULL )
    {
      if ( !IS_IMMORTAL(ch) )
      {
        if ( victim->morph->morph != NULL)
          strcat ( buf, victim->morph->morph->long_desc );
        else
          strcat ( buf, victim->long_descr );
      }
      else
      {
        strcat (buf, PERS(victim, ch, FALSE) );
        if ( !IS_NPC(victim) && !xIS_SET(ch->act, PLR_BRIEF) )
          strcat( buf, victim->pcdata->title );
        strcat( buf, ".\n\r" );
      }
    }
    else
      strcat (buf, victim->long_descr);
    send_to_char( buf, ch );
    show_visible_affects_to_char( victim, ch );
    return;
  }
  else
  {
    if ( victim->morph != NULL && victim->morph->morph != NULL &&
         !IS_IMMORTAL( ch ) )
      strcat( buf, MORPHPERS( victim, ch, FALSE ) );
    else
      strcat( buf, PERS( victim, ch, FALSE ) );
  }

  if ( !IS_NPC(victim) && !xIS_SET(ch->act, PLR_BRIEF) )
    strcat( buf, victim->pcdata->title );

  switch ( victim->position )
  {
  case POS_DEAD:
    strcat( buf, " is DEAD!!" );
    break;
  case POS_MORTAL:
    strcat( buf, " is mortally wounded." );
    break;
  case POS_INCAP:
    strcat( buf, " is incapacitated." );
    break;
  case POS_STUNNED:
    strcat( buf, " is lying here stunned." );
    break;
    /* Furniture ideas taken from ROT
    Furniture 1.01 is provided by Xerves
    Info rewrite for sleeping/resting/standing/sitting on Objects -- Xerves */
  case POS_SLEEPING:
    if (victim->on != NULL)
    {
      if (IS_SET(victim->on->value[2],SLEEP_AT))
      {
        sprintf(message,"&P is sleeping at %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else if (IS_SET(victim->on->value[2],SLEEP_ON))
      {
        sprintf(message,"&P is sleeping on %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else
      {
        sprintf(message, "&P is sleeping in %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
    }
    else
    {
      if (ch->position == POS_SITTING
          ||  ch->position == POS_RESTING )
        strcat( buf, "&P is sleeping nearby.&G" );
      else
        strcat( buf, "&P is deep in slumber here.&G" );
    }
    break;
  case POS_RESTING:
    if (victim->on != NULL)
    {
      if (IS_SET(victim->on->value[2],REST_AT))
      {
        sprintf(message,"&P is resting at %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else if (IS_SET(victim->on->value[2],REST_ON))
      {
        sprintf(message,"&P is resting on %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else
      {
        sprintf(message, "&P is resting in %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
    }
    else
    {
      if (ch->position == POS_RESTING)
        strcat ( buf, "&P is sprawled out alongside you.&G" );
      else
        if (ch->position == POS_MOUNTED)
          strcat ( buf, "&P is sprawled out at the foot of your mount.&G" );
        else
          strcat (buf, "&P is sprawled out here.&G" );
    }
    break;
  case POS_SITTING:
    if (victim->on != NULL)
    {
      if (IS_SET(victim->on->value[2],SIT_AT))
      {
        sprintf(message,"&P is sitting at %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else if (IS_SET(victim->on->value[2],SIT_ON))
      {
        sprintf(message,"&P is sitting on %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else
      {
        sprintf(message, "&P is sitting in %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
    }
    else
      strcat(buf, "&P is sitting here.");
    break;
  case POS_STANDING:
    if (victim->on != NULL)
    {
      if (IS_SET(victim->on->value[2],STAND_AT))
      {
        sprintf(message,"&P is standing at %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else if (IS_SET(victim->on->value[2],STAND_ON))
      {
        sprintf(message,"&P is standing on %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
      else
      {
        sprintf(message, "&P is standing in %s.",
                victim->on->short_descr);
        strcat(buf,message);
      }
    }
    else
      if ( IS_IMMORTAL(victim) )
        strcat( buf, "&P is here before you.&G" );
      else
        if ( ( victim->in_room->sector_type == SECT_UNDERWATER )
             && !IS_AFFECTED(victim, AFF_AQUA_BREATH) && !IS_NPC(victim) )
          strcat( buf, "&P is drowning here.&G" );
        else
          if ( victim->in_room->sector_type == SECT_UNDERWATER )
            strcat( buf, "&P is here in the water.&G" );
          else
            if ( ( victim->in_room->sector_type == SECT_OCEANFLOOR )
                 && !IS_AFFECTED(victim, AFF_AQUA_BREATH) && !IS_NPC(victim) )
              strcat( buf, "&P is drowning here.&G" );
            else
              if ( victim->in_room->sector_type == SECT_OCEANFLOOR )
                strcat( buf, "&P is standing here in the water.&G" );
              else
                if ( IS_AFFECTED(victim, AFF_FLOATING)
                     || IS_AFFECTED(victim, AFF_FLYING) )
                  strcat( buf, "&P is hovering here.&G" );
                else
                  strcat( buf, "&P is standing here.&G" );
    break;
  case POS_SHOVE:
    strcat( buf, " is being shoved around." );
    break;
  case POS_DRAG:
    strcat( buf, " is being dragged around." );
    break;
  case POS_MOUNTED:
    strcat( buf, " is here, upon " );
    if ( !victim->mount )
      strcat( buf, "thin air???" );
    else
      if ( victim->mount == ch )
        strcat( buf, "your back." );
      else
        if ( victim->in_room == victim->mount->in_room )
        {
          strcat( buf, PERS( victim->mount, ch, FALSE ) );
          strcat( buf, "." );
        }
        else
          strcat( buf, "someone who left??" );
    break;
  case POS_FIGHTING:
  case POS_EVASIVE:
  case POS_DEFENSIVE:
  case POS_AGGRESSIVE:
  case POS_BERSERK:
    strcat( buf, " is here, fighting " );
    if ( !victim->fighting )
    {
      strcat( buf, "thin air???" );

      /* some bug somewhere.... kinda hackey fix -h */
      if(! victim->mount)
        victim->position = POS_STANDING;
      else
        victim->position = POS_MOUNTED;
    }
    else if ( who_fighting( victim ) == ch )
      strcat( buf, "YOU!" );
    else if ( victim->in_room == victim->fighting->who->in_room )
    {
      strcat( buf, PERS( victim->fighting->who, ch, FALSE ) );
      strcat( buf, "." );
    }
    else
      strcat( buf, "someone who left??" );
    break;
  }

  strcat( buf, "\n\r" );
  buf[0] = UPPER(buf[0]);
  send_to_char( buf, ch );
  show_visible_affects_to_char( victim, ch );
  return;
}



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

  if ( can_see( victim, ch, FALSE ) && !IS_NPC( ch ) && !xIS_SET( ch->act, PLR_WIZINVIS ) )
  {
    act( AT_ACTION, "$n looks at you.", ch, NULL, victim, TO_VICT    );
    if ( victim != ch )
      act( AT_ACTION, "$n looks at $N.",  ch, NULL, victim, TO_NOTVICT );
    else
      act( AT_ACTION, "$n looks at $mself.", ch, NULL, victim, TO_NOTVICT );
  }

  if ( victim->description[0] != '\0' )
  {
    if ( victim->morph != NULL && victim->morph->morph != NULL)
      send_to_char ( victim->morph->morph->description , ch );
    else
      send_to_char (victim->description, ch);
  }
  else
  {
    if ( victim->morph != NULL && victim->morph->morph != NULL)
      send_to_char ( victim->morph->morph->description , ch );
    else if ( IS_NPC( victim ) )
      act( AT_PLAIN, "You see nothing special about $M.", ch, NULL, victim, TO_CHAR );
    else if ( ch != victim )
      act( AT_PLAIN, "$E isn't much to look at...", ch, NULL, victim, TO_CHAR );
    else
      act( AT_PLAIN, "You're not much to look at...", ch, NULL, NULL, TO_CHAR );
  }

  show_race_line( ch, victim );
  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( "\n\r", ch );
        if ( victim != ch )
          act( AT_PLAIN, "$N is using:", ch, NULL, victim, TO_CHAR );
        else
          act( AT_PLAIN, "You are using:", ch, NULL, NULL, TO_CHAR );
        found = TRUE;
      }
      if( (!IS_NPC(victim)) && (victim->race>0) && (victim->race<MAX_PC_RACE))
        send_to_char(race_table[victim->race]->where_name[iWear], ch);
      else
        send_to_char( where_name[iWear], ch );
      send_to_char( format_obj_to_char( obj, ch, TRUE ), ch );
      send_to_char( "\n\r", ch );
    }
  }

  /*
   * Crash fix here by Thoric
   */
  if ( IS_NPC(ch) || victim == ch )
    return;

  if ( IS_IMMORTAL( ch ) )
  {
    if ( IS_NPC( victim ) )
      ch_printf( ch, "\n\rMobile #%d '%s' ",
                 victim->pIndexData->vnum,
                 victim->name );
    else
      ch_printf( ch, "\n\r%s ", victim->name );
    ch_printf( ch, "is a level %d %s %s.\n\r",
               victim->level,
               IS_NPC(victim)?victim->race<MAX_NPC_RACE&&victim->race>=0?
             npc_race[victim->race]:"unknown":victim->race<MAX_PC_RACE&&
               race_table[victim->race]->race_name&&
               race_table[victim->race]->race_name[0] != '\0'?
               race_table[victim->race]->race_name:"unknown",
               IS_NPC(victim)?victim->class<MAX_NPC_CLASS&&victim->class>=0?
             npc_class[victim->class] : "unknown":victim->class<MAX_PC_CLASS&&
               class_table[victim->class]->who_name&&
               class_table[victim->class]->who_name[0] != '\0'?
               class_table[victim->class]->who_name:"unknown");
    /* Fix so it shows what is in class table
    	victim->race<MAX_NPC_RACE&&victim->race>=0?npc_race[victim->race] : "unknown",
    	victim->class<MAX_NPC_CLASS&&victim->class>=0?npc_class[victim->class] : "unknown" );
    */
  }

  if ( number_percent( ) < LEARNED(ch, gsn_peek) )
  {
    ch_printf( ch, "\n\rYou peek at %s inventory:\n\r",
               victim->sex == 1 ? "his" : victim->sex == 2 ? "her" : "its" );
    show_list_to_char( victim->first_carrying, ch, TRUE, TRUE );
    learn_from_success( ch, gsn_peek );
  }
  else
    if ( ch->pcdata->learned[gsn_peek] > 0 )
      learn_from_failure( ch, gsn_peek );

  return;
}


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

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

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

  return;
}



bool check_blind( CHAR_DATA *ch )
{
  if ( !IS_NPC(ch) && xIS_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!\n\r", ch );
    return FALSE;
  }

  return TRUE;
}

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

  if ( !str_cmp( arg, "n"  ) || !str_cmp( arg, "north"	  ) )
    door = 0;
  else if ( !str_cmp( arg, "e"  ) || !str_cmp( arg, "east"	  ) )
    door = 1;
  else if ( !str_cmp( arg, "s"  ) || !str_cmp( arg, "south"	  ) )
    door = 2;
  else if ( !str_cmp( arg, "w"  ) || !str_cmp( arg, "west"	  ) )
    door = 3;
  else if ( !str_cmp( arg, "u"  ) || !str_cmp( arg, "up"	  ) )
    door = 4;
  else if ( !str_cmp( arg, "d"  ) || !str_cmp( arg, "down"	  ) )
    door = 5;
  else if ( !str_cmp( arg, "ne" ) || !str_cmp( arg, "northeast" ) )
    door = 6;
  else if ( !str_cmp( arg, "nw" ) || !str_cmp( arg, "northwest" ) )
    door = 7;
  else if ( !str_cmp( arg, "se" ) || !str_cmp( arg, "southeast" ) )
    door = 8;
  else if ( !str_cmp( arg, "sw" ) || !str_cmp( arg, "southwest" ) )
    door = 9;
  else
    door = -1;
  return door;
}

void display_imm_toggle(CHAR_DATA *ch)
{
  /* Room flag display installed by Samson 12-10-97 */
  if( !IS_NPC(ch) && IS_IMMORTAL(ch) && IS_SET( ch->pcdata->flags, PCFLAG_AUTOFLAGS ) )
  {
    set_char_color( AT_BLUE, ch );

    ch_printf( ch, "[Area Flags: %s]\n\r", flag_string( ch->in_room->area->flags, area_flags ) );

    set_char_color( AT_BLUE, ch );


    ch_printf( ch, "[Room Flags: %s]\n\r", ext_flag_string( &ch->in_room->room_flags, r_flags ) );
  }

  /* Room Sector display written and installed by Samson 12-10-97 */
  if( !IS_NPC(ch) && IS_IMMORTAL(ch) && IS_SET( ch->pcdata->flags, PCFLAG_SECTORD ) )
  {
    set_char_color( AT_BLUE, ch );

    ch_printf( ch, "[Sector Type: %s]\n\r", sec_flags[ch->in_room->sector_type] );
  }

  /* Area name and filename display installed by Samson 12-13-97 */
  if( !IS_NPC(ch) && IS_IMMORTAL(ch) && IS_SET( ch->pcdata->flags, PCFLAG_ANAME ) )
  {
    set_char_color( AT_BLUE, ch );
    ch_printf( ch, "[Area name: %s]  ", ch->in_room->area->name );
    if ( ch->level >= LEVEL_CREATOR )
      ch_printf( ch, "[Area filename: %s]\n\r", ch->in_room->area->filename );
    else
      send_to_char( "\n\r", ch );
  }
  return;
}


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];
  char dir_n[50];
   char dir_e[50];
   char dir_s[50];
   char dir_w[50];
   char dir_u[50];
   char dir_d[50];
   char dir_ne[50];
   char dir_nw[50];
   char dir_se[50];
   char dir_sw[50];
   char dir_sm[50];
   char *exitcolor;
  EXIT_DATA *pexit;
  CHAR_DATA *victim;
  OBJ_DATA *obj;
  ROOM_INDEX_DATA *original;
  char *pdesc;
  sh_int door;
  int number, cnt;
  strncpy( dir_n, "&z-", 50 );
   strncpy( dir_e, "&z-", 50 );
   strncpy( dir_s, "&z-", 50 );
   strncpy( dir_w, "&z-", 50 );
   strncpy( dir_u, "&z-", 50 );
   strncpy( dir_d, "&z-", 50 );
   strncpy( dir_ne, "&z -", 50 );
   strncpy( dir_nw, "&z- ", 50 );
   strncpy( dir_se, "&z -", 50 );
   strncpy( dir_sw, "&z- ", 50 );
   strncpy( dir_sm, "&z-", 50 );
  if ( !ch->desc )
    return;

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

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

  if ( !check_blind( ch ) )
    return;

  if ( !IS_NPC(ch)
       &&   !xIS_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 ... \n\r", 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 );


  if ( arg1[0] == '\0' || !str_cmp( arg1, "auto" ) )
  {
    if( IS_PLR_FLAG( ch, PLR_ONMAP ) || IS_ACT_FLAG( ch, ACT_ONMAP ) )
    {
      display_map( ch );
      return;
    }
    /* 'look' or 'look auto' */
    /* 'look' or 'look auto' */
	for( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
	{
	   if( ( pexit->to_room
	      && !IS_SET (pexit->exit_info, EX_HIDDEN)
		&& !IS_SET (pexit->exit_info, EX_SECRET))
		|| (IS_SET (pexit->exit_info, EX_SECRET)
	      && !IS_SET (pexit->exit_info, EX_CLOSED)))
	   {
		exitcolor = "&Y";
		if( IS_SET( pexit->exit_info, EX_WINDOW ) )
		   exitcolor = "&C";
		if( IS_SET( pexit->exit_info, EX_SECRET ) )
		   exitcolor = "&b";
		if( IS_SET( pexit->exit_info, EX_CLOSED ) )
		   exitcolor = "&z";
		if( IS_SET( pexit->exit_info, EX_LOCKED ) )
		   exitcolor = "&R";

		if( pexit->vdir == DIR_NORTH )
		   snprintf( dir_n, 50, "%sN", exitcolor );

		if( pexit->vdir == DIR_EAST )
		   snprintf( dir_e, 50, "%sE", exitcolor );

		if( pexit->vdir == DIR_SOUTH )
		   snprintf( dir_s, 50, "%sS", exitcolor );

		if( pexit->vdir == DIR_WEST )
		   snprintf( dir_w, 50, "%sW", exitcolor );

		if( pexit->vdir == DIR_UP )
		   snprintf( dir_u, 50, "%sU", exitcolor );

		if( pexit->vdir == DIR_DOWN )
		   snprintf( dir_d, 50, "%sD", exitcolor );

		if( pexit->vdir == DIR_NORTHEAST )
		   snprintf( dir_ne, 50, "%sNE", exitcolor );

		if( pexit->vdir == DIR_NORTHWEST )
		   snprintf( dir_nw, 50, "%sNW", exitcolor );

		if( pexit->vdir == DIR_SOUTHEAST )
		   snprintf( dir_se, 50, "%sSE", exitcolor );

		if( pexit->vdir == DIR_SOUTHWEST )
		   snprintf( dir_sw, 50, "%sSW", exitcolor );
	   }
	}

	if( xIS_SET( ch->act, PLR_COMPASS ) )
	{
	   send_to_char( "\n\r", ch );

	   set_char_color( AT_RMNAME, ch );
	   ch_printf( ch, "%-50s", ch->in_room->name );
 
	   strncat( dir_nw, "  ", 50 );
	   ch_printf_color( ch, "         %s", dir_nw );
	   strncat( dir_n, "  ", 50 );
	   ch_printf_color( ch, "  %s", dir_n );
	   ch_printf_color( ch, "  %s\n\r", dir_ne );
           send_to_char( "&z-<----------------------------------------------->-     ", ch );
	   strncat( dir_w, "", 50 );
	   ch_printf_color( ch, "   %s", dir_w );
	   send_to_char( "&z<-", ch );
	   strncat( dir_u, "&z-(&Y&W*&z)", 50 );
	   ch_printf_color( ch, "%s", dir_u );
	   strncat( dir_d, "&z-", 50 );
	   send_to_char( "&z-", ch );
	   ch_printf_color( ch, "%s", dir_d );
	   send_to_char( "&z>", ch );
	   ch_printf_color( ch, "%s\n\r", dir_e );
	   send_to_char( "                                                        ", ch );
	   strncat( dir_sw, "  ", 50 );
	   ch_printf_color( ch, "   %s", dir_sw );
	   strncat( dir_s, "  ", 50 );
	   ch_printf_color( ch, "  %s", dir_s );
	   ch_printf_color( ch, "  %s\n\r", dir_se );	   
	}
      else
      {
 	   set_char_color( AT_RMNAME, ch );
	   send_to_char( ch->in_room->name, ch );
	   send_to_char( "\n\r", ch );
	   	}
    if ( !IS_NPC(ch) && xIS_SET(ch->act, PLR_AUTOMAP) )   /* maps */
    {
      draw_map( ch, roomdesc( ch ) );
      return;
    }
    
    if ( !IS_NPC(ch) && !xIS_SET(ch->act, PLR_BRIEF) )
    {
      set_char_color( AT_RMDESC, ch );
      send_to_char( ch->in_room->description, ch );
    }
    if ( !IS_NPC(ch) && xIS_SET(ch->act, PLR_AUTOEXIT) )
      do_exits( ch, "auto" );
    display_imm_toggle(ch);
    show_list_to_char( ch->in_room->first_content, ch, FALSE, FALSE );
    show_char_to_char( ch->in_room->first_person,  ch );
    return;
  }
  if ( !str_cmp( arg1, "under" ) )
  {
    int count;

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

    if ( ( obj = get_obj_here( ch, arg2 ) ) == NULL )
    {
      send_to_char( "You do not see that here.\n\r", ch );
      return;
    }
    if ( !CAN_WEAR( obj, ITEM_TAKE ) && ch->level < sysdata.level_getobjnotake )
    {
      send_to_char( "You can't seem to get a grip on it.\n\r", ch );
      return;
    }
    if ( ch->carry_weight + obj->weight > can_carry_w( ch ) )
    {
      send_to_char( "It's too heavy for you to look under.\n\r", 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.\n\r", ch );
    if( EXA_prog_trigger )
      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?\n\r", ch );
      return;
    }

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

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

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

      /* exam addition for the new liquidtable	-Nopey */
      {
        if (obj->value[2] > 17)
          obj->value[2] = 17;

        LIQ_TABLE *liq = get_liq_vnum(obj->value[2]);

        ch_printf(ch, "It's %s full of a %s liquid.",
                  obj->value[1] <	obj->value[0] / 4 ? "less than" :
                  obj->value[1] < 3 * obj->value[0] / 4 ? "about"	: "more than",
                  liq->color );
      }


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

    case ITEM_PORTAL:
      for ( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
      {
        if ( pexit->vdir == DIR_PORTAL
             &&   IS_SET(pexit->exit_info, EX_PORTAL) )
        {
          if ( room_is_private( pexit->to_room ) && ch->level < sysdata.level_override_private )
          {
            set_char_color( AT_WHITE, ch );
            send_to_char( "The room ahead is private!\n\r", ch );
            return;
          }

          if( IS_EXIT_FLAG( pexit, EX_OVERLAND ) )
          {
            original = ch->in_room;
            enter_map( ch, pexit->x, pexit->y, -1 );
            leave_map( ch, NULL, original );
          }
          else
          {
            original = ch->in_room;
            char_from_room( ch );
            char_to_room( ch, pexit->to_room );
            do_look( ch, "auto" );
            char_from_room( ch );
            char_to_room( ch, original );
          }
          return;
        }
      }
      send_to_char( "You see swirling chaos...\n\r", ch );
      break;
    case ITEM_CONTAINER:
    case ITEM_QUIVER:
    case ITEM_CORPSE_NPC:
    case ITEM_CORPSE_PC:
      if ( IS_SET(obj->value[1], CONT_CLOSED) )
      {
        send_to_char( "It is closed.\n\r", ch );
        break;
      }

    case ITEM_KEYRING:
      count = obj->count;
      obj->count = 1;
      if ( obj->item_type == ITEM_CONTAINER )
        act( AT_PLAIN, "$p contains:", ch, obj, NULL, TO_CHAR );
      else
        act( AT_PLAIN, "$p holds:", ch, obj, NULL, TO_CHAR );
      obj->count = count;
      show_list_to_char( obj->first_content, ch, TRUE, TRUE );
      if( EXA_prog_trigger )
        oprog_examine_trigger( ch, obj );
      break;
    }
    return;
  }

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

  door = get_door(arg1);
  if ( (pexit=find_door(ch, arg1, TRUE)) != NULL )
  {
    if ( IS_SET(pexit->exit_info, EX_CLOSED)
         &&  !IS_SET(pexit->exit_info, EX_WINDOW) )
    {
      if ( (IS_SET(pexit->exit_info, EX_SECRET)
            ||    IS_SET(pexit->exit_info, EX_DIG)) && door != -1 )
        send_to_char( "Nothing special there.\n\r", 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.\n\r", ch );

    /*
     * Ability to look into the next room			-Thoric
     */
    if ( pexit->to_room
         && ( IS_AFFECTED( ch, AFF_SCRYING )
              ||   ch->class == CLASS_THIEF
              ||   IS_SET( pexit->exit_info, EX_xLOOK )
              ||   get_trust(ch) >= LEVEL_IMMORTAL ) )
    {
      if ( !IS_SET( pexit->exit_info, EX_xLOOK )
           &&    get_trust( ch ) < LEVEL_IMMORTAL )
      {
        set_char_color( AT_MAGIC, ch );
        send_to_char( "You attempt to scry...\n\r", ch );
        /*
         * Change by Narn, Sept 96 to allow characters who don't have the
         * scry spell to benefit from objects that are affected by scry.
         */
        if (!IS_NPC(ch) )
        {
          int percent = LEARNED(ch, skill_lookup("scry") );
          if ( !percent )
          {
            if ( ch->class == CLASS_THIEF )
              percent = 95;
            else
              percent = 55;	/* 95 was too good -Thoric */
          }

          if ( number_percent( ) > percent )
          {
            send_to_char( "You fail.\n\r", ch );
            return;
          }
        }
      }
      if ( room_is_private( pexit->to_room ) && ch->level < sysdata.level_override_private )
      {
        set_char_color( AT_WHITE, ch );
        send_to_char( "The room ahead is private!\n\r", ch );
        return;
      }

      if( IS_EXIT_FLAG( pexit, EX_OVERLAND ) )
      {
        original = ch->in_room;
        enter_map( ch, pexit->x, pexit->y, -1 );
        leave_map( ch, NULL, original );
      }
      else
      {
        original = ch->in_room;
        char_from_room( ch );
        char_to_room( ch, pexit->to_room );
        do_look( ch, "auto" );
        char_from_room( ch );
        char_to_room( ch, original );
      }
    }
    return;
  }
  else
    if ( door != -1 )
    {
      send_to_char( "Nothing special there.\n\r", 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_color( pdesc, ch );
        if( EXA_prog_trigger )
          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_color( pdesc, ch );
        if( EXA_prog_trigger )
          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_color( "You see nothing special.\r\n", ch );
        else
          send_to_char_color( pdesc, ch );
        if( EXA_prog_trigger )
          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_color( pdesc, ch );
        if( EXA_prog_trigger )
          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_color( pdesc, ch );
        if( EXA_prog_trigger )
          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_color( pdesc, ch );
        if( EXA_prog_trigger )
          oprog_examine_trigger( ch, obj );
        return;
      }
    }
  }

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

void show_race_line( CHAR_DATA *ch, CHAR_DATA *victim )
{
  char buf[MAX_STRING_LENGTH];
  int feet, inches;


  if ( !IS_NPC(victim) && (victim != ch) )
  {
    feet =  victim->height / 12;
    inches = victim->height % 12;
    sprintf( buf, "%s is %d'%d\" and weighs %d pounds.\n\r", PERS(victim, ch, FALSE), feet, inches, victim->weight );
    send_to_char( buf, ch);
    return;
  }
  if ( !IS_NPC(victim) && (victim == ch) )
  {
    feet =  victim->height / 12;
    inches = victim->height % 12;
    sprintf( buf, "You are %d'%d\" and weigh %d pounds.\n\r",  feet, inches, victim->weight );
    send_to_char( buf, ch);
    return;
  }

}


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

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


  if ( victim != ch )
  {
    strcpy( buf, PERS(victim, ch, FALSE) );
    if ( percent >= 100 )
      strcat( buf, " is in perfect health.\n\r" );
    else if ( percent >=  90 )
      strcat( buf, " is slightly scratched.\n\r" );
    else if ( percent >=  80 )
      strcat( buf, " has a few bruises.\n\r"     );
    else if ( percent >=  70 )
      strcat( buf, " has some cuts.\n\r"         );
    else if ( percent >=  60 )
      strcat( buf, " has several wounds.\n\r"    );
    else if ( percent >=  50 )
      strcat( buf, " has many nasty wounds.\n\r" );
    else if ( percent >=  40 )
      strcat( buf, " is bleeding freely.\n\r"    );
    else if ( percent >=  30 )
      strcat( buf, " is covered in blood.\n\r"   );
    else if ( percent >=  20 )
      strcat( buf, " is leaking guts.\n\r"       );
    else if ( percent >=  10 )
      strcat( buf, " is almost dead.\n\r"        );
    else
      strcat( buf, " is DYING.\n\r"              );
  }
  else
  {
    strcpy( buf, "You" );
    if ( percent >= 100 )
      strcat( buf, " are in perfect health.\n\r" );
    else if ( percent >=  90 )
      strcat( buf, " are slightly scratched.\n\r");
    else if ( percent >=  80 )
      strcat( buf, " have a few bruises.\n\r"    );
    else if ( percent >=  70 )
      strcat( buf, " have some cuts.\n\r"        );
    else if ( percent >=  60 )
      strcat( buf, " have several wounds.\n\r"   );
    else if ( percent >=  50 )
      strcat( buf, " have many nasty wounds.\n\r");
    else if ( percent >=  40 )
      strcat( buf, " are bleeding freely.\n\r"   );
    else if ( percent >=  30 )
      strcat( buf, " are covered in blood.\n\r"  );
    else if ( percent >=  20 )
      strcat( buf, " are leaking guts.\n\r"      );
    else if ( percent >=  10 )
      strcat( buf, " are almost dead.\n\r"       );
    else
      strcat( buf, " are DYING.\n\r"             );
  }

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

/* 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;
  bool brief;

  if ( !ch->desc )
    return;

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

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

  if ( !check_blind( ch ) )
    return;

  set_char_color( AT_ACTION, ch );
  argument = one_argument( argument, arg1 );

  if ( arg1[0] == '\0' )
  {
    if ( xIS_SET(ch->act, PLR_BRIEF) )
      brief = TRUE;
    else
      brief = FALSE;
    xSET_BIT( ch->act, PLR_BRIEF );
    do_look( ch, "auto" );
    if ( !brief )
      xREMOVE_BIT(ch->act, PLR_BRIEF);
    return;
  }

  if ( ( victim = get_char_room( ch, arg1 ) ) == NULL )
  {
    send_to_char( "They're not here.\n\r", ch );
    return;
  }
  else
  {
    if ( can_see( victim, ch, FALSE ) )
    {
      act( AT_ACTION, "$n glances at you.", ch, NULL, victim, TO_VICT );
      act( AT_ACTION, "$n glances at $N.",  ch, NULL, victim, TO_NOTVICT );
    }
    if ( IS_IMMORTAL( ch ) && victim != ch )
    {
      if ( IS_NPC( victim ) )
        ch_printf( ch, "Mobile #%d '%s' ",
                   victim->pIndexData->vnum, victim->name );
      else
        ch_printf( ch, "%s ", victim->name );
      ch_printf( ch, "is a level %d %s %s.\n\r",
                 victim->level,
                 IS_NPC(victim)?victim->race<MAX_NPC_RACE&&victim->race>=0?
               npc_race[victim->race] : "unknown":victim->race<MAX_PC_RACE&&
                 race_table[victim->race]->race_name&&
                 race_table[victim->race]->race_name[0] != '\0'?
                 race_table[victim->race]->race_name:"unknown",
                 IS_NPC(victim)?victim->class<MAX_NPC_CLASS&&victim->class>=0?
               npc_class[victim->class] : "unknown":victim->class<MAX_PC_CLASS&&
                 class_table[victim->class]->who_name&&
                 class_table[victim->class]->who_name[0] != '\0'?
                 class_table[victim->class]->who_name:"unknown");
      /* New Change
      	    victim->race<MAX_NPC_RACE&&victim->race>=0?npc_race[victim->race] : "unknown",
      	    victim->class<MAX_NPC_CLASS&&victim->class>=0?npc_class[victim->class] : "unknown" );
      */
    }
    show_condition( ch, victim );

    return;
  }

  return;
}


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

  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?\n\r", ch );
    return;
  }

  EXA_prog_trigger = FALSE;
  do_look( ch, arg );
  EXA_prog_trigger = TRUE;

  /*
   * 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.\n\r", board->num_posts );
      else
        send_to_char( "There aren't any notes posted here.\n\r", ch );
    }

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

    case ITEM_ARMOR:
      if ( obj->value[1] == 0 )
        obj->value[1] = obj->value[0];
      if ( obj->value[1] == 0 )
        obj->value[1] = 1;
      dam = (sh_int) ((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, "\n\r" );
      send_to_char( buf, ch );
      break;

    case ITEM_WEAPON:
      dam = INIT_WEAPON_CONDITION - obj->value[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, "\n\r" );
      send_to_char( buf, ch );
      break;

    case ITEM_COOK:
      strcpy( buf, "As you examine it carefully you notice that it " );
      dam = obj->value[2];
      if (dam >= 3)
        strcat( buf, "is burned to a crisp.");
      else if (dam == 2)
        strcat( buf, "is a little over cooked.");
      else if (dam == 1)
        strcat( buf, "is perfectly roasted.");
      else
        strcat( buf, "is raw.");
      strcat( buf, "\n\r" );
      send_to_char( buf, ch );
    case ITEM_FOOD:
      if ( obj->timer > 0 && obj->value[1] > 0 )
        dam = (obj->timer * 10) / obj->value[1];
      else
        dam = 10;
      if ( obj->item_type == ITEM_FOOD )
        strcpy( buf, "As you examine it carefully you notice that it " );
      else
        strcpy( buf, "Also 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, "\n\r" );
      send_to_char( buf, ch );
      break;


    case ITEM_SWITCH:
    case ITEM_LEVER:
    case ITEM_PULLCHAIN:
      if ( IS_SET( obj->value[0], TRIG_UP ) )
        send_to_char( "You notice that it is in the up position.\n\r", ch );
      else
        send_to_char( "You notice that it is in the down position.\n\r", ch );
      break;
    case ITEM_BUTTON:
      if ( IS_SET( obj->value[0], TRIG_UP ) )
        send_to_char( "You notice that it is depressed.\n\r", ch );
      else
        send_to_char( "You notice that it is not depressed.\n\r", ch );
      break;

    case ITEM_CORPSE_PC:
    case ITEM_CORPSE_NPC:
      {
        sh_int timerfrac = obj->timer;
        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.\n\r", ch );
          break;
        case 4:
          send_to_char( "This corpse was slain a little while ago.\n\r", ch );
          break;
        case 3:
          send_to_char( "A foul smell rises from the corpse, and it is covered in flies.\n\r", ch );
          break;
        case 2:
          send_to_char( "A writhing mass of maggots and decay, you can barely go near this corpse.\n\r", ch );
          break;
        case 1:
        case 0:
          send_to_char( "Little more than bones, there isn't much left of this corpse.\n\r", ch );
          break;
        }
      }
    case ITEM_CONTAINER:
      if ( IS_OBJ_STAT( obj, ITEM_COVERING ) )
        break;
    case ITEM_DRINK_CON:
    case ITEM_QUIVER:
      send_to_char( "When you look inside, you see:\n\r", ch );
    case ITEM_KEYRING:
      EXA_prog_trigger = FALSE;
      sprintf( buf, "in %s",arg );
      do_look( ch, buf );
      EXA_prog_trigger = TRUE;
      break;
    }
    if ( IS_OBJ_STAT( obj, ITEM_COVERING ) )
    {
      EXA_prog_trigger = FALSE;
      sprintf( buf, "under %s", arg );
      do_look( ch, buf );
      EXA_prog_trigger = TRUE;
    }
    oprog_examine_trigger( ch, obj );
    if ( char_died(ch) || obj_extracted(obj) )
      return;

    check_for_trap( ch, obj, TRAP_EXAMINE );
  }
  return;
}

void do_exits( CHAR_DATA *ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  EXIT_DATA *pexit;
  bool found, closed=FALSE, locked=FALSE, DT=FALSE;
  bool fAuto;
  int  spaces;

  set_char_color( AT_EXITS, ch );
  fAuto  = !str_cmp( argument, "auto" );

  if ( !check_blind(ch) )
    return;

  strcpy( buf, fAuto ? "&WExits:" : "&WObvious exits:\n\r" );

  found = FALSE;
  for ( pexit = ch->in_room->first_exit; pexit; pexit = pexit->next )
  {
    if ( pexit->to_room
         &&  !IS_SET(pexit->exit_info, EX_SECRET)
         && (!IS_SET(pexit->exit_info, EX_WINDOW)
             ||   IS_SET(pexit->exit_info, EX_ISDOOR))
         &&  !IS_SET(pexit->exit_info, EX_HIDDEN) )
    {
      found = TRUE;
      closed = IS_SET(pexit->exit_info, EX_CLOSED);
      locked = (IS_SET(pexit->exit_info, EX_LOCKED) || IS_SET(pexit->exit_info, EX_BOLTED) );
      DT = ((IS_IMMORTAL(ch) || IS_AFFECTED(ch, AFF_DETECTTRAPS)) && xIS_SET(pexit->to_room->room_flags, ROOM_DEATH));

      if ( fAuto )
      {
        sprintf( buf, "%s %s" "%s" "&W" "%s", buf,
                 DT? "&R***" : !closed ? "" : locked ? "&R[&W" : "[",
                 DT ? strupper(dir_name[pexit->vdir]): dir_name[pexit->vdir],
                 DT? "&R***&W" : !closed ? "" : locked ? "&R]&W" : "]");
      }
      else
      {
        /* I don't want to underline spaces, so I'll calculate the number we need */
        spaces = 5 - strlen (dir_name[pexit->vdir]);
        if (spaces < 0)
          spaces = 0;
        sprintf( buf + strlen(buf), "%s" "%*s - %s\n\r",
                 capitalize( dir_name[pexit->vdir] ),
                 spaces,  /* number of spaces */
                 "",
                 DT ? "&RYou sense an aura of imminent doom this way&W" : locked ? "&R[&WClosed and Locked/Barred&R]&W" : closed ? "[Closed]" : room_is_dark( pexit->to_room ) ?  "Too dark to tell" : pexit->to_room->name );
      }
    }
  }

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

char *	const	day_name	[] =
  {
    "Monday", "Tuesday", "Wedensday", "Thursday", "Friday",
    "Saturday", "Sunday"
  };

char *	const	month_name	[] =
  {
    "January", "Febuary", "March", "April",
    "May", "June", "July", "August", "September",
    "October", "November", "December"
  };

void do_time( CHAR_DATA *ch, char *argument )
{
  extern char str_boot_time[];
  char *suf;
  int day;

  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, %s the %d%s of %s in the year %d.\n\r"
             "The mud started up at:    %s\r"
             "The system time (E.S.T.): %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],
             time_info.year,
             str_boot_time,
             (char *) ctime( &current_time ));

  if( sysdata.CLEANPFILES )
    ch_printf( ch, "\n\rNext pfile cleanup is scheduled for: %s\n\r", (char *)ctime( &new_pfile_time_t ) );

  return;
}



/*
 * Produce a description of the weather based on area weather using
 * the following sentence format:
 *		<combo-phrase> and <single-phrase>.
 * Where the combo-phrase describes either the precipitation and
 * temperature or the wind and temperature. The single-phrase
 * describes either the wind or precipitation depending upon the
 * combo-phrase.
 * Last Modified: July 31, 1997
 * Fireblade - Under Construction
 */
void do_weather(CHAR_DATA *ch, char *argument)
{
  char *combo, *single;
  char buf[MAX_INPUT_LENGTH];
  int temp, precip, wind;

  if( !IS_PLR_FLAG( ch, PLR_ONMAP ) )
  {

    if ( !xIS_OUTSIDE(ch) || INDOOR_SECTOR(ch->in_room->sector_type) )
    {
      send_to_char( "You can't see the sky from here.\n\r", ch );
      return;
    }
  }

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

  temp = (ch->in_room->area->weather->temp + 3*weath_unit - 1)/
         weath_unit;
  precip = (ch->in_room->area->weather->precip + 3*weath_unit - 1)/
           weath_unit;
  wind = (ch->in_room->area->weather->wind + 3*weath_unit - 1)/
         weath_unit;

  if ( precip >= 3 )
  {
    combo = preciptemp_msg[precip][temp];
    single = wind_msg[wind];
  }
  else
  {
    combo = windtemp_msg[wind][temp];
    single = precip_msg[precip];
  }

  sprintf(buf, "%s and %s.\n\r", combo, single);

  set_char_color(AT_BLUE, ch);

  ch_printf(ch, buf);
}

/*
 * 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, char *argument )
{
  char argall[MAX_INPUT_LENGTH];
  char argone[MAX_INPUT_LENGTH];
  char argnew[MAX_INPUT_LENGTH];
  HELP_DATA *pHelp;
  int lev;

  if ( argument[0] == '\0' )
    argument = "summary";

  if ( isdigit(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;
}

/*
 * LAWS command
 */
void do_laws( CHAR_DATA *ch, char *argument )
{
  char buf[1024];

  if ( argument == NULL)
    do_help( ch, "laws" );
  else
  {
    sprintf( buf, "law %s", argument );
    do_help( ch, buf );
  }
}

//  Ranks by number of matches between two whole words. Coded for the Similar Helpfiles
//  Snippet by Senir.
sh_int str_similarity( const char *astr, const char *bstr )
{
  sh_int matches=0;

  if (!astr || !bstr)
    return matches;

  for ( ; *astr; astr++)
  {
    if ( LOWER(*astr) == LOWER(*bstr) )
      matches++;

    if (++bstr == '\0')
      return matches;
  }

  return matches;
}

//  Ranks by number of matches until there's a nonmatching character between two words.
//  Coded for the Similar Helpfiles Snippet by Senir.
sh_int str_prefix_level( const char *astr, const char *bstr )
{
  sh_int matches=0;

  if (!astr || !bstr)
    return matches;

  for ( ; *astr; astr++)
  {
    if ( LOWER(*astr) == LOWER(*bstr) )
      matches++;
    else
      return matches;

    if (++bstr == '\0')
      return matches;
  }

  return matches;
}

// Main function of Similar Helpfiles Snippet by Senir. It loops through all of the
// helpfiles, using the string matching function defined to find the closest matching
// helpfiles to the argument. It then checks for singles. Then, if matching helpfiles
// are found at all, it loops through and prints out the closest matching helpfiles.
// If its a single(there's only one), it opens the helpfile.
void similar_help_files(CHAR_DATA *ch, char *argument)
{
  HELP_DATA *pHelp=NULL;
  char buf[MAX_STRING_LENGTH];
  char *extension;
  sh_int lvl=0;
  bool single=FALSE;


  send_to_pager_color( "&C&BSimilar Help Files:\n\r", ch);

  for ( pHelp = first_help; pHelp; pHelp=pHelp->next)
  {
    buf[0]='\0';
    extension=pHelp->keyword;

    if (pHelp->level > get_trust(ch))
      continue;

    while ( extension[0] != '\0' )
    {
      extension= one_argument(extension, buf);

      if ( str_similarity(argument, buf) > lvl)
      {
        lvl=str_similarity(argument, buf);
        single=TRUE;
      }
      else if ( str_similarity(argument, buf) == lvl && lvl > 0)
      {
        single=FALSE;
      }
    }
  }

  if (lvl==0)
  {
    send_to_pager_color( "&C&GNo similar help files.\n\r", ch);
    return;
  }

  for ( pHelp = first_help; pHelp; pHelp=pHelp->next)
  {
    buf[0]='\0';
    extension=pHelp->keyword;

    while ( extension[0] != '\0' )
    {
      extension=one_argument(extension, buf);

      if ( str_similarity(argument, buf) >= lvl
           && pHelp->level <= get_trust(ch))
      {
        if (single)
        {
          send_to_pager_color( "&C&GOpening only similar helpfile.&C\n\r", ch);
          do_help( ch, buf);
          return;
        }

        pager_printf_color(ch, "&C&G   %s\n\r", pHelp->keyword);
        break;

      }

    }
  }
  return;
}


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

  set_pager_color( AT_HELP, ch );

  if ( (pHelp = get_help( ch, argument )) == NULL )
  {
    //  Looks better printing out the missed argument before going to similar
    //  helpfiles. - Senir
    pager_printf_color( ch, "&C&wNo help on \'%s\' found.\n\r", argument );
    similar_help_files(ch, argument);
    return;
  }

  /* Make newbies do a help start. --Shaddai */
  if ( !IS_NPC(ch) && !str_cmp( argument, "start" ) )
    SET_BIT(ch->pcdata->flags, PCFLAG_HELPSTART);

  set_pager_color( AT_HELP, ch );

  if ( pHelp->level >= 0 && str_cmp( argument, "imotd" ) )
  {
    send_to_pager( pHelp->keyword, ch );
    send_to_pager( "\n\r", 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 );
  return;
}

void do_news( CHAR_DATA *ch, char *argument )
{
  set_pager_color( AT_NOTE, ch );
  do_help( ch, "news" );
}

extern char * help_greeting;	/* so we can edit the greeting online */

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

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

  switch( ch->substate )
  {
  default:
    break;
  case SUB_HELP_EDIT:
    if ( (pHelp = ch->dest_buf) == NULL )
    {
      bug( "hedit: sub_help_edit: NULL ch->dest_buf", 0 );
      stop_editing( ch );
      return;
    }
    if ( help_greeting == pHelp->text )
      help_greeting = NULL;
    STRFREE( pHelp->text );
    pHelp->text = copy_buffer( ch );
    if ( !help_greeting )
      help_greeting = pHelp->text;
    stop_editing( ch );
    return;
  }
  if ( (pHelp = get_help(ch, argument)) == NULL )     /* new help */
  {
    HELP_DATA *tHelp;
    char argnew[MAX_INPUT_LENGTH];
    int lev;
    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(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
 */
char *help_fix( char *text )
{
  char *fixed;

  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]\n\r",	ch );
    send_to_char( "\n\r",						ch );
    send_to_char( "Field being one of:\n\r",			ch );
    send_to_char( "  level keyword remove save\n\r",		ch );
    return;
  }

  if ( !str_cmp( arg1, "save" ) )
  {
    FILE *fpout;

    log_string_plus( "Saving help.are...", LOG_NORMAL, LEVEL_GREATER );

    rename( "help.are", "help.are.bak" );
    fclose( fpReserve );
    if ( ( fpout = fopen( "help.are", "w" ) ) == NULL )
    {
      bug( "hset save: fopen", 0 );
      perror( "help.are" );
      fpReserve = fopen( NULL_FILE, "r" );
      return;
    }

    fprintf( fpout, "#HELPS\n\n" );
    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 );
    fpReserve = fopen( NULL_FILE, "r" );
    send_to_char( "Saved.\n\r", 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.\n\r", ch );
    return;
  }
  if ( !str_cmp( arg1, "remove" ) )
  {
    UNLINK( pHelp, first_help, last_help, next, prev );
    STRFREE( pHelp->text );
    STRFREE( pHelp->keyword );
    DISPOSE( pHelp );
    send_to_char( "Removed.\n\r", ch );
    return;
  }
  if ( !str_cmp( arg1, "level" ) )
  {
    pHelp->level = atoi( arg2 );
    send_to_char( "Done.\n\r", ch );
    return;
  }
  if ( !str_cmp( arg1, "keyword" ) )
  {
    STRFREE( pHelp->keyword );
    pHelp->keyword = STRALLOC( strupper(arg2) );
    send_to_char( "Done.\n\r", ch );
    return;
  }

  do_hset( ch, "" );
}

void do_hl( CHAR_DATA *ch, char *argument )
{
  send_to_char( "If you want to use HLIST, spell it out.\n\r", ch );
  return;
}

/*
 * Show help topics in a level range				-Thoric
 * Idea suggested by Gorog
 * prefix keyword indexing added by Fireblade
 */
void do_hlist( CHAR_DATA *ch, char *argument )
{
  int min, max, minlimit, maxlimit, cnt;
  char arg[MAX_INPUT_LENGTH];
  HELP_DATA *help;
  bool minfound, maxfound;
  char *idx;

  maxlimit = get_trust(ch);
  minlimit = maxlimit >= LEVEL_GREATER ? -1 : 0;

  min = minlimit;
  max  = maxlimit;

  idx = NULL;
  minfound = FALSE;
  maxfound = FALSE;

  for ( argument = one_argument(argument, arg); arg[0] != '\0';
        argument = one_argument(argument, arg))
  {
    if( !isdigit(arg[0]) )
    {
      if ( idx )
      {
        set_char_color(AT_GREEN, ch);
        ch_printf(ch, "You may only use a single keyword to index the list.\n\r");
        return;
      }
      idx = STRALLOC(arg);
    }
    else
    {
      if ( !minfound )
      {
        min = URANGE(minlimit, atoi(arg), maxlimit);
        minfound = TRUE;
      }
      else
        if ( !maxfound )
        {
          max = URANGE(minlimit, atoi(arg), maxlimit);
          maxfound = TRUE;
        }
        else
        {
          set_char_color(AT_GREEN, ch);
          ch_printf(ch, "You may only use two level limits.\n\r");
          return;
        }
    }
  }

  if ( min > max )
  {
    int temp = min;

    min = max;
    max = temp;
  }

  set_pager_color( AT_GREEN, ch );
  pager_printf( ch, "Help Topics in level range %d to %d:\n\r\n\r", min, max );
  for ( cnt = 0, help = first_help; help; help = help->next )
    if ( help->level >= min && help->level <= max
         &&  (!idx || nifty_is_name_prefix(idx, help->keyword)) )
    {
      pager_printf( ch, "  %3d %s\n\r", help->level, help->keyword );
      ++cnt;
    }
  if ( cnt )
    pager_printf( ch, "\n\r%d pages found.\n\r", cnt );
  else
    send_to_char( "None found.\n\r", ch );

  if ( idx )
    STRFREE(idx);

  return;
}


/*
 * New do_who with WHO REQUEST, clan, race 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
 *
 * Who group by Altrag, Feb 28/97
 */
struct whogr_s
{
  struct whogr_s *next;
  struct whogr_s *follower;
  struct whogr_s *l_follow;
  DESCRIPTOR_DATA *d;
  int indent;
}
*first_whogr, *last_whogr;

struct whogr_s *find_whogr(DESCRIPTOR_DATA *d, struct whogr_s *first)
{
  struct whogr_s *whogr, *whogr_t;

  for (whogr = first; whogr; whogr = whogr->next)
    if (whogr->d == d)
      return whogr;
    else if (whogr->follower && (whogr_t = find_whogr(d, whogr->follower)))
      return whogr_t;
  return NULL;
}

void indent_whogr(CHAR_DATA *looker, struct whogr_s *whogr, int ilev)
{
  for ( ; whogr; whogr = whogr->next )
  {
    if (whogr->follower)
    {
      int nlev = ilev;
      CHAR_DATA *wch =
        (whogr->d->original ? whogr->d->original : whogr->d->character);

      if (can_see(looker, wch, TRUE) && !IS_IMMORTAL(wch))
        nlev += 3;
      indent_whogr(looker, whogr->follower, nlev);
    }
    whogr->indent = ilev;
  }
}

/* This a great big mess to backwards-structure the ->leader character
   fields */
void create_whogr(CHAR_DATA *looker)
{
  DESCRIPTOR_DATA *d;
  CHAR_DATA *wch;
  struct whogr_s *whogr, *whogr_t;
  int dc = 0, wc = 0;

  while ((whogr = first_whogr) != NULL)
  {
    first_whogr = whogr->next;
    DISPOSE(whogr);
  }
  first_whogr = last_whogr = NULL;
  /* Link in the ones without leaders first */
  for (d = last_descriptor; d; d = d->prev)
  {
    if (d->connected != CON_PLAYING && d->connected != CON_EDITING)
      continue;
    ++dc;
    wch = (d->original ? d->original : d->character);
    if (!wch->leader || wch->leader == wch || !wch->leader->desc ||
        IS_NPC(wch->leader) || IS_IMMORTAL(wch) || IS_IMMORTAL(wch->leader))
    {
      CREATE(whogr, struct whogr_s, 1);
      if (!last_whogr)
        first_whogr = last_whogr = whogr;
      else
      {
        last_whogr->next = whogr;
        last_whogr = whogr;
      }
      whogr->next = NULL;
      whogr->follower = whogr->l_follow = NULL;
      whogr->d = d;
      whogr->indent = 0;
      ++wc;
    }
  }
  /* Now for those who have leaders.. */
  while (wc < dc)
    for (d = last_descriptor; d; d = d->prev)
    {
      if (d->connected != CON_PLAYING && d->connected != CON_EDITING)
        continue;
      if (find_whogr(d, first_whogr))
        continue;
      wch = (d->original ? d->original : d->character);
      if (wch->leader && wch->leader != wch && wch->leader->desc &&
          !IS_NPC(wch->leader) && !IS_IMMORTAL(wch) &&
          !IS_IMMORTAL(wch->leader) &&
          (whogr_t = find_whogr(wch->leader->desc, first_whogr)))
      {
        CREATE(whogr, struct whogr_s, 1);
        if (!whogr_t->l_follow)
          whogr_t->follower = whogr_t->l_follow = whogr;
        else
        {
          whogr_t->l_follow->next = whogr;
          whogr_t->l_follow = whogr;
        }
        whogr->next = NULL;
        whogr->follower = whogr->l_follow = NULL;
        whogr->d = d;
        whogr->indent = 0;
        ++wc;
      }
    }
  /* Set up indentation levels */
  indent_whogr(looker, first_whogr, 0);

  /* And now to linear link them.. */
  for (whogr_t = NULL, whogr = first_whogr; whogr; )
    if (whogr->l_follow)
    {
      whogr->l_follow->next = whogr;
      whogr->l_follow = NULL;
      if (whogr_t)
        whogr_t->next = whogr = whogr->follower;
      else
        first_whogr = whogr = whogr->follower;
    }
    else
    {
      whogr_t = whogr;
      whogr = whogr->next;
    }
}



void do_who( CHAR_DATA *ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  char clan_name[MAX_INPUT_LENGTH];
  char council_name[MAX_INPUT_LENGTH];
  char invis_str[MAX_INPUT_LENGTH];
  char char_name[MAX_INPUT_LENGTH];
  char *extra_title;
  char class_text[MAX_INPUT_LENGTH];
  char const *sex;
  char const *race;
  struct whogr_s *whogr, *whogr_p;
  DESCRIPTOR_DATA *d;
  int iClass, iRace;
  int iLevelLower;
  int iLevelUpper;
  int nNumber;
  int nMatch;
  bool rgfClass[MAX_CLASS];
  bool rgfRace[MAX_RACE];
  bool fClassRestrict;
  bool fRaceRestrict;
  bool fImmortalOnly;
  bool fLeader;
  bool fPkill;
  bool fShowHomepage;
  bool fClanMatch; /* SB who clan (order),who guild, and who council */
  bool fCouncilMatch;
  bool fDeityMatch;
  bool fGroup;
  CLAN_DATA *pClan = NULL;
  COUNCIL_DATA *pCouncil = NULL;
  DEITY_DATA *pDeity = NULL;
  FILE *whoout = NULL;

  /*
  #define WT_IMM    0;
  #define WT_MORTAL 1;
  #define WT_DEADLY 2;
  */

  WHO_DATA *cur_who = NULL;
  WHO_DATA *next_who = NULL;
  WHO_DATA *first_mortal = NULL;
  WHO_DATA *first_imm = NULL;
  WHO_DATA *first_deadly  = NULL;
  WHO_DATA *first_grouped = NULL;
  WHO_DATA *first_groupwho = NULL;


  /*
   * Set default arguments.
   */
  iLevelLower    = 0;
  iLevelUpper    = MAX_LEVEL;
  fClassRestrict = FALSE;
  fRaceRestrict  = FALSE;
  fImmortalOnly  = FALSE;
  fPkill         = FALSE;
  fShowHomepage  = FALSE;
  fClanMatch	   = FALSE; /* SB who clan (order), who guild, who council */
  fCouncilMatch  = FALSE;
  fDeityMatch    = FALSE;
  fGroup	   = FALSE; /* Alty who group */
  fLeader	   = FALSE;
  for ( iClass = 0; iClass < MAX_CLASS; iClass++ )
    rgfClass[iClass] = FALSE;
  for ( iRace = 0; iRace < MAX_RACE; iRace++ )
    rgfRace[iRace] = FALSE;

#ifdef REQWHOARG
  /*
   * The who command must have at least one argument because we often
   * have up to 500 players on. Too much spam if a player accidentally
   * types "who" with no arguments.           --Gorog
   */
  if ( ch && argument[0] == '\0' )
  {
    send_to_pager_color(
      "\n\r&GYou must specify at least one argument.\n\rUse 'who 1' to view the entire who list.\n\r", ch);
    return;
  }
#endif

  /*
   * Parse arguments.
   */
  nNumber = 0;
  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.\n\r", ch );
        return;
      }
    }
    else
    {
      if ( strlen(arg) < 3 )
      {
        send_to_char( "Arguments must be longer than that.\n\r", ch );
        return;
      }

      /*
       * Look for classes to turn on.
       */
      if ( !str_cmp( arg, "deadly" ) || !str_cmp( arg, "pkill" ) )
        fPkill = TRUE;
      else
        if ( !str_cmp( arg, "imm" ) || !str_cmp( arg, "gods" ) )
          fImmortalOnly = TRUE;
        else
          if ( !str_cmp( arg, "leader" ) )
            fLeader = TRUE;
          else
            if ( !str_cmp( arg, "www" ) )
              fShowHomepage = TRUE;
            else
              if ( !str_cmp( arg, "group" ) && ch )
                fGroup = TRUE;
              else		 /* SB who clan (order), guild, council */
                if  ( ( pClan = get_clan (arg) ) )
                  fClanMatch = TRUE;
                else
                  if ( ( pCouncil = get_council (arg) ) )
                    fCouncilMatch = TRUE;
                  else
                    if ( ( pDeity = get_deity (arg) ) )
                      fDeityMatch = TRUE;
                    else
                    {
                      for ( iClass = 0; iClass < MAX_CLASS; iClass++ )
                      {
                        if ( !str_cmp( arg, class_table[iClass]->who_name ) )
                        {
                          rgfClass[iClass] = TRUE;
                          break;
                        }
                      }
                      if ( iClass != MAX_CLASS )
                        fClassRestrict = TRUE;

                      for ( iRace = 0; iRace < MAX_RACE; iRace++ )
                      {
                        if ( !str_cmp( arg, race_table[iRace]->race_name ) )
                        {
                          rgfRace[iRace] = TRUE;
                          break;
                        }
                      }
                      if ( iRace != MAX_RACE )
                        fRaceRestrict = TRUE;

                      if ( iClass == MAX_CLASS && iRace == MAX_RACE
                           && fClanMatch == FALSE
                           && fCouncilMatch == FALSE
                           && fDeityMatch == FALSE )
                      {
                        send_to_char( "That's not a class, race, order, guild,"
                                      " council or deity.\n\r", ch );
                        return;
                      }
                    }
    }
  }

  /*
   * Now find matching chars.
   */
  nMatch = 0;
  buf[0] = '\0';
  if ( ch )
    set_pager_color( AT_GREEN, ch );
  else
  {
    if ( fShowHomepage )
      whoout = fopen( WEBWHO_FILE, "w" );
    else
      whoout = fopen( WHO_FILE, "w" );
    if ( !whoout )
    {
      bug( "do_who: cannot open who file!" );
      return;
    }
  }

  /* start from last to first to get it in the proper order */
  if (fGroup)
  {
    create_whogr(ch);
    whogr = first_whogr;
    d = whogr->d;
  }
  else
  {
    whogr = NULL;
    d = last_descriptor;
  }
  whogr_p = NULL;
  for ( ; d; whogr_p = whogr, whogr = (fGroup ? whogr->next : NULL),
        d = (fGroup ? (whogr ? whogr->d : NULL) : d->prev))
  {
    CHAR_DATA *wch;
    char const *class;

    if ( (d->connected != CON_PLAYING && d->connected != CON_EDITING)
         ||   !can_see( ch, d->character, TRUE ) || d->original)
      continue;
    wch   = d->original ? d->original : d->character;
    if ( wch->level < iLevelLower
         ||   wch->level > iLevelUpper
         || ( fPkill && !CAN_PKILL( wch ) )
         || ( fImmortalOnly  && wch->level < LEVEL_IMMORTAL )
         || ( fClassRestrict && !rgfClass[wch->class] )
         || ( fRaceRestrict && !rgfRace[wch->race] )
         || ( fClanMatch && ( pClan != wch->pcdata->clan ))  /* SB */
         || ( fCouncilMatch && ( pCouncil != wch->pcdata->council )) /* SB */
         || ( fDeityMatch && ( pDeity != wch->pcdata->deity )) )
      continue;
    if ( fLeader && !(wch->pcdata->council &&
                      ((wch->pcdata->council->head2 &&
                        !str_cmp(wch->pcdata->council->head2, wch->name)) ||
                       (wch->pcdata->council->head &&
                        !str_cmp(wch->pcdata->council->head, wch->name)))) &&
         !(wch->pcdata->clan && ((wch->pcdata->clan->deity &&
                                  !str_cmp(wch->pcdata->clan->deity,wch->name) )
                                 || (wch->pcdata->clan->leader
                                     && !str_cmp(wch->pcdata->clan->leader, wch->name ) )
                                 || (wch->pcdata->clan->number1
                                     && !str_cmp(wch->pcdata->clan->number1, wch->name ) )
                                 || (wch->pcdata->clan->number2
                                     && !str_cmp(wch->pcdata->clan->number2, wch->name )))))
      continue;

    if (fGroup && !wch->leader &&
        !IS_SET(wch->pcdata->flags, PCFLAG_GROUPWHO) &&
        (!whogr_p || !whogr_p->indent))
      continue;

    nMatch++;

    if ( fShowHomepage
         &&   wch->pcdata->homepage
         &&   wch->pcdata->homepage[0] != '\0' )
      sprintf( char_name, "<A HREF=\"%s\">%s</A>",
               show_tilde( wch->pcdata->homepage ), wch->name );
    else
      strcpy( char_name, wch->name );

    sprintf( class_text, "%s%2d ", NOT_AUTHED(wch) ? "N" : " ", wch->level/*, class_table[wch->class]->who_name */);
    class = class_text;



    switch ( wch->sex )
    {
    case SEX_MALE:
      sex = "M";
      break;

    case SEX_FEMALE:
      sex = "F";
      break;

    case SEX_NEUTRAL:
      sex = "N";
      break;
    }

    switch ( wch->level )
    {
    default:
      break;
    case MAX_LEVEL -  0:
      class = " IMP";
      break;
    case MAX_LEVEL -  1:
      class = " Imm";
      break;
    case MAX_LEVEL -  2:
      class = " Imm";
      break;
    case MAX_LEVEL -  3:
      class = " Imm";
      break;
    case MAX_LEVEL -  4:
      class = " Imm";
      break;
    case MAX_LEVEL -  5:
      class = " Imm";
      break;
    case MAX_LEVEL -  6:
      class = " Imm";
      break;
    case MAX_LEVEL -  7:
      class = " Imm";
      break;
    case MAX_LEVEL -  8:
      class = " Imm";
      break;
    case MAX_LEVEL -  9:
      class = " Imm";
      break;
    case MAX_LEVEL - 10:
      class = " Imm";
      break;
    case MAX_LEVEL - 11:
      class = " Imm";
      break;
    case MAX_LEVEL - 12:
      class = " Imm";
      break;
    case MAX_LEVEL - 13:
      class = " Imm";
      break;
    case MAX_LEVEL - 14:
      class = " Imm";
      break;
    case MAX_LEVEL - 15:
      class = " AV";
      break;
    }

    switch ( wch->race )
    {
    case 0:
      race = "Human";
      break;
    case 1:
      race = "Elf";
      break;
    case 2:
      race = "Dwarf";
      break;
    case 3:
      race = "Halfling";
      break;
    case 4:
      race = "Pixie";
      break;
    case 5:
      race = "Minotaur";
      break;
    case 6:
      race = "Orc";
      break;
    case 7:
      race = "Ogre";
      break;
    case 8:
      race = "Troll";
      break;
    case 9:
      race = "Giant";
      break;
    case 10:
      race = "Gith";
      break;
    case 11:
      race = "Drow";
      break;
    case 12:
      race = "Dragon";
      break;
    case 13:
      race = "Lizardman";
      break;
    case 14:
      race = "Gnome";
      break;
    }

    if ( !str_cmp( wch->name, sysdata.guild_overseer ) )
      extra_title = " [Overseer of Guilds]";
    else if ( !str_cmp( wch->name, sysdata.guild_advisor ) )
      extra_title = " [Advisor to Guilds]";
    else
      extra_title = "";

    if ( IS_RETIRED( wch ) )
      class = "Retired";
    else if ( IS_GUEST( wch ) )
      class = "Guest";
    else if ( wch->pcdata->clan
              &&  !str_cmp( wch->name, wch->pcdata->clan->leader )
              &&   wch->pcdata->clan->leadrank[0] != '\0' )
      class = wch->pcdata->clan->leadrank;
    else if ( wch->pcdata->clan
              &&  !str_cmp( wch->name, wch->pcdata->clan->number1 )
              &&   wch->pcdata->clan->onerank[0] != '\0' )
      class = wch->pcdata->clan->onerank;
    else if ( wch->pcdata->clan
              &&  !str_cmp( wch->name, wch->pcdata->clan->number2 )
              &&   wch->pcdata->clan->tworank[0] != '\0' )
      class = wch->pcdata->clan->tworank;
    else if ( wch->pcdata->rank && wch->pcdata->rank[0] != '\0' )
      class = wch->pcdata->rank;

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

      if ( pclan->clan_type == CLAN_ORDER )
      {
        if ( !str_cmp( wch->name, pclan->deity ) )
          strcat( clan_name, "Deity, Order of " );
        else
          if ( !str_cmp( wch->name, pclan->leader ) )
            strcat( clan_name, "Leader, Order of " );
          else
            if ( !str_cmp( wch->name, pclan->number1 ) )
              strcat( clan_name, "Number One, Order of " );
            else
              if ( !str_cmp( wch->name, pclan->number2 ) )
                strcat( clan_name, "Number Two, Order of " );
              else
                strcat( clan_name, "Order of " );
      }
      else
        if ( pclan->clan_type == CLAN_GUILD )
        {
          if ( !str_cmp( wch->name, pclan->leader ) )
            strcat( clan_name, "Leader, " );
          if ( !str_cmp( wch->name, pclan->number1 ) )
            strcat( clan_name, "First, " );
          if ( !str_cmp( wch->name, pclan->number2 ) )
            strcat( clan_name, "Second, " );
        }
        else
        {
          if ( !str_cmp( wch->name, pclan->deity ) )
            strcat( clan_name, "Deity of " );
          else
            if ( !str_cmp( wch->name, pclan->leader ) )
              strcat( clan_name, "Leader of " );
            else
              if ( !str_cmp( wch->name, pclan->number1 ) )
                strcat( clan_name, "Number One " );
              else
                if ( !str_cmp( wch->name, pclan->number2 ) )
                  strcat( clan_name, "Number Two " );
        }
      strcat( clan_name, pclan->name );
      if ( pclan->clan_type == CLAN_GUILD )
        strcat( clan_name, ">" );
      else
        strcat( clan_name, ")" );
    }
    else
      clan_name[0] = '\0';

    if ( wch->pcdata->council )
    {
      strcpy( council_name, " [" );
      if (  wch->pcdata->council->head2 == NULL )
      {
        if (!str_cmp (wch->name, wch->pcdata->council->head))
          strcat (council_name, "Head of ");
      }
      else
      {
        if (!str_cmp (wch->name, wch->pcdata->council->head)
            || !str_cmp ( wch->name, wch->pcdata->council->head2) )
          strcat (council_name, "Co-Head of ");
      }
      strcat( council_name, wch->pcdata->council_name );
      strcat( council_name, "]" );
    }
    else
      council_name[0] = '\0';

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


    sprintf( buf, "&B|&p%-3s &RR&w%d&B |&g %-10s&B| &W%-10s&B |&D %-25s&B|&R%-3s%s%s%s%s%s&D\n\r",
             class, //level
             wch->remorts,
             race,
             char_name,
             wch->pcdata->title,
             IS_SET(wch->pcdata->flags, PCFLAG_HELPER) ? "&R[&WHELPER&R]&D " : "",
             (wch->desc && wch->desc->connected) ? "[W] " : "",
             xIS_SET(wch->act, PLR_AFK) ? "[AFK] " : "",
             xIS_SET(wch->act, PLR_ATTACKER) ? "(A) " : "",
             xIS_SET(wch->act, PLR_THIEF)  ? "(T) "  : "",
             invis_str); //robert


    /*
     * 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 ( wch->level > 50 && IS_IMMORTAL( wch ) )
      cur_who->type = WT_IMM;
    else if ( fGroup )
      if ( wch->leader || (whogr_p && whogr_p->indent) )
        cur_who->type = WT_GROUPED;
      else
        cur_who->type = WT_GROUPWHO;
    else if ( CAN_PKILL( wch ) )
      cur_who->type = WT_DEADLY;
    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_DEADLY:
      cur_who->next = first_deadly;
      first_deadly = cur_who;
      break;
    case WT_GROUPED:
      cur_who->next = first_grouped;
      first_grouped = cur_who;
      break;
    case WT_GROUPWHO:
      cur_who->next = first_groupwho;
      first_groupwho = 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.
   */
  /*
   * Two extras now for grouped and groupwho (wanting group). -- Alty
   */
  send_to_pager( "\n\r&O+&B----------------------------------------------------------------------------&O+&B\n\r", ch );
  send_to_pager( "&B|                       +-=[&cPlayers of  Eldhamud&B]=-+                         |\n\r", ch );
  send_to_pager( "&O+&B--------&O+&B-----------&O+&B------------&O+&B--------------------------&O+&B---------------&O+&B\n\r", ch );
  send_to_pager( "| Level  |   Race    |    Name    |          Title           | Flags         |\n\r", ch );
  send_to_pager( "&O+&B--------&O+&B-----------&O+&B------------&O+&B--------------------------&O+&B---------------&O+&B\n\r", ch );

  for ( cur_who = first_mortal; cur_who; cur_who = next_who )
  {
    if ( !ch )
      fprintf( whoout, "%s", cur_who->text );
    else
      send_to_pager( cur_who->text, ch );
    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  if ( first_deadly )
  {
    if ( !ch )
      fprintf( whoout, "%s", "|----------------------------[ DEADLY  CHARACTERS ]------------------------|\n\r" );
    else
      send_to_pager( "&B|        |           |            |                          |               |\n\r", ch );
    send_to_pager( "|--------&O+&B-----------&O+&B------------&O+&B==[ &cDEADLY  CHARACTERS&B ]==&O+&B---------------|\n\r", ch );

  }

  for ( cur_who = first_deadly; cur_who; cur_who = next_who )
  {
    if ( !ch )
      fprintf( whoout, "%s", cur_who->text );
    else
      send_to_pager( cur_who->text, ch );
    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  if (first_grouped)
  {
    if ( !ch )
      fprintf( whoout, "%s", "\n\r-----------------------------[ GROUPED CHARACTERS ]---------------------------\n\r\n\r" );
    else
      send_to_pager( "&B|        |           |            |                          |               |\n\r", ch );
    send_to_pager( "|--------&O+&B-----------&O+&B------------&O+&B==[ &cGROUPED CHARACTERS&B ]==&O+&B---------------|\n\r", ch );
  }
  for ( cur_who = first_grouped; cur_who; cur_who = next_who )
  {
    /*      if ( !ch )
            fprintf( whoout, cur_who->text );
          else*/
    send_to_pager( cur_who->text, ch );
    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  if (first_groupwho)
  {
    if ( !ch )
      fprintf( whoout, "%s", "|------------------------------[ WANTING GROUP ]----------------------------|\n\r" );
    else
      send_to_pager( "*B|        |           |            |                          |               |\n\r", ch );
    send_to_pager( "|--------&O+&B-----------&O+&B------------&O+&B--==[ &cWANTING  GROUP&B ]==--&O+&B---------------|\n\r", ch );


  }
  for ( cur_who = first_groupwho; cur_who; cur_who = next_who )
  {
    if ( !ch )
      fprintf( whoout, "%s", cur_who->text );
    else
      send_to_pager( cur_who->text, ch );
    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  if ( first_imm )
  {
    if ( !ch )
      fprintf( whoout, "%s", "|--------------------------------[ IMMORTALS ]------------------------------|\n\r" );
    else
      send_to_pager( "&B|        |           |            |                          |               |\n\r", ch );
    send_to_pager( "|--------&O+&B-----------&O+&B------------&O+&B----==[ &cIMMORTALS&B  ]==----&O+&B---------------|\n\r", ch );
  }

  for ( cur_who = first_imm; cur_who; cur_who = next_who )
  {
    if ( !ch )
      fprintf( whoout, "%s", cur_who->text );
    else
      send_to_pager_color( cur_who->text, ch );
    next_who = cur_who->next;
    DISPOSE( cur_who->text );
    DISPOSE( cur_who );
  }

  send_to_pager( "&B|        |           |            |                          |               |\n\r", ch );
  send_to_pager( "&O+&B--------&O+&B-----------&O+&B------------&O+&B--------------------------&O+&B---------------&O+&D\n\r", ch );


  if ( !ch )
  {
    fprintf( whoout, "%d player%s.\n\r", nMatch, nMatch == 1 ? "" : "s" );
    fclose( whoout );
    return;
  }

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

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

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

  if ( ( obj1 = get_obj_carry( ch, arg1 ) ) == NULL )
  {
    send_to_char( "You do not have that item.\n\r", 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.\n\r", ch );
      return;
    }
  }
  else
  {
    if ( ( obj2 = get_obj_carry( ch, arg2 ) ) == NULL )
    {
      send_to_char( "You do not have that item.\n\r", ch );
      return;
    }
  }

  msg		= NULL;
  value1	= 0;
  value2	= 0;

  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 );
  return;
}



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

  one_argument( argument, arg );

  if ( arg[0]!='\0'
       &&   (victim=get_char_world(ch, arg)) && !IS_NPC(victim)
       &&   IS_SET(victim->pcdata->flags, PCFLAG_DND)
       &&   get_trust(ch) < get_trust(victim) )
  {
    act( AT_PLAIN, "You didn't find any $T.", ch, NULL, arg, TO_CHAR );
    return;
  }

  set_pager_color( AT_PERSON, ch );
  if ( arg[0] == '\0' )
  {
    pager_printf( ch, "\n\rPlayers near you in %s:\n\r", ch->in_room->area->name );
    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
           &&   victim->in_room->area == ch->in_room->area
           &&   can_see( ch, victim, TRUE )
           && ( get_trust(ch) >= get_trust(victim)
                ||   !IS_SET(victim->pcdata->flags, PCFLAG_DND) )
         ) /* if victim has the DND flag ch must outrank them */

      {
        found = TRUE;
        /*		if ( CAN_PKILL( victim ) )
        		  set_pager_color( AT_PURPLE, ch );
        		else
        		  set_pager_color( AT_PERSON, ch );
        */
        pager_printf_color( ch, "&P%-13s  ", victim->name );
        if ( IS_IMMORTAL( victim ) && victim->level > 50 )
          send_to_pager_color( "&P(&WImmortal&P)\t", ch );
        else if ( CAN_PKILL( victim ) && victim->pcdata->clan
                  && victim->pcdata->clan->clan_type != CLAN_ORDER
                  && victim->pcdata->clan->clan_type != CLAN_GUILD )
          pager_printf_color( ch, "%-18s\t", victim->pcdata->clan->badge );
        else if ( CAN_PKILL( victim ) )
          send_to_pager_color( "(&wUnclanned&P)\t", ch );
        else
          send_to_pager( "\t\t\t", ch );
        pager_printf_color( ch, "&P%s\n\r", victim->in_room->name );
      }
    if ( !found )
      send_to_char( "None\n\r", ch );
  }
  else
  {
    found = FALSE;
    for ( victim = first_char; victim; victim = victim->next )
      if ( victim->in_room
           &&   victim->in_room->area == ch->in_room->area
           &&   !IS_AFFECTED(victim, AFF_HIDE)
           &&   !IS_AFFECTED(victim, AFF_SNEAK)
           &&   can_see( ch, victim, TRUE )
           &&   is_name( arg, victim->name ) )
      {
        found = TRUE;
        pager_printf( ch, "%-28s %s\n\r",
                      PERS(victim, ch, TRUE), victim->in_room->name );
        break;
      }
    if ( !found )
      act( AT_PLAIN, "You didn't find any $T.", ch, NULL, arg, TO_CHAR );
  }

  return;
}




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

  one_argument( argument, arg );

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

  if ( ( victim = get_char_room( ch, arg ) ) == NULL )
  {
    send_to_char( "They're not here.\n\r", ch );
    return;
  }
  if ( victim == ch )
  {
    send_to_char( "You decide you're pretty sure you could take yourself in a fight.\n\r", ch );
    return;
  }
  diff = victim->level - ch->level;

  if ( diff <= -10 )
    msg = "You are far more experienced than $N.";
  else if ( diff <=  -5 )
    msg = "$N is not nearly as experienced as you.";
  else if ( diff <=  -2 )
    msg = "You are more experienced than $N.";
  else if ( diff <=   1 )
    msg = "You are just about as experienced as $N.";
  else if ( diff <=   4 )
    msg = "You are not nearly as experienced as $N.";
  else if ( diff <=   9 )
    msg = "$N is far more experienced than you!";
  else
    msg = "$N would make a great teacher for you!";
  act( AT_CONSIDER, msg, ch, NULL, victim, TO_CHAR );

  diff = (int) (victim->max_hit - ch->max_hit) / 6;

  if ( diff <= -200)
    msg = "$N looks like a feather!";
  else if ( diff <= -150)
    msg = "You could kill $N with your hands tied!";
  else if ( diff <= -100)
    msg = "Hey! Where'd $N go?";
  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 equipment!";
  else if ( diff <=  200)
    msg = "Why don't you dig a grave for yourself first?";
  else
    msg = "$N is built like a TANK!";
  act( AT_CONSIDER, msg, ch, NULL, victim, TO_CHAR );

  return;
}



/*
 * Place any skill types you don't want them to be able to practice
 * normally in this list.  Separate each with a space.
 * (Uses an is_name check). -- Altrag
 */
#define CANT_PRAC "Tongue"

void do_practice( CHAR_DATA *ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  int sn;

  if ( IS_NPC(ch) )
    return;

  if ( argument[0] == '\0' )
  {
    int	col;
    sh_int	lasttype, cnt;

    col = cnt = 0;
    lasttype = SKILL_SPELL;
    set_pager_color( AT_MAGIC, ch );
    for ( sn = 0; sn < top_sn; sn++ )
    {
      if ( !skill_table[sn]->name )
        break;

      if ( strcmp(skill_table[sn]->name, "reserved") == 0
           && ( IS_IMMORTAL(ch) || CAN_CAST(ch) ) )
      {
        if ( col % 3 != 0 )
          send_to_pager( "\n\r", ch );
        //   set_pager_color( AT_DGREY, ch );
        send_to_pager_color(
          " &Y----------------------------------[&CSpells&Y]----------------------------------&D\n\r", ch);
        col = 0;
      }
      if ( skill_table[sn]->type != lasttype )
      {
        if ( !cnt )
          send_to_pager( "                                   (none)\n\r", ch );
        else
          if ( col % 3 != 0 )
            send_to_pager( "&D\n\r", ch );
        //       set_pager_color( AT_CYAN, ch );
        pager_printf_color( ch,
                            " &Y----------------------------------&C%ss&Y----------------------------------\n\r",
                            skill_tname[skill_table[sn]->type]);
        col = cnt = 0;
      }
      lasttype = skill_table[sn]->type;

      if (!IS_IMMORTAL(ch)
          && ( skill_table[sn]->guild != CLASS_NONE
               && ( !IS_GUILDED(ch)
                    || (ch->pcdata->clan->class != skill_table[sn]->guild) ) ) )
        continue;



      if ( ch->level < skill_table[sn]->skill_level[ch->class]
           || (!IS_IMMORTAL(ch) && skill_table[sn]->skill_level[ch->class] == 0) )
        continue;

      if ( ch->pcdata->learned[sn] <= 0   &&   SPELL_FLAG(skill_table[sn], SF_SECRETSKILL) )
        continue;

      ++cnt;
      //    set_pager_color( AT_ORANGE, ch );
      pager_printf( ch, "&c%20.20s", skill_table[sn]->name );
      if ( ch->pcdata->learned[sn] > 0 )
        set_pager_color( AT_YELLOW, ch );
      pager_printf( ch, "&Y %3d%%&D ", ch->pcdata->learned[sn] );
      if ( ++col % 3 == 0 )
        send_to_pager( "\n\r", ch );
    }

    if ( col % 3 != 0 )
      send_to_pager( "&D\n\r", ch );
    set_pager_color( AT_GREEN, ch );
    pager_printf( ch, "You have %d practice sessions left.\n\r",
                  ch->practice );
  }
  else
  {
    CHAR_DATA *mob;
    int adept;
    bool can_prac = TRUE;

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

    for ( mob = ch->in_room->first_person; mob; mob = mob->next_in_room )
      if ( IS_NPC(mob) && xIS_SET(mob->act, ACT_PRACTICE) )
        break;

    if ( !mob )
    {
      send_to_char( "You can't do that here.\n\r", ch );
      return;
    }

    if ( ch->practice <= 0 )
    {
      act( AT_TELL, "$n tells you 'You must earn some more practice sessions.'",
           mob, NULL, ch, TO_VICT );
      return;
    }

    sn = skill_lookup( argument );



    if ( ch->pcdata->learned[sn] <= 0   &&   SPELL_FLAG(skill_table[sn], SF_SECRETSKILL) )
    {
      send_to_char("$n tells you 'You cannot practice that untill someone teaches you...'", ch);
      return;
    }


    if ( can_prac && ( ( sn == -1 ) || ( !IS_NPC(ch) &&  ch->level < skill_table[sn]->skill_level[ch->class])))
    {
      act( AT_TELL, "$n tells you 'You're not ready to learn that yet...'", mob, NULL, ch, TO_VICT );
      return;
    }

    if ( is_name( skill_tname[skill_table[sn]->type], CANT_PRAC ) )
    {
      act( AT_TELL, "$n tells you 'I do not know how to teach that.'",
           mob, NULL, ch, TO_VICT );
      return;
    }

    /*
     * Skill requires a special teacher
     */
    if ( skill_table[sn]->teachers && skill_table[sn]->teachers[0] != '\0' )
    {
      sprintf( buf, "%d", mob->pIndexData->vnum );
      if ( !is_name( buf, skill_table[sn]->teachers ) )
      {
        act( AT_TELL, "$n tells you, 'I know not know how to teach that.'",
             mob, NULL, ch, TO_VICT );
        return;
      }
    }

    /*
     * Guild checks - right now, cant practice guild skills - done on 
     * induct/outcast
     */
    /*
    	if ( !IS_NPC(ch) && !IS_GUILDED(ch)
    	&&    skill_table[sn]->guild != CLASS_NONE)
    	{
    	    act( AT_TELL, "$n tells you 'Only guild members can use that..'"
    		mob, NULL, ch, TO_VICT );
    	    return;
    	}
     
    	if ( !IS_NPC(ch) && skill_table[sn]->guild != CLASS_NONE 
    	     && ch->pcdata->clan->class != skill_table[sn]->guild )
    	{
    	    act( AT_TELL, "$n tells you 'That can not be used by your guild.'"
    		mob, NULL, ch, TO_VICT );
    	    return;
    	}
    */
    if ( !IS_NPC(ch) && skill_table[sn]->guild != CLASS_NONE)
    {
      act( AT_TELL, "$n tells you 'That is only for members of guilds...'",
           mob, NULL, ch, TO_VICT );
      return;
    }

    /*
     * Disabled for now
    if ( mob->level < skill_table[sn]->skill_level[ch->class]
    ||   mob->level < skill_table[sn]->skill_level[mob->class] )
    {
        act( AT_TELL, "$n tells you 'You must seek another to teach you that...'",
    	mob, NULL, ch, TO_VICT );
        return;
    }
     */

    adept = class_table[ch->class]->skill_adept * 0.55;

    if ( ch->pcdata->learned[sn] >= adept )
    {
      act( AT_TELL, "$n, You'll have to practice it on your own now...",
           ch, NULL, ch, TO_CHAR );
      sprintf( buf, "$n tells you, 'I've taught you everything I can about %s.'",
               skill_table[sn]->name );
      act( AT_TELL, buf, mob, NULL, ch, TO_VICT );

    }
    else
    {
      ch->practice--;
      ch->pcdata->learned[sn] += int_app[get_curr_int(ch)].learn;
      act( AT_ACTION, "You practice $T.",
           ch, NULL, skill_table[sn]->name, TO_CHAR );
      act( AT_ACTION, "$n practices $T.",
           ch, NULL, skill_table[sn]->name, TO_ROOM );
      if ( ch->pcdata->learned[sn] >= adept )
      {
        ch->pcdata->learned[sn] = adept;
        act( AT_TELL,
             "$n tells you. 'You'll have to practice it on your own now...'",
             mob, NULL, ch, TO_VICT );
      }
    }
  }
  return;
}


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

  set_char_color( AT_YELLOW, ch );
  one_argument( argument, arg );
  if ( !str_cmp( arg, "max" ) )
  {
    if ( IS_PKILL( ch ) )
      wimpy = (int) ch->max_hit / 2.25;
    else
      wimpy = (int) ch->max_hit / 1.2;
  }
  else
    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.\n\r", ch );
    return;
  }
  if ( IS_PKILL( ch ) && wimpy > (int) ch->max_hit / 2.25 )
  {
    send_to_char( "Such cowardice ill becomes you.\n\r", ch );
    return;
  }
  else if ( wimpy > (int) ch->max_hit / 1.2 )
  {
    send_to_char( "Such cowardice ill becomes you.\n\r", ch );
    return;
  }
  ch->wimpy	= wimpy;
  ch_printf( ch, "Wimpy set to %d hit points.\n\r", wimpy );
  return;
}



void do_password( CHAR_DATA *ch, char *argument )
{
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  char *pArg;
  char *pwdnew;
  char *p;
  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.
   */
  pArg = arg1;
  while ( isspace(*argument) )
    argument++;

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

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

  pArg = arg2;
  while ( isspace(*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 <new> <again>.\n\r", ch );
    send_to_char( "Syntax: password <new> <again>.\n\r", ch );
    return;
  }

  /* This should stop all the mistyped password problems --Shaddai */
  if ( strcmp( arg1, arg2 ))
  {
    send_to_char("Passwords don't match try again.\n\r", ch );
    return;
  }
  if ( strlen(arg2) < 5 )
  {
    send_to_char( "New password must be at least five characters long.\n\r", ch );
    return;
  }

  if (arg1[0] == '!' || arg2[0] == '!')
  {
    send_to_char( "New password cannot begin with the '!' character.",ch);
    return;
  }

  /*
   * No tilde allowed because of player file format.
   */
  pwdnew = smaug_crypt( arg2 );
  for ( p = pwdnew; *p != '\0'; p++ )
  {
    if ( *p == '~' )
    {
      send_to_char(	"New password not acceptable, try again.\n\r", ch );
      return;
    }
  }

  DISPOSE( ch->pcdata->pwd );
  ch->pcdata->pwd = str_dup( pwdnew );
  if ( IS_SET(sysdata.save_flags, SV_PASSCHG) )
    save_char_obj( ch );
  if ( ch->desc && ch->desc->host[0]  != '\0' )
    sprintf(log_buf, "%s changing password from site %s\n", ch->name, ch->desc->host );
  else
    sprintf(log_buf, "%s changing thier password with no descriptor!", ch->name);
  log_string( log_buf );
  send_to_char( "Ok.\n\r", ch );
  return;
}



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

  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( "\n\r", ch );
    }

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


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

  col = 0;
  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 <  LEVEL_HERO
             &&   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( "\n\r", ch );
        }
    if ( col % 6 != 0 )
      send_to_pager( "\n\r", ch );
  }
  else
  {
    found = FALSE;
    for ( hash = 0; hash < 126; hash++ )
      for ( command = command_hash[hash]; command; command = command->next )
        if ( command->level <  LEVEL_HERO
             &&   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( "\n\r", ch );
        }

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

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

  if ( IS_NPC( ch ) )
    return;

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

    /* Channels everyone sees regardless of affiliation --Blodkai */
    send_to_char_color( "\n\r &gPublic channels  (severe penalties for abuse)&G:\n\r  ", ch );
    /*(    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_RACETALK )?
                         " &G+RACETALK" :
                         " &g-racetalk" ); */
    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_GOSSIP )    ?
                     " &G+GOSSIP" 	          :
                     " &g-gossip" );
    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_OOC )   ?
                     " &G+OOC" 	          :
                     " &g-ooc" );


    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_FLAME ) ?
                     " &G+FLAME" 	          :
                     " &g-flame" );

    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_SAYTO ) ?
                     " &G+SAYTO" 	          :
                     " &g-sayto" );

    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_MUSIC )   ?
                     " &G+MUSIC" 	          :
                     " &g-music" );
    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_ASK )     ?
                     " &G+ASK"                 :
                     " &g-ask" );

    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_YELL )    ?
                     " &G+YELL"	          :
                     " &g-yell" );

    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_NEWBIE) ?
                     " &G+NEWBIE"	     	  :
                     " &g-newbie" );

    if ( get_trust( ch ) > 2 && !NOT_AUTHED( ch ) )
      ch_printf_color( ch, "%s", !IS_SET( ch->deaf, CHANNEL_AUCTION ) ?
                       " &G+AUCTION" 	          :
                       " &g-auction" );

    if ( IS_HERO( ch ) )
      ch_printf_color( ch, "%s", !IS_SET( ch->deaf, CHANNEL_AVTALK )  ?
                       " &G+AVATAR"	          :
                       " &g-avatar" );

    /* For organization channels (orders, clans, guilds, councils) */
    send_to_char_color( "\n\r &gPrivate channels (severe penalties for abuse)&G:\n\r ", ch );
    ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_TELLS )   ?
                     " &G+TELLS"               :
                     " &g-tells" );
    ch_printf_color( ch, "%s", !IS_SET( ch->deaf, CHANNEL_WHISPER )   ?
                     " &G+WHISPER"		  :
                     " &g-whisper" );
    if ( !IS_NPC( ch ) && ch->pcdata->clan )
    {
      if ( ch->pcdata->clan->clan_type == CLAN_ORDER )
        send_to_char_color( !IS_SET( ch->deaf, CHANNEL_ORDER ) ?
                            " &G+ORDER"	:	" &g-order", ch );

      else if ( ch->pcdata->clan->clan_type == CLAN_GUILD )
        send_to_char_color( !IS_SET( ch->deaf, CHANNEL_GUILD ) ?
                            " &G+GUILD"	:	" &g-guild", ch );
      else
        send_to_char_color( !IS_SET( ch->deaf, CHANNEL_CLAN )  ?
                            " &G+CLAN"	:	" &g-clan", ch );
    }

    if ( !IS_NPC( ch ) && ch->pcdata->council )
      ch_printf_color( ch, "%s",   !IS_SET( ch->deaf, CHANNEL_COUNCIL)?
                       " &G+COUNCIL"	     	  :
                       " &g-council" );

    /* Immortal channels */
    if ( IS_IMMORTAL( ch ) )
    {
      send_to_char_color( "\n\r &gImmortal Channels&G:\n\r  ", ch );
      send_to_char_color( !IS_SET( ch->deaf, CHANNEL_IMMTALK )    ?
                          " &G+IMMTALK"	:	" &g-immtalk", ch );
      /*          send_to_char_color( !IS_SET( ch->deaf, CHANNEL_PRAY )       ?
      		" &G+PRAY"	:	" &g-pray", ch ); */
      if ( get_trust( ch ) >= sysdata.muse_level )
        send_to_char_color( !IS_SET( ch->deaf, CHANNEL_HIGHGOD )  ?
                            " &G+MUSE"	:	" &g-muse", ch );
      send_to_char_color( !IS_SET( ch->deaf, CHANNEL_MONITOR )    ?
                          " &G+MONITOR"	:	" &g-monitor", ch );
      send_to_char_color( !IS_SET( ch->deaf, CHANNEL_AUTH )	?
                          " &G+AUTH"	:	" &g-auth", ch );
    }
    if ( get_trust( ch ) >= sysdata.log_level )
    {
      send_to_char_color( !IS_SET( ch->deaf, CHANNEL_LOG ) 	?
                          " &G+LOG"	:	" &g-log", ch);
      send_to_char_color( !IS_SET( ch->deaf, CHANNEL_BUILD)       ?
                          " &G+BUILD"	:	" &g-build", ch );
      send_to_char_color( !IS_SET( ch->deaf, CHANNEL_COMM ) 	?
                          " &G+COMM"	:	" &g-comm", ch );
      send_to_char_color( !IS_SET (ch->deaf, CHANNEL_WARN)
                          ? " &G+WARN" : " &g-warn", ch);
      if ( get_trust( ch ) >= sysdata.think_level )
        send_to_char_color( !IS_SET( ch->deaf, CHANNEL_HIGH ) 	?
                            " &G+HIGH"	:	" &g-high", ch );
    }
    send_to_char( "\n\r", ch );
  }
  else
  {
    bool fClear;
    bool ClearAll;
    int bit;

    bit=0;
    ClearAll = FALSE;

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

    if ( !str_cmp( arg+1, "auction"  ) )
      bit = CHANNEL_AUCTION;
    else if ( !str_cmp( arg+1, "traffic"  ) )
      bit = CHANNEL_TRAFFIC;
    else if ( !str_cmp( arg+1, "gossip"     ) )
      bit = CHANNEL_GOSSIP;
    else if ( !str_cmp( arg+1, "clan"     ) )
      bit = CHANNEL_CLAN;
    else if ( !str_cmp( arg+1, "council"  ) )
      bit = CHANNEL_COUNCIL;
    else if ( !str_cmp( arg+1, "guild"    ) )
      bit = CHANNEL_GUILD;
    else if ( !str_cmp( arg+1, "ooc"    ) )
      bit = CHANNEL_OOC;
    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, "high"     ) )
      bit = CHANNEL_HIGH;
    else if ( !str_cmp( arg+1, "pray"     ) )
      bit = CHANNEL_PRAY;
    else if ( !str_cmp( arg+1, "avatar"   ) )
      bit = CHANNEL_AVTALK;
    else if ( !str_cmp( arg+1, "monitor"  ) )
      bit = CHANNEL_MONITOR;
    else if ( !str_cmp( arg+1, "auth"     ) )
      bit = CHANNEL_AUTH;
    else if ( !str_cmp( arg+1, "newbie"   ) )
      bit = CHANNEL_NEWBIE;
    else if ( !str_cmp( arg+1, "music"    ) )
      bit = CHANNEL_MUSIC;
    else if ( !str_cmp( arg+1, "muse"     ) )
      bit = CHANNEL_HIGHGOD;
    else if ( !str_cmp( arg+1, "ask"      ) )
      bit = CHANNEL_ASK;
    else if ( !str_cmp( arg+1, "shout"    ) )
      bit = CHANNEL_SHOUT;
    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, "warn"     ) )
      bit = CHANNEL_WARN;
    else if ( !str_cmp( arg+1, "order"    ) )
      bit = CHANNEL_ORDER;
    else if ( !str_cmp( arg+1, "flame"  ) )
      bit = CHANNEL_FLAME;
    else if ( !str_cmp( arg+1, "sayto"  ) )
      bit = CHANNEL_SAYTO;
    else if ( !str_cmp( arg+1, "whisper"  ) )
      bit = CHANNEL_WHISPER;
    else if ( !str_cmp( arg+1, "racetalk" ) )
      bit = CHANNEL_RACETALK;
    else if ( !str_cmp( arg+1, "all"      ) )
      ClearAll = TRUE;
    else
    {
      send_to_char( "Set or clear which channel?\n\r", ch );
      return;
    }

    if (( fClear ) && ( ClearAll ))
    {
      REMOVE_BIT (ch->deaf, CHANNEL_RACETALK);
      REMOVE_BIT (ch->deaf, CHANNEL_AUCTION);
      REMOVE_BIT (ch->deaf, CHANNEL_GOSSIP);
      REMOVE_BIT (ch->deaf, CHANNEL_OOC);
      REMOVE_BIT (ch->deaf, CHANNEL_FLAME);
      REMOVE_BIT (ch->deaf, CHANNEL_PRAY);
      REMOVE_BIT (ch->deaf, CHANNEL_TRAFFIC);
      REMOVE_BIT (ch->deaf, CHANNEL_MUSIC);
      REMOVE_BIT (ch->deaf, CHANNEL_ASK);
      REMOVE_BIT (ch->deaf, CHANNEL_SHOUT);
      REMOVE_BIT (ch->deaf, CHANNEL_YELL);

      /*     if (ch->pcdata->clan)
             REMOVE_BIT (ch->deaf, CHANNEL_CLAN);

      if (ch->pcdata->council)
      REMOVE_BIT (ch->deaf, CHANNEL_COUNCIL);

           if (ch->pcdata->guild)
             REMOVE_BIT (ch->deaf, CHANNEL_GUILD);
      */
      if (ch->level >= LEVEL_IMMORTAL)
        REMOVE_BIT (ch->deaf, CHANNEL_AVTALK);

      /*
      if (ch->level >= sysdata.log_level )
        REMOVE_BIT (ch->deaf, CHANNEL_COMM);
      */

    }
    else if ((!fClear) && (ClearAll))
    {
      SET_BIT (ch->deaf, CHANNEL_RACETALK);
      SET_BIT (ch->deaf, CHANNEL_AUCTION);
      SET_BIT (ch->deaf, CHANNEL_TRAFFIC);
      SET_BIT (ch->deaf, CHANNEL_GOSSIP);
      SET_BIT (ch->deaf, CHANNEL_OOC);
      SET_BIT (ch->deaf, CHANNEL_PRAY);
      SET_BIT (ch->deaf, CHANNEL_MUSIC);
      SET_BIT (ch->deaf, CHANNEL_ASK);
      SET_BIT (ch->deaf, CHANNEL_SHOUT);
      SET_BIT (ch->deaf, CHANNEL_FLAME);
      SET_BIT (ch->deaf, CHANNEL_YELL);

      /*     if (ch->pcdata->clan)
             SET_BIT (ch->deaf, CHANNEL_CLAN);

      if (ch->pcdata->council)
      SET_BIT (ch->deaf, CHANNEL_COUNCIL);

           if ( IS_GUILDED(ch) )
             SET_BIT (ch->deaf, CHANNEL_GUILD);
      */
      if (ch->level >= LEVEL_IMMORTAL)
        SET_BIT (ch->deaf, CHANNEL_AVTALK);

      /*
      if (ch->level >= sysdata.log_level)
        SET_BIT (ch->deaf, CHANNEL_COMM);
      */

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

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

  return;
}


/*
 * display WIZLIST file						-Thoric
 */
void do_wizlist( CHAR_DATA *ch, char *argument )
{
  set_pager_color( AT_IMMORT, ch );
  show_file( ch, WIZLIST_FILE );
}

/*
 * Contributed by Grodyn.
 * Display completely overhauled, 2/97 -- Blodkai
 */
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_GREEN, ch );

  if ( arg[0] == '\0' )
  {
    set_char_color( AT_DGREEN, ch );
    send_to_char( "\n\rConfigurations ", ch );
    set_char_color( AT_GREEN, ch );
    send_to_char( "(use 'config +/- <keyword>' to toggle, see 'help config')\n\r\n\r", ch );
    set_char_color( AT_DGREEN, ch );
    send_to_char( "Display:   ", ch );
    set_char_color( AT_GREY, ch );
    ch_printf( ch, "%-12s   %-12s   %-12s   %-12s\n\r           %-12s   %-12s   %-12s   %-12s\n\r           %-12s   %-12s", //  %-12s
               IS_SET( ch->pcdata->flags, PCFLAG_PAGERON ) 	? "[+] PAGER"
               : "[-] pager",
               IS_SET( ch->pcdata->flags, PCFLAG_GAG )     	? "[+] GAG"
               : "[-] gag",
               xIS_SET( ch->act, PLR_BRIEF )                	? "[+] BRIEF"
               : "[-] brief",
               xIS_SET( ch->act, PLR_COMBINE )              	? "[+] COMBINE"
               : "[-] combine",
               xIS_SET( ch->act, PLR_BLANK )                	? "[+] BLANK"
               : "[-] blank",
               xIS_SET( ch->act, PLR_PROMPT )               	? "[+] PROMPT"
               : "[-] prompt",
               xIS_SET( ch->act, PLR_ANSI )                 	? "[+] ANSI"
               : "[-] ansi",
               xIS_SET( ch->act, PLR_RIP )                  	? "[+] RIP"
               : "[-] rip",
               xIS_SET( ch->act, PLR_COMPASS )			? "[+] COMPASS"
               : "[-] compass",
               xIS_SET( ch->act, PLR_AUTOMAP)			? "[+] MAP"
               : "[-] map" );

    set_char_color( AT_DGREEN, ch );
    send_to_char( "\n\r\n\rAuto:      ", ch );
    set_char_color( AT_GREY, ch );
    ch_printf( ch, "%-12s   %-12s   %-12s   %-12s",
               xIS_SET(ch->act, PLR_AUTOSAC  )             	? "[+] AUTOSAC"
               : "[-] autosac",
               xIS_SET(ch->act, PLR_AUTOGOLD )             	? "[+] AUTOGOLD"
               : "[-] autogold",
               xIS_SET(ch->act, PLR_AUTOLOOT )             	? "[+] AUTOLOOT"
               : "[-] autoloot",
               xIS_SET(ch->act, PLR_AUTOEXIT )             	? "[+] AUTOEXIT"
               : "[-] autoexit" );

    set_char_color( AT_DGREEN, ch );
    send_to_char( "\n\r\n\rSafeties:  ", ch );
    set_char_color( AT_GREY, ch );
    ch_printf( ch, "%-12s   %-12s",
               IS_SET( ch->pcdata->flags, PCFLAG_NORECALL ) 	? "[+] NORECALL"
               : "[-] norecall",
               IS_SET( ch->pcdata->flags, PCFLAG_NOSUMMON ) 	? "[+] NOSUMMON"
               : "[-] nosummon" );

    if ( !IS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) )
      ch_printf( ch, "   %-12s   %-12s",
                 xIS_SET(ch->act, PLR_SHOVEDRAG )             ? "[+] DRAG"
                 : "[-] drag",
                 xIS_SET(ch->act, PLR_NICE )               	? "[+] NICE"
                 : "[-] nice" );

    set_char_color( AT_DGREEN, ch );
    send_to_char( "\n\r\n\rMisc:      ", ch );
    set_char_color( AT_GREY, ch );
    ch_printf( ch, "%-12s   %-12s   %-12s",
               xIS_SET(ch->act, PLR_TELNET_GA )		? "[+] TELNETGA"
               : "[-] telnetga",
               IS_SET( ch->pcdata->flags, PCFLAG_GROUPWHO ) ? "[+] GROUPWHO"
               : "[-] groupwho",
               IS_SET( ch->pcdata->flags, PCFLAG_NOINTRO )  ? "[+] NOINTRO"
               : "[-] nointro" );

    set_char_color( AT_DGREEN, ch );
    send_to_char( "\n\r\n\rSettings:  ", ch );
    set_char_color( AT_GREY, ch );
    ch_printf_color( ch, "Pager Length (%d)    Wimpy (&W%d&w)",
                     ch->pcdata->pagerlen,
                     ch->wimpy );
    /* Config option for Room Flag display added by Samson 12-10-97 */
    /* Config option for Sector Type display added by Samson 12-10-97 */
    /* Config option Area name and filename added by Samson 12-13-97 */
    // changed how all this was setup sept 2005 tommi
    if ( IS_IMMORTAL( ch ) )
    {
      set_char_color( AT_DGREEN, ch );
      send_to_char( "\n\r\n\rToggles:  ", ch );
      set_char_color( AT_GREY, ch );
      ch_printf( ch, "%-12s   %-12s   %-12s   %-12s\n\r",
                 xIS_SET(ch->act, PLR_ROOMVNUM ) 		? "[+] VNUM"
                 : "[-] vnum",
                 IS_SET(ch->pcdata->flags, PCFLAG_AUTOFLAGS) ? "[+] ROOMFLAGS"
                 : "[-] roomflags",
                 IS_SET(ch->pcdata->flags, PCFLAG_SECTORD)   ? "[+] SECTORTYPE"
                 : "[-] sectortype ",
                 IS_SET(ch->pcdata->flags, PCFLAG_ANAME)     ? "[+] FILENAME"
                 : "[-] filename ");
    }

    set_char_color( AT_DGREEN, ch );
    send_to_char( "\n\r\n\rSentences imposed on you (if any):", ch );
    set_char_color( AT_YELLOW, ch );
    ch_printf( ch, "\n\r%s%s%s%s%s%s",
               xIS_SET(ch->act, PLR_SILENCE )  ?
               " For your abuse of channels, you are currently silenced.\n\r" : "",
               xIS_SET(ch->act, PLR_NO_EMOTE ) ?
               " The gods have removed your emotes.\n\r"                      : "",
               xIS_SET(ch->act, PLR_NO_TELL )  ?
               " You are not permitted to send 'tells' to others.\n\r"        : "",
               xIS_SET(ch->act, PLR_LITTERBUG )?
               " A convicted litterbug.  You cannot drop anything.\n\r"       : "",
               xIS_SET(ch->act, PLR_THIEF )    ?
               " A proven thief, you will be hunted by the authorities.\n\r"  : "",
               xIS_SET(ch->act, PLR_KILLER )   ?
               " For the crime of murder you are sentenced to death...\n\r"   : "" );
  }
  else
  {
    bool fSet;
    int bit = 0;

    if ( arg[0] == '+' )
      fSet = TRUE;
    else if ( arg[0] == '-' )
      fSet = FALSE;
    else
    {
      send_to_char( "Config -option or +option?\n\r", 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, "autogold" ) )
      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, "rip"      ) )
      bit = PLR_RIP;
    else if ( !str_prefix( arg+1, "compass"  ) )
      bit = PLR_COMPASS;
    else if (!str_prefix( arg+1,  "map"      ) )
      bit = PLR_AUTOMAP;     /* maps */
    else if ( !str_prefix( arg+1, "nice"     ) )
      bit = PLR_NICE;
    else if ( !str_prefix( arg+1, "drag"     ) )
      bit = PLR_SHOVEDRAG;
    else if ( IS_IMMORTAL( ch )
              &&   !str_prefix( arg+1, "vnum"     ) )
      bit = PLR_ROOMVNUM;


    if (bit)
    {
      if ( (bit == PLR_FLEE || bit == PLR_NICE || bit == PLR_SHOVEDRAG)
           &&  IS_SET( ch->pcdata->flags, PCFLAG_DEADLY ) )
      {
        send_to_char( "Pkill characters can not config that option.\n\r", ch );
        return;
      }

      if ( fSet )
        xSET_BIT   (ch->act, bit);
      else
        xREMOVE_BIT(ch->act, bit);
      send_to_char( "Ok.\n\r", 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 ( IS_IMMORTAL ( ch )
                && ( !str_prefix( arg+1, "roomflags" ) ) )
        bit = PCFLAG_AUTOFLAGS;
      else if ( IS_IMMORTAL ( ch )
                && ( !str_prefix( arg+1, "sectortypes" ) ) )
        bit = PCFLAG_SECTORD;
      else if ( IS_IMMORTAL ( ch )
                && ( !str_prefix( arg+1, "filename" ) ) )
        bit = PCFLAG_ANAME;

      else if ( !str_prefix( arg+1, "groupwho" ) )
        bit = PCFLAG_GROUPWHO;
      else if ( !str_prefix( arg+1, "@hgflag_" ) )
        bit = PCFLAG_HIGHGAG;
      else
      {
        send_to_char( "Config which option?\n\r", ch );
        return;
      }

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

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

  return;
}


void do_credits( CHAR_DATA *ch, char *argument )
{
  do_help( ch, "credits" );
}


extern int top_area;

/*
 * New do_areas, written by Fireblade, last modified - 4/27/97
 *
 *   Syntax: area            ->      lists areas in alphanumeric order
 *           area <a>        ->      lists areas with soft max less than
 *                                                    parameter a
 *           area <a> <b>    ->      lists areas with soft max bewteen
 *                                                    numbers a and b
 *           area old        ->      list areas in order loaded
 *
 */
void do_areas( CHAR_DATA *ch, char *argument )
{
  char *header_string1 = "\n\r|  Author       |             Area"
                         "                     | "
                         "Recommended |  Enforced |\n\r";
  char *header_string2 = "----------------+-----------------"
                         "-------------------+----"
                         "---------+------------\n\r";
  char *print_string = "| %-13s | %-34s | %4d - %-4d | %3d - "
                       "%-3d |\n\r";

  AREA_DATA *pArea;
  int lower_bound = 0;
  int upper_bound = MAX_LEVEL + 1;
  /* make sure is to init. > max area level */
  char arg[MAX_STRING_LENGTH];

  argument = one_argument(argument,arg);

  if(arg[0] != '\0')
  {
    if(!is_number(arg))
    {
      if(!strcmp(arg,"old"))
      {
        set_pager_color( AT_PLAIN, ch );
        send_to_pager(header_string1, ch);
        send_to_pager(header_string2, ch);
        for (pArea = first_area; pArea;pArea = pArea->next)
        {
          pager_printf(ch, print_string,
                       pArea->author, pArea->name,
                       pArea->low_soft_range,
                       pArea->hi_soft_range,
                       pArea->low_hard_range,
                       pArea->hi_hard_range);
        }
        return;
      }
      else
      {
        send_to_char("Area may only be followed by numbers, or 'old'.\n\r", ch);
        return;
      }
    }

    upper_bound = atoi(arg);
    lower_bound = upper_bound;

    argument = one_argument(argument,arg);

    if(arg[0] != '\0')
    {
      if(!is_number(arg))
      {
        send_to_char("Area may only be followed by numbers.\n\r", ch);
        return;
      }

      upper_bound = atoi(arg);

      argument = one_argument(argument,arg);
      if(arg[0] != '\0')
      {
        send_to_char("Only two level numbers allowed.\n\r",ch);
        return;
      }
    }
  }

  if(lower_bound > upper_bound)
  {
    int swap = lower_bound;
    lower_bound = upper_bound;
    upper_bound = swap;
  }

  set_pager_color( AT_PLAIN, ch );
  send_to_char("----------------+--------------------------------------+-------------+------------", ch);
  send_to_pager(header_string1, ch);
  send_to_pager(header_string2, ch);

  for (pArea = first_area_name; pArea; pArea = pArea->next_sort_name)
  {
    if (pArea->hi_soft_range >= lower_bound
        &&  pArea->low_soft_range <= upper_bound)
    {
      pager_printf(ch, print_string,
                   pArea->author, pArea->name,
                   pArea->low_soft_range,
                   pArea->hi_soft_range,
                   pArea->low_hard_range,
                   pArea->hi_hard_range);
    }
  }
  send_to_char("----------------+--------------------------------------+-------------+------------\n\r", ch);
  return;
}

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

  if xIS_SET(ch->act, PLR_AFK)
  {
    xREMOVE_BIT(ch->act, PLR_AFK);
    send_to_char( "You are no longer afk.\n\r", ch );
    /*	act(AT_GREY,"$n is no longer afk.", ch, NULL, NULL, TO_ROOM);*/
    act(AT_GREY,"$n is no longer afk.", ch, NULL, NULL, TO_CANSEE);
  }
  else
  {
    xSET_BIT(ch->act, PLR_AFK);
    send_to_char( "You are now afk.\n\r", ch );
    /*	act(AT_GREY,"$n is now afk.", ch, NULL, NULL, TO_ROOM);*/
    act(AT_GREY,"$n is now afk.", ch, NULL, NULL, TO_CANSEE);
    return;
  }

}

void do_slist( CHAR_DATA *ch, char *argument )
{
  int sn, i, lFound;
  char skn[MAX_INPUT_LENGTH];
  char buf[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];
  int lowlev, hilev;
  sh_int lasttype = SKILL_SPELL;

  if ( IS_NPC(ch) )
    return;

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

  lowlev=1;
  hilev=100;

  if (arg1[0]!='\0')
    lowlev=atoi(arg1);

  if ((lowlev<1) || (lowlev>LEVEL_IMMORTAL))
    lowlev=1;

  if (arg2[0]!='\0')
    hilev=atoi(arg2);

  if ((hilev<0) || (hilev>=LEVEL_IMMORTAL))
    hilev=LEVEL_HERO;

  if(lowlev>hilev)
    lowlev=hilev;

  set_pager_color( AT_MAGIC, ch );
  send_to_pager("SPELL & SKILL LIST\n\r",ch);
  send_to_pager("------------------\n\r",ch);

  for (i=lowlev; i <= hilev; i++)
  {
    lFound= 0;
    sprintf(skn,"Spell");
    for ( sn = 0; sn < top_sn; sn++ )
    {
      if ( !skill_table[sn]->name )
        break;

      if ( skill_table[sn]->type != lasttype )
      {
        lasttype = skill_table[sn]->type;
        strcpy( skn, skill_tname[lasttype] );
      }

      if ( ch->pcdata->learned[sn] <= 0
           &&   SPELL_FLAG(skill_table[sn], SF_SECRETSKILL) )
        continue;

      if(i==skill_table[sn]->skill_level[ch->class]  )
      {
        if( !lFound )
        {
          lFound=1;
          pager_printf( ch, "Level %d\n\r", i );
        }
        switch (skill_table[sn]->minimum_position)
        {
        default:
          sprintf(buf, "Invalid");
          bug( "do_slist: skill with invalid minpos, skill=%s",
               skill_table[sn]->name );
          break;
        case POS_DEAD:
          sprintf(buf, "any");
          break;
        case POS_MORTAL:
          sprintf(buf, "mortally wounded");
          break;
        case POS_INCAP:
          sprintf(buf, "incapacitated");
          break;
        case POS_STUNNED:
          sprintf(buf, "stunned");
          break;
        case POS_SLEEPING:
          sprintf(buf, "sleeping");
          break;
        case POS_RESTING:
          sprintf(buf, "resting");
          break;
        case POS_STANDING:
          sprintf(buf, "standing");
          break;
        case POS_FIGHTING:
          sprintf(buf, "fighting");
          break;
        case POS_EVASIVE:
          sprintf(buf, "fighting (evasive)");   /* Fighting style support -haus */
          break;
        case POS_DEFENSIVE:
          sprintf(buf, "fighting (defensive)");
          break;
        case POS_AGGRESSIVE:
          sprintf(buf, "fighting (aggressive)");
          break;
        case POS_BERSERK:
          sprintf(buf, "fighting (berserk)");
          break;
        case POS_MOUNTED:
          sprintf(buf, "mounted");
          break;
        case POS_SITTING:
          sprintf(buf, "sitting");
          break;
        }

        pager_printf(ch, "%7s: %20.20s \t Current: %-3d Max: %-3d  MinPos: %s \n\r",
                     skn, skill_table[sn]->name,
                     ch->pcdata->learned[sn],
                     skill_table[sn]->skill_adept[ch->class],
                     buf );
      }
    }
  }
  return;
}

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_pager("You must input the name of an online character.\n\r", ch);
    return;
  }

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

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

  set_pager_color( AT_GREY, ch );
  pager_printf(ch, "\n\r'%s%s.'\n\r %s is a %s level %d %s %s, %d years of age.\n\r",
               victim->name,
               victim->pcdata->title,
               victim->sex == SEX_MALE ? "He" :
               victim->sex == SEX_FEMALE ? "She" : "It",
               victim->sex == SEX_MALE ? "male" :
               victim->sex == SEX_FEMALE ? "female" : "neutral",
               victim->level,
               capitalize(race_table[victim->race]->race_name),
               class_table[victim->class]->who_name,
               get_age(victim) );

  pager_printf(ch, " %s is a %sdeadly player",
               victim->sex == SEX_MALE ? "He" :
               victim->sex == SEX_FEMALE ? "She" : "It",
               IS_SET(victim->pcdata->flags, PCFLAG_DEADLY) ? "" : "non-");

  if ( victim->pcdata->clan )
  {
    if ( victim->pcdata->clan->clan_type == CLAN_ORDER )
      send_to_pager( ", and belongs to the Order ", ch );
    else
      if ( victim->pcdata->clan->clan_type == CLAN_GUILD )
        send_to_pager( ", and belongs to the ", ch );
      else
        send_to_pager( ", and belongs to Clan ", ch );
    send_to_pager( victim->pcdata->clan->name, ch );
  }
  send_to_pager( ".\n\r", ch );

  if(victim->pcdata->council)
    pager_printf(ch, " %s holds a seat on:  %s\n\r",
                 victim->sex == SEX_MALE ? "He" :
                 victim->sex == SEX_FEMALE ? "She" : "It",
                 victim->pcdata->council->name );

  if(victim->pcdata->deity)
    pager_printf(ch, " %s has found succor in the deity %s.\n\r",
                 victim->sex == SEX_MALE ? "He" :
                 victim->sex == SEX_FEMALE ? "She" : "It",
                 victim->pcdata->deity->name);

  if(victim->pcdata->homepage && victim->pcdata->homepage[0] != '\0')
    pager_printf(ch, " %s homepage can be found at %s\n\r",
                 victim->sex == SEX_MALE ? "His" :
                 victim->sex == SEX_FEMALE ? "Her" : "Its",
                 show_tilde( victim->pcdata->homepage ) );

  if(victim->pcdata->bio && victim->pcdata->bio[0] != '\0')
    pager_printf(ch, " %s's personal bio:\n\r%s",
                 victim->name,
                 victim->pcdata->bio);
  else
    pager_printf(ch, " %s has yet to create a bio.\n\r",
                 victim->name );

  if(IS_IMMORTAL(ch))
  {
    send_to_pager("-------------------\n\r", ch);
    send_to_pager("Info for immortals:\n\r", ch);
    if ( mip_enabled( victim ) )
      pager_printf( ch, "%s's client is: %s (sec_code: %s)\n\r",
                    victim->name, victim->pcdata->mip_ver, victim->pcdata->sec_code );

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

    pager_printf(ch, "%s has killed %d mobiles, and been killed by a mobile %d times.\n\r",
                 victim->name, victim->pcdata->mkills, victim->pcdata->mdeaths );
    if ( victim->pcdata->pkills || victim->pcdata->pdeaths )
      pager_printf(ch, "%s has killed %d players, and been killed by a player %d times.\n\r",
                   victim->name, victim->pcdata->pkills, victim->pcdata->pdeaths );
    if ( victim->pcdata->illegal_pk )
      pager_printf(ch, "%s has committed %d illegal player kills.\n\r",
                   victim->name, victim->pcdata->illegal_pk );

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

    if (victim->pcdata->nuisance )
    {
      pager_printf_color( ch, "&RNuisance   &cStage: (&R%d&c/%d)  Power:  &w%d  &cTime:  &w%s.\n\r", victim->pcdata->nuisance->flags,
                          MAX_NUISANCE_STAGE, victim->pcdata->nuisance->power,
                          ctime(&victim->pcdata->nuisance->time));
    }
    if(victim->pcdata->release_date != 0)
      pager_printf(ch, "%s was helled by %s, and will be released on %24.24s.\n\r",
                   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))
    {
      sprintf(buf2, "list %s", buf);
      do_comment(ch, buf2);
    }

    if(xIS_SET(victim->act, PLR_SILENCE) || xIS_SET(victim->act, PLR_NO_EMOTE)
        || xIS_SET(victim->act, PLR_NO_TELL) || xIS_SET(victim->act, PLR_THIEF)
        || xIS_SET(victim->act, PLR_KILLER) )
    {
      sprintf(buf2, "This player has the following flags set:");
      if(xIS_SET(victim->act, PLR_SILENCE))
        strcat(buf2, " silence");
      if(xIS_SET(victim->act, PLR_NO_EMOTE))
        strcat(buf2, " noemote");
      if(xIS_SET(victim->act, PLR_NO_TELL) )
        strcat(buf2, " notell");
      if(xIS_SET(victim->act, PLR_THIEF) )
        strcat(buf2, " thief");
      if(xIS_SET(victim->act, PLR_KILLER) )
        strcat(buf2, " killer");
      strcat(buf2, ".\n\r");
      send_to_pager(buf2, ch);
    }
    if ( victim->desc && victim->desc->host[0]!='\0' )   /* added by Gorog */
    {
      sprintf (buf2, "%s's IP info: %s ", victim->name, victim->desc->host);
      strcat (buf2, "\n\r");
      send_to_pager(buf2, ch);
    }
  }
}

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

  if ( IS_NPC(ch) )
    return;
  set_char_color( AT_NOTE, ch );
  argument = one_argument(argument, arg);
  if ( !*arg )
  {
    if ( IS_SET(ch->pcdata->flags, PCFLAG_PAGERON) )
    {
      send_to_char( "Pager disabled.\n\r", ch );
      do_config(ch, "-pager");
    }
    else
    {
      ch_printf( ch, "Pager is now enabled at %d lines.\n\r", ch->pcdata->pagerlen );
      do_config(ch, "+pager");
    }
    return;
  }
  if ( !is_number(arg) )
  {
    send_to_char( "Set page pausing to how many lines?\n\r", 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.\n\r", ch->pcdata->pagerlen );
  return;
}

/*
 * The ignore command allows players to ignore up to MAX_IGN
 * other players. Players may ignore other characters whether
 * they are online or not. This is to prevent people from
 * spamming someone and then logging off quickly to evade
 * being ignored.
 * Syntax:
 *	ignore		-	lists players currently ignored
 *	ignore none	-	sets it so no players are ignored
 *	ignore <player>	-	start ignoring player if not already
 *				ignored otherwise stop ignoring player
 *	ignore reply	-	start ignoring last player to send a
 *				tell to ch, to deal with invis spammers
 * Last Modified: June 26, 1997
 * - Fireblade
 */
void do_ignore(CHAR_DATA *ch, char *argument)
{
  char arg[MAX_INPUT_LENGTH];
  IGNORE_DATA *temp, *next;
  char fname[1024];
  struct stat fst;
  CHAR_DATA *victim;

  if(IS_NPC(ch))
    return;

  argument = one_argument(argument, arg);

  sprintf(fname, "%s%c/%s", PLAYER_DIR,
          tolower(arg[0]), capitalize(arg));

  victim = NULL;

  /* If no arguements, then list players currently ignored */
  if(arg[0] == '\0')
  {
    set_char_color(AT_DIVIDER, ch);
    ch_printf(ch, "\n\r----------------------------------------\n\r");
    set_char_color(AT_DGREEN, ch);
    ch_printf(ch, "You are currently ignoring:\n\r");
    set_char_color(AT_DIVIDER, ch);
    ch_printf(ch, "----------------------------------------\n\r");
    set_char_color(AT_IGNORE, ch);

    if(!ch->pcdata->first_ignored)
    {
      ch_printf(ch, "\t    no one\n\r");
      return;
    }

    for(temp = ch->pcdata->first_ignored; temp;
        temp = temp->next)
    {
      ch_printf(ch,"\t  - %s\n\r",temp->name);
    }

    return;
  }
  /* Clear players ignored if given arg "none" */
  else if(!strcmp(arg, "none"))
  {
    for(temp = ch->pcdata->first_ignored; temp; temp = next)
    {
      next = temp->next;
      UNLINK(temp, ch->pcdata->first_ignored,
             ch->pcdata->last_ignored,
             next, prev);
      STRFREE(temp->name);
      DISPOSE(temp);
    }

    set_char_color(AT_IGNORE, ch);
    ch_printf(ch, "You now ignore no one.\n\r");

    return;
  }
  /* Prevent someone from ignoring themself... */
  else if(!strcmp(arg, "self") || nifty_is_name(arg, ch->name))
  {
    set_char_color(AT_IGNORE, ch);
    ch_printf(ch, "Did you type something?\n\r");
    return;
  }
  else
  {
    int i;

    /* get the name of the char who last sent tell to ch */
    if(!strcmp(arg, "reply"))
    {
      if(!ch->reply)
      {
        set_char_color(AT_IGNORE, ch);
        ch_printf(ch, "They're not here.\n\r");
        return;
      }
      else
      {
        strcpy(arg, ch->reply->name);
      }
    }

    /* Loop through the linked list of ignored players */
    /* 	keep track of how many are being ignored     */
    for(temp = ch->pcdata->first_ignored, i = 0; temp;
        temp = temp->next, i++)
    {
      /* If the argument matches a name in list remove it */
      if(!strcmp(temp->name, capitalize(arg)))
      {
        UNLINK(temp, ch->pcdata->first_ignored,
               ch->pcdata->last_ignored,
               next, prev);
        set_char_color(AT_IGNORE, ch);
        ch_printf(ch,"You no longer ignore %s.\n\r",
                  temp->name);
        STRFREE(temp->name);
        DISPOSE(temp);
        return;
      }
    }

    /* if there wasn't a match check to see if the name   */
    /* is valid. This if-statement may seem like overkill */
    /* but it is intended to prevent people from doing the*/
    /* spam and log thing while still allowing ya to      */
    /* ignore new chars without pfiles yet...             */
    if( stat(fname, &fst) == -1 &&
        (!(victim = get_char_world(ch, arg)) ||
         IS_NPC(victim) ||
         strcmp(capitalize(arg),victim->name) != 0))
    {
      set_char_color(AT_IGNORE, ch);
      ch_printf(ch,"No player exists by that"
                " name.\n\r");
      return;
    }

    if(victim)
    {
      strcpy(capitalize(arg),victim->name);
    }

    if (!check_parse_name( capitalize(arg), TRUE ))
    {
      set_char_color( AT_IGNORE, ch );
      send_to_char( "No player exists by that name.\n\r", ch );
      return;
    }

    /* If its valid and the list size limit has not been */
    /* reached create a node and at it to the list	     */
    if(i < MAX_IGN)
    {
      IGNORE_DATA *new;
      CREATE(new, IGNORE_DATA, 1);
      new->name = STRALLOC(capitalize(arg));
      new->next = NULL;
      new->prev = NULL;
      LINK(new, ch->pcdata->first_ignored,
           ch->pcdata->last_ignored, next, prev);
      set_char_color(AT_IGNORE, ch);
      ch_printf(ch,"You now ignore %s.\n\r", new->name);
      return;
    }
    else
    {
      set_char_color(AT_IGNORE, ch);
      ch_printf(ch,"You may only ignore %d players.\n\r",
                MAX_IGN);
      return;
    }
  }
}

/*
 * This function simply checks to see if ch is ignoring ign_ch.
 * Last Modified: October 10, 1997
 * - Fireblade
 */
bool is_ignoring(CHAR_DATA *ch, CHAR_DATA *ign_ch)
{
  IGNORE_DATA *temp;

  if(IS_NPC(ch) || IS_NPC(ign_ch))
    return FALSE;

  for(temp = ch->pcdata->first_ignored; temp; temp = temp->next)
  {
    if(nifty_is_name(temp->name, ign_ch->name))
      return TRUE;
  }

  return FALSE;
}

/* Version info -- Scryn */

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

  set_char_color(AT_YELLOW, ch);
  ch_printf(ch, "SMAUG %s.%s\n\r", SMAUG_VERSION_MAJOR, SMAUG_VERSION_MINOR);

  if(IS_IMMORTAL(ch))
    ch_printf(ch, "Compiled on %s at %s.\n\r", __DATE__, __TIME__);

  return;
}

/***************************************************************************
*     Shows mana and blood (if vampire) requirements to cast a spell.      *
*          Created by Desden, el Chaman Tibetano - Jun 1999                *
* Snippets page: http://luisso.net/smaug_snippets.htm *
*                Email: jose@luisso.net                          *
***************************************************************************/
void do_mana( CHAR_DATA *ch, char *argument)
{
  SKILLTYPE *skill=NULL;
  char arg1[MAX_INPUT_LENGTH];
  int sn;
  int col = 0;

  argument=one_argument(argument, arg1);

  if (IS_NPC(ch))
  {
    send_to_char("Mobs cannot use this command.\n\r", ch);
    return;
  }

  if (arg1[0]=='\0')
  {
    if(IS_VAMPIRE(ch))
      send_to_char("Syntax: blood all\n\r        blood <spell>\n\r",ch);
    else
      send_to_char("Syntax: mana all\n\r        mana <spell>\n\r",ch);
    return;
  }

  if(!strcmp(arg1,"all"))
  {
    set_pager_color(AT_YELLOW,ch);

    if(IS_VAMPIRE(ch))
      send_to_pager("                         BLOOD REQUIRED TO CAST\n\r", ch);
    else
      send_to_pager("                         MANA REQUIRED TO CAST\n\r", ch);

    send_to_pager("                        -----------------------\n\r", ch);

    for ( sn = 0; sn < top_sn ; sn++ )
    {
      skill=get_skilltype(sn);
      if(ch->pcdata->learned[sn] < 1 ||
          !skill->name || !skill->min_mana )
        continue;

      if(ch->level>=skill->skill_level[ch->class]  )
      {
        if(IS_VAMPIRE(ch))
        {
          if(ch->pcdata->condition[COND_BLOODTHIRST] >= BLOOD)
            pager_printf_color(ch, "&Y%-22.22s:%4d   ",
                               skill->name, BLOOD);
          else
            pager_printf_color(ch, "&R%-22.22s:%4d   ",
                               skill->name, BLOOD);
        }
        else
        {
          if(ch->mana >= MANA)
            pager_printf_color(ch, "&Y%-22.22s:%4d   ", skill->name, MANA );
          else
            pager_printf_color(ch, "&R%-22.22s:%4d   ",
                               skill->name, MANA );
        }

        if(++col % 3 == 0)

          pager_printf(ch,"\n\r");

      }
    }

    pager_printf(ch,"\n\r");
  }

  else

  {
    if((sn =skill_lookup( arg1)) > 0)
    {
      skill=get_skilltype(sn);

      if(!skill->min_mana)
      {
        ch_printf(ch, "%s, '%s' doesn't use %s points.\n\r", ch->name, skill->name, IS_VAMPIRE(ch)?"blood":"mana");
        return;
      }

      if(ch->pcdata->learned[sn] < 1)
      {
        if (  ch->level < skill->skill_level[ch->class]  )
        {
          send_to_char( "You have not enough level to cast it.\n\r", ch );
          return;
        }
        else
        {
          send_to_char("You must practice a spell before using it.\n\r",ch);
          return;
        }
      }

      pager_printf(ch,"You need %d %s points to cast '%s' with success.\n\r",IS_VAMPIRE(ch)?BLOOD:MANA,IS_VAMPIRE(ch)?"blood":"mana", skill->name);
    }
    else
      ch_printf(ch, "That is not a spell or a skill.\n\r");
  }
  return;
}