DalekenMUD/
DalekenMUD/data/
DalekenMUD/data/notes/
DalekenMUD/data/player/
DalekenMUD/data/system/poses/
DalekenMUD/doc/Homepage/images/
DalekenMUD/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc 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.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||                            act_info.c                             ||
    || Informational and configuration commands.                         ||
 *_/<>\_________________________________________________________________/<>\_*/


#include "mud.h"


/*
 * Local functions.
 */
char *format_obj_to_char	args( ( char * buf, OBJ_DATA *obj,
					CHAR_DATA *ch, bool fShort ) );
void show_list_to_char		args( ( OBJ_DATA *list, CHAR_DATA *ch,
					bool fShort, bool fShowNothing ) );
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 ) );
void show_practice		args( ( CHAR_DATA *ch, bool zero ) );

#if defined( HAVE_FORTUNE )
FILE *popen	args( ( const char *command, const char *type ) );
int pclose	args( ( FILE *stream ) );
char *fgetf	args( ( char *s, int n, register FILE *iop ) );
#endif

const char *const sector_string[SECT_MAX] = {
    "&w[inside]", "&K[&wcity&K]", "&y[&gfield&y]", "&g[forest]",
    "&K[&ghills&K]", "&K[mountain]", "&b[&cwater&b]", "&b[water]",
    "&b[underwater]", "&c[air]", "&y[&Ydesert&y]", "&w[&Kspace&w]",
    "&G[&gswamp&G]"
};


char *format_obj_to_char( char *buf, OBJ_DATA *obj, CHAR_DATA *ch, bool fShort )
{
    char *p;

    if( IS_IMMORTAL( ch ) && ch->desc
	&& xIS_SET( get_char( ch )->act, PLR_INFO ) )
	sprintf( buf, "&w[%6.6d] ", obj->unique_key % 1000000 );
    else
	buf[0] = '\0';
    if( IS_OBJ_STAT( obj, ITEM_QUEST ) && !IS_NPC( ch )
	&& (OBJ_DATA *)ch->pcdata->quest->target == obj )
	strcat( buf, "&B<TARGET> " );
    else if( IS_OBJ_STAT( obj, ITEM_QUEST ) )
	strcat( buf, "&b(Quest) " );
    if( IS_OBJ_STAT( obj, ITEM_INVIS ) )
	strcat( buf, "&w(Invis) " );
    if( ( IS_AFFECTED( ch, AFF_DETECT_EVIL )
	  || ( IS_SET( race_table[ch->race].race_abilities,
		       RACE_DETECT_ALIGN )
	       && IS_GOOD( ch ) ) )
	&& IS_OBJ_STAT( obj, ITEM_EVIL ) )
	strcat( buf, "&r(Red Aura) " );
    if( IS_AFFECTED( ch, AFF_DETECT_MAGIC )
	&& IS_OBJ_STAT( obj, ITEM_MAGIC ) )
	strcat( buf, "&c(Magical) " );
    if( IS_OBJ_STAT( obj, ITEM_GLOW ) )
	strcat( buf, "&G(Glowing) " );
    if( IS_OBJ_STAT( obj, ITEM_HUM ) )
	strcat( buf, "&y(Humming) " );
    if( IS_OBJ_STAT( obj, ITEM_SHARP ) )
	strcat( buf, "&w(Sharp) " );
    if( IS_AFFECTED( ch, AFF_AURA_SIGHT ) )
    {
	if( IS_OBJ_STAT( obj, ITEM_CHARGED ) )
	    strcat( buf, "&W(CHARGED) " );
	if( IS_OBJ_STAT( obj, ITEM_OWNER ) )
	    strcat( buf, "&m(Owner) " );
    }
    if( IS_OBJ_STAT( obj, ITEM_RUNED ) )
	strcat( buf, "&w&4(Runed)&n " );
    if( IS_OBJ_STAT( obj, ITEM_BURIED ) )
	strcat( buf, "&k&2(Buried)&n " );
    if( obj->item_type == ITEM_CONTAINER
	&& IS_SET( obj->value[1], CONT_TRAPPED )
	&& !IS_NPC( ch )
	&& ch->pcdata->learned[gsn_detect_traps] / 2 > number_percent( ) )
	strcat( buf, "&R<TRAPPED> " );

    strcat( buf, "&g" );
    if( fShort )
    {
	if( obj->short_descr )
	    strcat( buf, obj->short_descr );
	if( obj->condition < 100 )
	    strcat( buf, " &y[Terrible]" );
	else if( obj->condition < 200 )
	    strcat( buf, " &y[Very Bad]" );
	else if( obj->condition < 400 )
	    strcat( buf, " &y[Bad]" );
	else if( obj->condition < 600 )
	    strcat( buf, " &y[Good]" );
	else if( obj->condition < 800 )
	    strcat( buf, " &y[Very Good]" );
	else if( obj->condition < 950 )
	    strcat( buf, " &y[Excellent]" );
    }
    else
    {
	if( obj->description )
	    strcat( buf, obj->description );
    }

    while( ( p = strstr( buf, "&x" ) ) )
	*( p + 1 ) = 'g';
    strcat( buf, "&n" );
    return buf;
}



/*
 * 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 )
{
    OBJ_DATA *obj;
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH * 4];
    char **prgpstrShow;
    char pstrShow[MAX_STRING_LENGTH];
    int *prgnShow;
    int nShow;
    int iShow;
    int count;
    bool fCombine;

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

    buf1[0] = '\0';
    /*
     * Alloc space for output lines.
     */
    count = 0;
    for( obj = list; obj; obj = obj->next_content )
    {
	if( obj->deleted )
	    continue;
	count++;
    }

    prgpstrShow = alloc_mem( count * sizeof( char * ) );
    prgnShow = alloc_mem( count * sizeof( int ) );

    nShow = 0;

    /*
     * Format the list of objects.
     */
    for( obj = list; obj; obj = obj->next_content )
    {
	if( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj ) )
	{
	    format_obj_to_char( pstrShow, 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]++;
			fCombine = TRUE;
			break;
		    }
		}
	    }

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

    /*
     * Output the formatted list.
     */
    for( iShow = 0; iShow < nShow; iShow++ )
    {
	if( IS_NPC( ch ) || xIS_SET( ch->act, PLR_COMBINE ) )
	{
	    if( prgnShow[iShow] != 1 )
	    {
		sprintf( buf, "&c(%2d) ", prgnShow[iShow] );
		strcat( buf1, buf );
	    }
	    else
	    {
		strcat( buf1, "     " );
	    }
	}
	strcat( buf1, prgpstrShow[iShow] );
	strcat( buf1, "\n\r" );
	free_string( prgpstrShow[iShow] );
    }

    if( fShowNothing && nShow == 0 )
    {
	if( IS_NPC( ch ) || xIS_SET( ch->act, PLR_COMBINE ) )
	    strcat( buf1, "     " );
	strcat( buf1, "&gNothing.&n\n\r" );
    }

    /*
     * Clean up.
     */
    free_mem( prgpstrShow, count * sizeof( char * ) );
    free_mem( prgnShow, count * sizeof( int ) );

    send_to_char( buf1, ch );
    return;
}


void show_char_to_char_0( CHAR_DATA *victim, CHAR_DATA *ch )
{
    char buf[MAX_STRING_LENGTH];
    char tmp[MAX_INPUT_LENGTH];
    char *p;

    buf[0] = '\0';

    if( IS_AFFECTED( victim, AFF_HOLD ) )
	strcat( buf, "&gEntwined in a web, " );
    if( !IS_NPC( ch ) && IS_NPC( victim )
	&& (MOB_INDEX_DATA *)ch->pcdata->quest->target == victim->pIndexData )
	strcat( buf, "&B<TARGET> " );
    if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_WIZINVIS )
	&& get_trust( victim ) < get_trust( ch ) )
	strcat( buf, "&C(Wizinvis) " );
    if( !IS_NPC( victim ) && !victim->desc )
	strcat( buf, "&w&4(Linkdead)&n " );
    if( IS_AFFECTED( victim, AFF_INVISIBLE ) )
	strcat( buf, "&w(Invis) " );
    if( IS_AFFECTED( victim, AFF_HIDE ) )
	strcat( buf, "&w&v(Hide)&n " );
    if( IS_AFFECTED( ch, AFF_DETECT_EVIL )
	|| IS_SET( race_table[ch->race].race_abilities, RACE_DETECT_ALIGN )
	|| IS_AFFECTED( ch, AFF_AURA_SIGHT ) )
    {
	if( IS_EVIL( victim ) )
	    strcat( buf, "&r(Red Aura) " );
	else if( IS_GOOD( victim ) )
	    strcat( buf, "&b(Blue Aura) " );
    }
    if( IS_AFFECTED( victim, AFF_FAERIE_FIRE ) )
	strcat( buf, "&M(Glowing) " );
    if( IS_AFFECTED( ch, AFF_AURA_SIGHT ) )
    {
	if( IS_AFFECTED( victim, AFF_CHARM ) )
	    strcat( buf, "&m(Obedient) " );
	/* phase shift races don't show */
	if( IS_AFFECTED( victim, AFF_PASS_DOOR ) )
	    strcat( buf, "&w(Hazy Aura) " );
	if( IS_AFFECTED( victim, AFF_PLAGUE ) )
	    strcat( buf, "&r&2(Pestilent Aura)&n " );
	if( IS_AFFECTED( victim, AFF_SANCTUARY )
	    || ( !IS_NPC( victim )
		 && IS_SET( race_table[victim->race].race_abilities, RACE_SANCT ) ) )
	{
	    if( IS_EVIL( victim ) )
		strcat( buf, "&K(Black Aura) " );
	    else
		strcat( buf, "&W(White Aura) " );
	}
	if( IS_AFFECTED( victim, AFF_ATTACKSHIELD ) )
	    strcat( buf, "&r&v(Flaming)&n " );
    }
    if( IS_NPC( victim ) && xIS_SET( victim->act, ACT_BURIED ) )
	strcat( buf, "&g&v(Buried)&n " );
    if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_OUTLAW ) )
	strcat( buf, "&R(OUTLAW) " );
    if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_BATTLE ) )
	strcat( buf, "&f&r(BATTLE)&n " );
    if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_AFK ) )
	strcat( buf, "&g<AFK> " );
    if( !IS_NPC( victim ) && xIS_SET( victim->act, PLR_BUSY ) )
	strcat( buf, "&w<BUSY> " );
    if( is_clan_enemy( victim, ch ) )
	strcat( buf, "&R<ENEMY> " );
    strcat( buf, "&x" );

    if( ( victim->position == POS_STANDING
	  || ( IS_NPC( victim ) && xIS_SET( victim->pIndexData->affected_by,
					    AFF_SLEEP ) ) )
	&& victim->long_descr[0] != '\0' )
    {
	strcat( buf, victim->long_descr );
	strcat( buf, "&n" );
	while( ( p = strstr( buf, "&x" ) ) )
	    *( p + 1 ) = 'g';
	send_to_char( buf, ch );
	return;
    }

    if( !IS_NPC( victim ) && victim->pcdata->immname
	&& victim->pcdata->immname[0] )
    {
	strcat( buf, victim->pcdata->immname );
	strcat( buf, " " );
    }

    strcat( buf, can_see( ch, victim ) ? IS_NPC( victim )
	    ? capitalize( victim->short_descr ) : victim->name
	    : "Someone" );
    if( !IS_NPC( victim ) && !xIS_SET( ch->act, PLR_BRIEF ) )
	strcat( buf, victim->pcdata->title );

    strcat( buf, "&x " );
    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;
    case POS_SLEEPING:
	if( victim->on != NULL )
	{
	    if( IS_SET( victim->on->value[0], FURN_SLEEP_IN ) )
		sprintf( tmp, " is sleeping in %s.",
			 victim->on->short_descr );
	    else if( IS_SET( victim->on->value[0], FURN_SLEEP_ON ) )
		sprintf( tmp, " is sleeping on %s.",
			 victim->on->short_descr );
	    else
		sprintf( tmp, " is sleeping at %s.",
			victim->on->short_descr );
	    strcat( buf, tmp );
	}
	else
	    strcat( buf, "is sleeping here." );
	break;
    case POS_MEDITATING:
	strcat( buf, "is meditating here." );
	break;
    case POS_RESTING:
	if( victim->on != NULL )
	{
	    if( IS_SET( victim->on->value[0], FURN_REST_ON ) )
		sprintf( tmp, " is resting on %s.",
			 victim->on->short_descr );
	    else if( IS_SET( victim->on->value[0], FURN_REST_AT ) )
		sprintf( tmp, " is resting at %s.",
			 victim->on->short_descr );
	    else
		sprintf( tmp, " is resting in %s.",
			victim->on->short_descr );
	    strcat( buf, tmp );
	}
	else
	    strcat( buf, "is resting here." );
	break;
    case POS_SITTING:
	if( victim->on != NULL )
	{
	    if( IS_SET( victim->on->value[0], FURN_REST_ON ) )
		sprintf( tmp, " is sitting on %s.",
			 victim->on->short_descr );
	    else if( IS_SET( victim->on->value[0], FURN_REST_AT ) )
		sprintf( tmp, " is sitting at %s.",
			 victim->on->short_descr );
	    else
		sprintf( tmp, " is sitting in %s.",
			victim->on->short_descr );
	    strcat( buf, tmp );
	}
	else
	    strcat( buf, "is resting here." );
	break;
    case POS_SMASHED:
    case POS_GETTING_UP:
	strcat( buf, "is here struggling to get up." );
	break;
    case POS_STANDING:
	if( victim->on != NULL )
	{
	    if( IS_SET( victim->on->value[0], FURN_STAND_ON ) )
		sprintf( tmp, " is standing on %s.",
			 victim->on->short_descr );
	    else if( IS_SET( victim->on->value[0], FURN_STAND_AT ) )
		sprintf( tmp, " is standing at %s.",
			 victim->on->short_descr );
	    else
		sprintf( tmp, " is standing in %s.",
			 victim->on->short_descr );

	    strcat( buf, tmp );
	}
	else if( IS_SET( victim->body_parts, BODY_PART_WINGS )
		 || xIS_SET( ch->affected_by, AFF_FLYING ) )
	    strcat( buf, "is floating in mid-air." );
	else
	    strcat( buf, "is here." );
	break;
    case POS_FIGHTING:
	strcat( buf, "is here, fighting " );
	if( !victim->fighting )
	    strcat( buf, "thin air??" );
	else if( victim->fighting == ch )
	    strcat( buf, "YOU!" );
	else if( victim->in_room == victim->fighting->in_room )
	{
	    strcat( buf, PERS( victim->fighting, ch ) );
	    strcat( buf, "." );
	}
	else
	    strcat( buf, "somone who left??" );
	break;
    }

    strcat( buf, "&n\n\r" );
    buf[0] = UPPER( buf[0] );
    while( ( p = strstr( buf, "&x" ) ) )
	*( p + 1 ) = 'g';
    send_to_char( buf, ch );
    return;
}


void show_char_to_char_1( CHAR_DATA *victim, CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    char buf[MAX_STRING_LENGTH];
    int iWear;
    int percent;
    bool found;

    act( "$n looks at $N.", ch, NULL, victim, TO_CANSEE );

    sprintf( buf, "&g$N is of the %s race.&n\n\r", race_table[victim->race].name );
    act( buf, ch, NULL, victim, TO_CHAR );

    if( IS_NPC( victim ) && !IS_NPC( ch )
	&& ch->pcdata->learned[gsn_mob_lore] / 2 > number_percent( ) )
    {
	if( victim->class >= 0 )
	{
	    act( "&r$N's eyes show the knowledge of a $t.&n",
		 ch, class_table[victim->class].name, victim, TO_CHAR );
	}
	if( ( victim->spec_fun && spec_table[victim->spec_fun].message ) )
	{
	    act( spec_table[victim->spec_fun].message,
		 ch, NULL, victim, TO_CHAR );
	}
	if( IS_NPC( victim ) && xIS_SET( victim->act, ACT_WIMPY ) )
	{
	    act( "$N is a bit of a wuss.", ch, NULL, victim, TO_CHAR );
	}
	charprintf( ch, "%s&n wears about a size %d.\n\r",
		    capitalize( PERS( victim, ch ) ),
		    get_size( victim ) );
	if( get_curr_temp( victim ) < -30 )
	    act( "$N is cold-blooded.", ch, NULL, victim, TO_CHAR );
	else if( get_curr_temp( victim ) > 30 )
	    act( "$N is warm blooded.", ch, NULL, victim, TO_CHAR );
    }
    if( victim->description[0] != '\0' )
    {
	send_to_char( "&w", ch );
	send_to_char( victim->description, ch );
    }
    else
    {
	act( "You see nothing special about $M.", ch, NULL, victim, TO_CHAR );
    }

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

    strcpy( buf, "&c$n " );

    if( percent >= 100 )
	strcat( buf, "look$% perfectly fit" );
    else if( percent >= 90 )
	strcat( buf, "is slightly scratched" );
    else if( percent >= 80 )
	strcat( buf, "is looking a bit tattered around the edges" );
    else if( percent >= 70 )
	strcat( buf, "has some cuts" );
    else if( percent >= 60 )
	strcat( buf, "is beaten up pretty badly" );
    else if( percent >= 50 )
	strcat( buf, "has many nasty wounds" );
    else if( percent >= 40 )
	strcat( buf, "is bleeding freely" );
    else if( percent >= 30 )
	strcat( buf, "is standing in a pool of $s own blood" );
    else if( percent >= 20 )
	strcat( buf, "is leaking guts" );
    else if( percent >= 10 )
	strcat( buf, "is well overdue to be put down" );
    else
	strcat( buf, "look$% like the walking dead" );

    if( ch->desc && xIS_SET( get_char( ch )->act, PLR_INFO ) )
	sprintf( buf + strlen( buf ), ". &w[#%d L%d %d/%dhp]&n\n\r",
		 victim->unique_key, victim->level,
		 victim->hit, victim->max_hit );
    else
	strcat( buf, ".&n\n\r" );
    act( buf, victim, NULL, ch, TO_VICT );

    found = FALSE;
    for( iWear = 1; iWear < MAX_WEAR; iWear++ )
    {
	if( !( obj = get_eq_char( victim, iWear ) )
	    || !can_see_obj( ch, obj ) )
	    continue;
	if( !found )
	{
	    act( "&m$E is using:", ch, NULL, victim, TO_CHAR );
	    found = TRUE;
	}
	do
	{
	    char tmp[MAX_STRING_LENGTH];
	    send_to_char( wear_table[iWear].where_name, ch );
	    send_to_char( format_obj_to_char( tmp, obj, ch, TRUE ), ch );
	    send_to_char( "&n\n\r", ch );
	    for( obj = obj->next_content; obj; obj = obj->next_content )
		if( !obj->deleted && obj->wear_loc == iWear
		    && can_see_obj( ch, obj ) )
		    break;
	}
	while( obj );
    }

    if( victim != ch
	&& !IS_NPC( ch ) && get_success( ch, gsn_peek, 100 ) )
    {
	send_to_char( "\n\r&yYou peek at the inventory:&n\n\r", ch );
	show_list_to_char( victim->carrying, ch, TRUE, TRUE );
    }

    return;
}


void show_char_to_char( CHAR_DATA *list, CHAR_DATA *ch )
{
    CHAR_DATA *rch;
    int hidden = 0;

    if( !list )
	return;

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

	if( ( !IS_NPC( rch )
	      && xIS_SET( rch->act, PLR_WIZINVIS )
	      && get_trust( ch ) < get_trust( rch ) )
	    || ( IS_NPC( rch )
		 && rch->pIndexData->vnum == MOB_VNUM_SUPERMOB ) )
	    continue;

	if( can_see( ch, rch ) )
	{
	    show_char_to_char_0( rch, ch );
	}
	else if( room_is_dark( ch->in_room )
		 && ( IS_AFFECTED( rch, AFF_INFRARED )
		     || IS_SET( race_table[rch->race].race_abilities,
			       RACE_INFRAVISION ) ) )
	{
	    send_to_char( "You see glowing &rred&n eyes watching YOU!\n\r", ch );
	}
	else if( IS_AFFECTED( ch, AFF_SENSE_LIFE ) )
	    hidden++;
    }
    if( hidden )
	charprintf( ch, "&cYou sense the presence of %d other lifeforms.&n\n\r", hidden );

    return;
}


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

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

    return TRUE;
}


void do_look( CHAR_DATA *ch, const char *argument )
{
    ROOM_INDEX_DATA *room, *old_room;
    OBJ_DATA *obj;
    CHAR_DATA *victim;
    EXIT_DATA *pexit;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char *pdesc;
    int door;

    if( !ch->desc )
	return;

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

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

    if( !check_blind( ch ) )
	return;

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

    if( arg1[0] == '\0' || !str_cmp( arg1, AUTOLOOK ) )
    {
	if( !IS_NPC( ch )
	    && !xIS_SET( ch->act, PLR_HOLYLIGHT )
	    && room_is_dark( ch->in_room )
	    && !IS_SET( race_table[ch->race].race_abilities, RACE_DARK_SIGHT ) )
	{
	    send_to_char( "&KIt is pitch black ... &n\n\r", ch );
	    show_char_to_char( ch->in_room->people, ch );
	    return;
	}

	/* 'look' or 'look auto' */
	if( ch->desc && xIS_SET( get_char( ch )->act, PLR_INFO ) )
	    charprintf( ch, "&b[&r%d&b] &y%s  %s &w[%s]&n\n\r",
			ch->in_room->vnum, ch->in_room->name,
			IS_SET( ch->in_room->room_flags, ROOM_FLOODED )
			? "&b...flooded..."
			: sector_string[ch->in_room->sector_type],
			flag_string( room_flags, &ch->in_room->room_flags ) );


	else
	    charprintf( ch, "&y%s  %s&n\n\r",
			ch->in_room->name,
			IS_SET( ch->in_room->room_flags, ROOM_FLOODED )
			? "&b...flooded..."
			: sector_string[ch->in_room->sector_type] );

	if( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_AUTOEXIT ) )
	    do_exits( ch, AUTOLOOK );

	if( !IS_NPC( ch ) && str_cmp( arg1, AUTOLOOK )
	    && ch->pcdata->learned[gsn_detect_traps] / 2 > number_percent( ) )
	{
	    for( door = 0; door < 6; ++door )
	    {
		if( ch->in_room->exit[door]
		    && IS_SET( ch->in_room->exit[door]->exit_info, EX_TRAPPED ) )
		    act( "&R<&rThe door to the $t is trapped&R>&n", ch,
			 dir_name[door], NULL, TO_CHAR );
	    }
	}
	if( arg1[0] == '\0'
	    || ( !IS_NPC( ch ) && !xIS_SET( ch->act, PLR_BRIEF ) ) )
	{
	    send_to_char( "&w", ch );
	    if( ch->in_room->description[0] == '.' )
		send_to_char( ch->in_room->description + 1, ch );
	    else
		send_to_char( ch->in_room->description, ch );
	    send_to_char( "&n", ch );
	}

	if( IS_SET( ch->in_room->room_flags, ROOM_CONE_OF_SILENCE )
	    || IS_SET( ch->in_room->room_flags, ROOM_TEMP_CONE_OF_SILENCE ) )
	    send_to_char( "It seems eerily quiet.\n\r", ch );

	show_list_to_char( ch->in_room->contents, ch, FALSE, FALSE );
	show_char_to_char( ch->in_room->people, ch );
	if( !IS_NPC( ch ) && arg1[0] != '\0' )
	    auto_track( ch );
	if( xIS_SET( ch->in_room->progtypes, LOOK_PROG ) )
	    rprog_percent_check( ch->in_room, ch, NULL, NULL, LOOK_PROG );
	return;
    }

    if( !IS_NPC( ch )
	&& !xIS_SET( ch->act, PLR_HOLYLIGHT )
	&& room_is_dark( ch->in_room )
	&& !IS_SET( race_table[ch->race].race_abilities, RACE_DARK_SIGHT ) )
    {
	send_to_char( "&KIt is pitch black ... &n\n\r", ch );
	return;
    }

    if( !str_cmp( arg1, "area" ) )
    {
	send_to_char( "&gYou take a look at the the general lay of the land.\n\r&w",
		      ch );
	if( ch->in_room->area->description
	    && ch->in_room->area->description[0] != '\0' )
	{
	    if( ch->in_room->area->description[0] == '.' )
		send_to_char( ch->in_room->area->description + 1, ch );
	    else
		send_to_char( ch->in_room->area->description, ch );
	}
	else
	{
	    send_to_char( "You find nothing of any real interest in the vicinity.\n\r", ch );
	}
	send_to_char( "&n", ch );
	return;
    }

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

	if( !( obj = get_obj_here( ch, arg2 ) ) )
	{
	    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 );
	    return;

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

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

	case ITEM_FOUNTAIN:
	    charprintf( ch, "&gIt flows with %s&g liquid.\n\r",
			liq_table[obj->value[2]].liq_color );
	    break;

	case ITEM_PORTAL:
	    room = get_room_index( obj->value[0] );
	    if( room->area->plane != ch->in_room->area->plane )
	    {
		send_to_char( "A blinding &Wlight&N obscures your vision.\n\r",
			      ch );
		break;
	    }
	    old_room = ch->in_room;
	    ch->in_room = room;
	    do_look( ch, AUTOLOOK );
	    ch->in_room = old_room;
	    break;

	case ITEM_CONTAINER:
	    if( IS_SET( obj->value[1], CONT_CLOSED ) )
	    {
		send_to_char( "&gIt is closed.&n\n\r", ch );
		return;
	    }
	case ITEM_CORPSE_NPC:
	case ITEM_CORPSE_PC:
	    act( "&c$p contains:&n", ch, obj, NULL, TO_CHAR );
	    show_list_to_char( obj->contains, ch, TRUE, TRUE );
	    break;
	}
	if( xIS_SET( obj->pIndexData->progtypes, EXAMINE_PROG ) )
	    oprog_percent_check( ch, obj, NULL, EXAMINE_PROG );
	return;
    }

    if( ( victim = get_char_room( ch, arg1 ) ) )
    {
	show_char_to_char_1( victim, ch );
	if( IS_NPC( victim ) && xIS_SET( victim->pIndexData->progtypes,
					 LOOK_PROG ) )
	    mprog_percent_check( victim, ch, NULL, NULL, LOOK_PROG );
	return;
    }

    pdesc = get_extra_descr( arg1, ch->in_room->extra_descr );
    if( pdesc )
    {
	send_to_char( pdesc, ch );
	if( xIS_SET( ch->in_room->progtypes, LOOK_PROG ) )
	    rprog_percent_check( ch->in_room, ch, NULL, NULL, LOOK_PROG );
	return;
    }

    for( obj = ch->in_room->contents; obj; obj = obj->next_content )
    {
	if( can_see_obj( ch, obj ) )
	{
	    pdesc = get_extra_descr( arg1, obj->extra_descr );
	    if( pdesc )
	    {
		send_to_char( pdesc, ch );
		if( xIS_SET( obj->pIndexData->progtypes, LOOK_PROG ) )
		    oprog_percent_check( ch, obj, NULL, LOOK_PROG );
		return;
	    }

	    pdesc = get_extra_descr( arg1, obj->pIndexData->extra_descr );
	    if( pdesc )
	    {
		send_to_char( pdesc, ch );
		if( xIS_SET( obj->pIndexData->progtypes, LOOK_PROG ) )
		    oprog_percent_check( ch, obj, NULL, LOOK_PROG );
		return;
	    }
	}

	if( is_obj_name( obj, arg1 ) )
	{
	    send_to_char( obj->description, ch );
	    send_to_char( "\n\r", ch );
	    if( xIS_SET( obj->pIndexData->progtypes, LOOK_PROG ) )
		oprog_percent_check( ch, obj, NULL, LOOK_PROG );
	    return;
	}
    }

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( can_see_obj( ch, obj ) )
	{
	    pdesc = get_extra_descr( arg1, obj->extra_descr );
	    if( pdesc )
	    {
		send_to_char( pdesc, ch );
		if( xIS_SET( obj->pIndexData->progtypes, LOOK_PROG ) )
		    oprog_percent_check( ch, obj, NULL, LOOK_PROG );
		return;
	    }

	    pdesc = get_extra_descr( arg1, obj->pIndexData->extra_descr );
	    if( pdesc )
	    {
		send_to_char( pdesc, ch );
		if( xIS_SET( obj->pIndexData->progtypes, LOOK_PROG ) )
		    oprog_percent_check( ch, obj, NULL, LOOK_PROG );
		return;
	    }
	}

	if( is_obj_name( obj, arg1 ) )
	{
	    send_to_char( obj->description, ch );
	    send_to_char( "\n\r", ch );
	    if( xIS_SET( obj->pIndexData->progtypes, LOOK_PROG ) )
		oprog_percent_check( ch, obj, NULL, LOOK_PROG );
	    return;
	}
    }

    if( !str_prefix( arg1, "north" ) )
	door = 0;
    else if( !str_prefix( arg1, "east" ) )
	door = 1;
    else if( !str_prefix( arg1, "south" ) )
	door = 2;
    else if( !str_prefix( arg1, "west" ) )
	door = 3;
    else if( !str_prefix( arg1, "up" ) )
	door = 4;
    else if( !str_prefix( arg1, "down" ) )
	door = 5;
    else
    {
	send_to_char( "You do not see that here.\n\r", ch );
	return;
    }

    /*
     * 'look direction'
     */
    if( !( pexit = ch->in_room->exit[door] ) )
    {
	send_to_char( "Nothing special there.\n\r", ch );
	return;
    }

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

    if( pexit->keyword
	&& pexit->keyword[0] != '\0'
	&& pexit->keyword[0] != ' ' )
    {
	if( IS_SET( pexit->exit_info, EX_BASHED ) )
	    act( "The $d has been bashed from its hinges.",
		 ch, NULL, pexit->keyword, TO_CHAR );
	else if( IS_SET( pexit->exit_info, EX_CLOSED ) )
	    act( "The $d is closed.", ch, NULL, pexit->keyword, TO_CHAR );
	else if( IS_SET( pexit->exit_info, EX_ISDOOR ) )
	    act( "The $d is open.", ch, NULL, pexit->keyword, TO_CHAR );
    }

    return;
}



void do_examine( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char buf[MAX_INPUT_LENGTH];

    if( argument[0] == '\0' )
    {
	send_to_char( "Examine what?\n\r", ch );
	return;
    }
    if( !( obj = get_obj_here( ch, argument ) ) )
    {
	send_to_char( "You don't see that here.\n\r", ch );
	return;
    }

    do_look( ch, argument );
    if( obj->deleted )		/* safety check for oprogs */
	return;
    charprintf( ch, "&gThis item mainly consists of: %s\n\r",
	     flag_string( material_flags, &obj->pIndexData->material ) );
    charprintf( ch, "Item condition is about %d%%&n\n\r",
	     number_fuzzy( number_range( obj->condition / 10 - 10,
					 obj->condition / 10 + 10 ) ) );

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

    case ITEM_ARMOUR:
	if( obj->value[1] < 0 )
	    send_to_char( "A little tag says 'one size fits all'.\n\r", ch );
	else
	{
	    charprintf( ch, "A little tag says 'size %d'.\n\r", obj->value[1] );
	}
	break;

    case ITEM_DRINK_CON:
    case ITEM_CONTAINER:
    case ITEM_CORPSE_NPC:
    case ITEM_CORPSE_PC:
    case ITEM_PORTAL:
	send_to_char( "When you look inside, you see:\n\r", ch );
	sprintf( buf, "in %s", argument );
	do_look( ch, buf );
    }
    if( obj->level > ch->level + 3 )
	send_to_char( "&gYou would need more experience to use this item.\n\r",
		      ch );

    return;
}


/*
 * Thanks to Zrin for auto-exit part.
 */
void do_exits( CHAR_DATA *ch, const char *argument )
{
    EXIT_DATA *pexit;
    char buf[MAX_STRING_LENGTH];
    int door;
    bool found;
    bool fAuto;

    buf[0] = '\0';
    fAuto = !str_cmp( argument, AUTOLOOK );

    if( !check_blind( ch ) )
	return;

    strcpy( buf, fAuto ? "&c[Exits:" : "&cObvious exits:&g\n\r" );

    found = FALSE;
    for( door = 0; door <= 5; door++ )
    {
	if( ( pexit = ch->in_room->exit[door] )
	    && pexit->to_room
	    && !IS_SET( pexit->exit_info, EX_HIDDEN )
	    && ( !IS_SET( pexit->exit_info, EX_CLOSED ) || fAuto ) )
	{
	    found = TRUE;
	    if( fAuto )
	    {
		strcat( buf, " " );
		strcat( buf, dir_name[door] );
		if( IS_SET( pexit->exit_info, EX_CLOSED ) )
		    strcat( buf, "->closed" );
	    }
	    else
	    {
		if( ch->desc && xIS_SET( get_char( ch )->act, PLR_INFO ) )
		    sprintf( buf + strlen( buf ), "&w[%5d] ",
			     pexit->to_room->vnum );
		sprintf( buf + strlen( buf ), "&c%-5s - &g%s&n\n\r",
			capitalize( dir_name[door] ),
			room_is_dark( pexit->to_room )
			? "Too dark to tell"
			: pexit->to_room->name );
	    }
	}
    }

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

    if( fAuto )
	strcat( buf, "]&n\n\r" );

    send_to_char( buf, ch );
    return;
}


void do_score( CHAR_DATA *ch, const char *argument )
{
    BUFFER *buf = buffer_new( MAX_STRING_LENGTH );
    char linebuf[MAX_INPUT_LENGTH];
    char tmp[MAX_INPUT_LENGTH];
    char *p;

    buffer_strcat( buf, "&w_____.-------------------------------------------"
		   "--------------------._____&n\n\r" );
    buffer_strcat( buf, "&K \\\\\\&m" );

    strcpy( linebuf, ch->name );
    sprintf( linebuf, "%s%s&m, a%s %s %s.",
	     ch->name, IS_NPC( ch ) ? "" : ch->pcdata->title,
	     isvowel( *race_table[ch->race].name ) ? "n" : "",
	     race_table[ch->race].name,
	     ch->class < 0 ? "Mob" : class_table[ch->class].name );
    p = &linebuf[0];
    while( ( p = strstr( p, "&x" ) ) )
	*( p + 1 ) = 'm';
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 67 ) );
    buffer_strcat( buf, "&K///&n\n\r" );

    buffer_strcat( buf, " &K_>>>&w-----------------&K=> &b(----------&B[&KX"
		   "&B]&b----------)&K <=&w-----------------&K<<<_&n\n\r" );

    if( ch->sublevel )
	bprintf( buf, " &b|&g Level &c%13d&g(&c%3d&g) &b|&g",
		 ch->level, ch->sublevel );
    else
	bprintf( buf, " &b|&g Level &c%18d &b|&g",
		 ch->level );

    if( get_age( ch ) != 0 )
	sprintf( linebuf, "You are %d years old (%d hours).",
		 get_age( ch ), ( ch->played / 3600 ) );
    else
	sprintf( linebuf, "You have played for %d hours.",
		 ( ch->played / 3600 ) );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 44 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&g Experience&c%6d&g.&c%2.2d&g/&c%4d &b|&g",
	     ( get_tnl( ch ) - ch->exp ) / 100, ch->exp % 100,
	     get_tnl( ch ) / 100 );
    if( !IS_NPC( ch ) && ch->pcdata->religion )
	sprintf( linebuf, "You follow the %s&n&g religion.",
		 ( ch->pcdata->religion->display_name
		   && ch->pcdata->religion->display_name[0] )
		 ? ch->pcdata->religion->display_name
		 : ch->pcdata->religion->name );
    else
	strcpy( linebuf, "You follow no religion." );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 44 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&g Hit Points &c%6d&g/&c%6d &b|&g",
	     ch->hit, ch->max_hit );
    if( !IS_NPC( ch ) && ch->pcdata->clan )
	sprintf( linebuf, "You are a%s %s %s %s.",
		 isvowel( *ch->pcdata->clan->name ) ? "n" : "",
		 ch->pcdata->clan->name,
		 flag_string( clan_type_flags, &ch->pcdata->clan->clan_type ),
		 rank_name( ch->pcdata->clan_rank,
					ch->pcdata->clan->clan_type ) );
    else
	strcpy( linebuf, "You aren't a member of a clan." );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 44 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&g Mana &c%12d&g/&c%6d &b|-------&B[&KX&B]"
	     "&b----------) &K<=&w---------------- - - -&n\n\r",
	     total_mana( ch->mana ), total_mana( ch->max_mana ) );

    bprintf( buf, " &b|&g Movement &c%8d&g/&c%6d &b|&g",
	     ch->move, ch->max_move );
    sprintf( linebuf, "You have &y%d&g gold on hand.", ch->gold );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 42 ) );
    buffer_strcat( buf, "  &b|&n\n\r" );

    buffer_strcat( buf, "&w- - - ----------------&K=> &b(--'&g" );
    sprintf( linebuf, "Your bank account has &y%d&g gold.",
	     IS_NPC( ch ) ? 0 : ch->pcdata->banked );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 42 ) );
    buffer_strcat( buf, "  &b|&n\n\r" );

    bprintf( buf, " &b|&m Strength    &c%3d &m(&c%2d&m)  &b|&g",
	     IS_NPC( ch ) ? 15 + race_table[ch->race].str_mod
	     : ch->pcdata->perm_str,
	     get_curr_str( ch ) );
    sprintf( linebuf, "You can practice another &y%d&g times.",
	     ch->practice );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 47 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&m Intelligence&c%3d &m(&c%2d&m)  &b|&g",
	     IS_NPC( ch ) ? 15 + race_table[ch->race].int_mod
	     : ch->pcdata->perm_int,
	     get_curr_int( ch ) );
    sprintf( linebuf, "You flee from combat at &y%d&g hitpoints.",
	     ch->wimpy );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 47 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&m Wisdom      &c%3d &m(&c%2d&m)  &b|&g",
	     IS_NPC( ch ) ? 15 + race_table[ch->race].wis_mod
	     : ch->pcdata->perm_wis,
	     get_curr_wis( ch ) );
    if( !IS_NPC( ch ) )
	sprintf( linebuf, "You have killed &y%d&g and died &y%d&g times.",
		 ch->pcdata->killed, ch->pcdata->died );
    else
	strcpy( linebuf, "--" );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 47 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&m Dexterity   &c%3d &m(&c%2d&m)  &b|&g",
	     IS_NPC( ch ) ? 15 + race_table[ch->race].dex_mod
	     : ch->pcdata->perm_dex,
	     get_curr_dex( ch ) );
    strcpy( linebuf, "You are " );
    if( GET_AC( ch ) >= 101 )
	strcat( linebuf, "WORSE than naked!" );
    else if( GET_AC( ch ) >= 80 )
	strcat( linebuf, "naked" );
    else if( GET_AC( ch ) >= 60 )
	strcat( linebuf, "wearing clothes" );
    else if( GET_AC( ch ) >= 40 )
	strcat( linebuf, "slightly armoured" );
    else if( GET_AC( ch ) >= 20 )
	strcat( linebuf, "somewhat armoured" );
    else if( GET_AC( ch ) >= 0 )
	strcat( linebuf, "armoured" );
    else if( GET_AC( ch ) >= -20 )
	strcat( linebuf, "well armoured" );
    else if( GET_AC( ch ) >= -40 )
	strcat( linebuf, "strongly armoured" );
    else if( GET_AC( ch ) >= -60 )
	strcat( linebuf, "heavily armoured" );
    else if( GET_AC( ch ) >= -80 )
	strcat( linebuf, "massively armoured" );
    else if( GET_AC( ch ) >= -100 )
	strcat( linebuf, "supremely armoured" );
    else if( GET_AC( ch ) >= -150 )
	strcat( linebuf, "awesomely armoured" );
    else if( GET_AC( ch ) >= -200 )
	strcat( linebuf, "divinely armoured" );
    else if( GET_AC( ch ) >= -300 )
	strcat( linebuf, "incredibly armoured" );
    else if( GET_AC( ch ) >= -400 )
	strcat( linebuf, "like a brick wall" );
    else if( GET_AC( ch ) >= -500 )
	strcat( linebuf, "hard as a rock" );
    else if( GET_AC( ch ) >= -750 )
	strcat( linebuf, "indestructable" );
    else if( GET_AC( ch ) >= -1000 )
	strcat( linebuf, "made from steel" );
    else
	strcat( linebuf, "impossibly armoured!" );
    if( ch->level >= 20 )
	sprintf( strchr( linebuf, '\0' ), " &y[%d]&g.", GET_AC( ch ) );
    else
	strcat( linebuf, "." );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 47 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    bprintf( buf, " &b|&m Constitution&c%3d &m(&c%2d&m)  &b|&g",
	     IS_NPC( ch ) ? 15 + race_table[ch->race].con_mod
	     : ch->pcdata->perm_con,
	     get_curr_con( ch ) );
    strcpy( linebuf, "You are " );
    if( ch->alignment > 900 )
	strcat( linebuf, "&Wangelic" );
    else if( ch->alignment > 700 )
	strcat( linebuf, "&wsaintly" );
    else if( ch->alignment > 500 )
	strcat( linebuf, "&wdevout" );
    else if( ch->alignment > 350 )
	strcat( linebuf, "&wgood" );
    else if( ch->alignment > 200 )
	strcat( linebuf, "honorable" );
    else if( ch->alignment > 100 )
	strcat( linebuf, "kind" );
    else if( ch->alignment > -100 )
	strcat( linebuf, "neutral" );
    else if( ch->alignment > -200 )
	strcat( linebuf, "base" );
    else if( ch->alignment > -350 )
	strcat( linebuf, "mean" );
    else if( ch->alignment > -500 )
	strcat( linebuf, "&revil" );
    else if( ch->alignment > -700 )
	strcat( linebuf, "&rfiendish" );
    else if( ch->alignment > -900 )
	strcat( linebuf, "&rdemonic" );
    else
	strcat( linebuf, "&Rsatanic" );
    if( ch->level >= 10 )
	sprintf( strchr( linebuf, '\0' ), " &y[%d]&g.", ch->alignment );
    else
	strcat( linebuf, "." );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 47 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    buffer_strcat( buf, "&w- - - ----------------&K=> &b(----------&B[&KX&B]"
		   "&b----------) &K<=&w---------------- - - -&n\n\r" );

    if( get_first_class( ch ) != CLASS_NONE )
    {
	int i, j = 0;

	buffer_strcat( buf, " &b|&m " );
	sprintf( linebuf, "Original Class: &c%s&m",
		 class_table[get_first_class( ch )].name );

	if( get_second_class( ch ) != CLASS_NONE )
	{
	    buffer_strcat( buf, colour_strpad( tmp, linebuf, 35 ) );
	    sprintf( linebuf, "&mSecond Class: &c%s",
		     class_table[get_second_class( ch )].name );
	    buffer_strcat( buf, colour_strpad( tmp, linebuf, 35 ) );
	}
	else
	    buffer_strcat( buf, colour_strpad( tmp, linebuf, 71 ) );
	buffer_strcat( buf, "&b|&n\n\r" );

	for( i = 0; i < 5; ++i )
	{
	    if( ch->pcdata->multi_class[i] == CLASS_ADEPT )
	    {
		if( j % 2 == 1 )
		    buffer_strcat( buf, colour_strpad( tmp, linebuf, 35 ) );
		sprintf( linebuf, "&mAdept: &c%s", class_table[i].name );
		if( j % 2 == 0 )
		{
		    buffer_strcat( buf, " &b|&m " );
		}
		else
		{
		    buffer_strcat( buf, colour_strpad( tmp, linebuf, 35 ) );
		    buffer_strcat( buf, "&b|&n\n\r" );
		}
		++j;
	    }
	}
	if( j % 2 == 1 )
	{
	    buffer_strcat( buf, colour_strpad( tmp, linebuf, 70 ) );
	    buffer_strcat( buf, "&b|&n\n\r" );
	}
	buffer_strcat( buf, "&w- - - ----------------&K=> &b(----------&B[&KX"
		       "&B]&b----------) &K<=&w---------------- - - -&n\n\r" );
    }

    if( get_aspire_class( ch ) >= 0 && ch->level < LEVEL_HERO )
    {
	buffer_strcat( buf, " &b|&r" );
	sprintf( linebuf, "You are aspiring to become a %s.",
		 class_table[get_aspire_class( ch )].name );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, 71 ) );
	buffer_strcat( buf, "&b|&n\n\r" );
    }

    buffer_strcat( buf, " &b|&r" );
    sprintf( linebuf, "You are carrying &y%d&r/&y%d&r items"
	     " and &y%d&r/&y%d&r weight.",
	     ch->carry_number, can_carry_n( ch ),
	     ch->carry_weight, can_carry_w( ch ) );
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 71 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    if( ch->level >= 15 )
    {
	int pad;

	if( ch->level >= 50 )
	    pad = 24;
	else
	    pad = 35;

	buffer_strcat( buf, " &b|&r" );
	sprintf( linebuf, "Attack Rating &y%d&r.", get_hitroll( ch ) );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, pad ) );

	if( pad == 35 )
	    pad++;
	sprintf( linebuf, "Damage Bonus &y%d&r.", get_damroll( ch ) );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, pad ) );

	if( pad == 24 )
	{
	    sprintf( linebuf, "Speed &y%d&r.", get_speed( ch ) );
	    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 23 ) );
	}

	buffer_strcat( buf, "&b|&n\n\r" );

	buffer_strcat( buf, " &b|&r" );
	sprintf( linebuf, "Magic Resistance &y%d&r.",
		 get_magic_resist( ch ) );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, 24 ) );

	sprintf( linebuf, "Resilience &y%d &r(&y%d&r)",
		 race_table[ch->race].resil, get_curr_resil( ch ) );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, 24 ) );

	sprintf( linebuf, "Body Temp. &y%d&r (&y%d&r)",
		 race_table[ch->race].body_temp + 37,
		 get_curr_temp( ch ) + 37 );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, 23 ) );

	buffer_strcat( buf, "&b|&n\n\r" );
    }

    if( !IS_NPC( ch ) )
    {
	bool added = FALSE;

	linebuf[0] = '\0';

	if( ch->pcdata->condition[COND_DRUNK] > 200 )
	{
	    added = TRUE;
	    strcat( linebuf, "   You are drunk.   " );
	}
	if( ch->pcdata->condition[COND_THIRST] == 0
	    && ch->level <= LEVEL_HERO )
	{
	    added = TRUE;
	    strcat( linebuf, "   You are thirsty.   " );
	}
	if( ch->pcdata->condition[COND_FULL] == 0
	    && ch->level <= LEVEL_HERO )
	{
	    added = TRUE;
	    strcat( linebuf, "   You are hungry.   " );
	}
	if( added )
	{
	    buffer_strcat( buf, " &b|&r" );
	    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 71 ) );
	    buffer_strcat( buf, "&b|&n\n\r" );
	}
    }

    if( IS_AFFECTED( ch, AFF_DEAD ) )
	buffer_strcat( buf, " &b|&R            It is only a matter of time "
		       "before you die!!               &b|&n\n\r" );

    buffer_strcat( buf, " &b|&r" );
    switch( ch->position )
    {
    case POS_DEAD:
	strcpy( linebuf, "You are &Rslowly decomposing!!" );
	break;
    case POS_MORTAL:
	strcpy( linebuf, "You are MORTALLY WOUNDED!" );
	break;
    case POS_INCAP:
	strcpy( linebuf, "You are incapacitated!" );
	break;
    case POS_STUNNED:
	strcpy( linebuf, "You are &ystunned&r." );
	break;
    case POS_SLEEPING:
	strcpy( linebuf, "You are sleeping." );
	break;
    case POS_MEDITATING:
	ch->position = POS_RESTING;
    case POS_RESTING:
	strcpy( linebuf, "You are resting." );
	break;
    case POS_SITTING:
	strcpy( linebuf, "You are sitting." );
	break;
    case POS_GETTING_UP:
	strcpy( linebuf, "You are struggling to retain you feet." );
	break;
    case POS_SMASHED:
	strcpy( linebuf, "You are flat on your back." );
	break;
    default:
	ch->position = POS_STANDING;
    case POS_STANDING:
	strcpy( linebuf, "You are standing." );
	break;
    case POS_FIGHTING:
	strcpy( linebuf, "You are fighting." );
	break;
    }
    buffer_strcat( buf, colour_strcentre( tmp, linebuf, 71 ) );
    buffer_strcat( buf, "&b|&n\n\r" );

    if( IS_IMMORTAL( ch ) && xIS_SET( get_char( ch )->act, PLR_INFO ) )
    {
	buffer_strcat( buf, "&w- - - ----------------&K=> &b(----------&B[&KX"
		       "&B]&b----------) &K<=&w---------------- - - -&n\n\r" );

	buffer_strcat( buf, " &b|&g " );
	sprintf( linebuf, "&gBamfin:  %s",
		 ( ch->pcdata != NULL && ch->pcdata->bamfin[0] != '\0' )
		 ? ch->pcdata->bamfin : "Not set." );
	buffer_strcat( buf, colour_strpad( tmp, linebuf, 70 ) );
	buffer_strcat( buf, "&b|&n\n\r" );
	buffer_strcat( buf, " &b|&g " );
	sprintf( linebuf, "&gBamfout: %s",
		 ( ch->pcdata != NULL && ch->pcdata->bamfout[0] != '\0' )
		 ? ch->pcdata->bamfout : "Not set." );
	buffer_strcat( buf, colour_strpad( tmp, linebuf, 70 ) );
	buffer_strcat( buf, "&b|&n\n\r" );
    }

    if( ch->short_descr && ch->short_descr[0] )
    {
	strcpy( linebuf, ch->short_descr );
	p = &linebuf[0];
	while( ( p = strstr( p, "&x" ) ) )
	    *( p + 1 ) = 'B';
    }
    else
	strcpy( linebuf, ch->name );
    if( colour_strlen( linebuf ) < 19 )
    {
	buffer_strcat( buf, "&w- - - ----------------&K=>&b (--&B" );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, 19 ) );
	buffer_strcat( buf, "&b--) &K<=&w---------------- - - -&n\n\r" );
    }
    else
    {
	buffer_strcat( buf, "&w- - - ----------------&K=>&B" );
	buffer_strcat( buf, colour_strcentre( tmp, linebuf, 27 ) );
	buffer_strcat( buf, "&K<=&w---------------- - - -&n\n\r" );
    }

    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_magic( CHAR_DATA *ch, const char *argument )
{
    BUFFER *buf = buffer_new( MAX_STRING_LENGTH );
    int i;

    buffer_strcat( buf, "&gMagic statistics.\n\r" );
    buffer_strcat( buf, "Sphere     Skill         Mana\n\r" );

    for( i = 0; i < MAGIC_MAX; ++i )
	bprintf( buf, "%s%-7s %3d (%3d) %8d/%5d\n\r",
		 magic_colour[i], magic_name[i],
		 IS_NPC( ch ) ? 2 + race_table[ch->race].mana_gain[i]
		 : ch->pcdata->perm_magic[i], get_magic( ch, i ),
		 ch->mana[i], ch->max_mana[i] );
    bprintf( buf, "&gTotal   --- (---) %8d/%5d&n\n\r",
	     total_mana( ch->mana ), total_mana( ch->max_mana ) );

    send_to_char( buf->data, ch );
    buffer_free( buf );
}


void do_affects( CHAR_DATA *ch, const char *argument )
{
    AFFECT_DATA *paf;
    bool printed = FALSE;
    char buf[MAX_INPUT_LENGTH];

    if( ch->affected )
    {
	for( paf = ch->affected; paf; paf = paf->next )
	{
	    if( paf->deleted
		|| IS_SET( skill_table[paf->type].skill_type,
			   SKILL_TYPE_ENCHANTMENT ) )
		continue;

	    if( !printed )
	    {
		send_to_char( "&yYou are affected by:&n\n\r", ch );
		printed = TRUE;
	    }

	    if( ch->level >= LEVEL_IMMORTAL )
		send_to_char( show_affect( buf, paf, TRUE, TRUE ), ch );
	    else if( ch->level >= 10 )
		send_to_char( show_affect( buf, paf, TRUE, FALSE ), ch );
	    else
		send_to_char( show_affect( buf, paf, FALSE, FALSE ), ch );
	}
    }
    if( !printed )
    {
	send_to_char( "&gYou have no magical affects.&n\n\r", ch );
    }

    return;
}


const char *const day_name[] =
{
    "the Moon", "the Bull", "Deception", "Thunder", "Freedom",
    "the Great Gods", "the Sun", NULL
};

const char *const month_name[] =
{
    "Winter", "the Winter Wolf", "the Frost Giant", "the Old Forces",
    "the Grand Struggle", "the Spring", "Nature", "Futility", "the Dragon",
    "the Sun", "the Heat", "the Battle", "the Dark Shades", "the Shadows",
    "the Long Shadows", "the Ancient Darkness", "the Great Evil", NULL
};

void do_time( CHAR_DATA *ch, const char *argument )
{
    const char *suf;
    int day;

    day = ch->in_room->area->plane->time.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";

    charprintf( ch,
		"&gIt is %d o'clock %s, Day of %s, %d%s the Month of %s.&n\n\r",
		( ch->in_room->area->plane->time.hour % 12 == 0 ) ? 12
		: ch->in_room->area->plane->time.hour % 12,
		ch->in_room->area->plane->time.hour >= 12 ? "pm" : "am",
		day_name[day % 7], day, suf,
		month_name[ch->in_room->area->plane->time.month] );
    if( ch->in_room->area->plane->weather.sky == SKY_CLOUDLESS
	&& ( ch->in_room->area->plane->time.hour <= 6
	  || ch->in_room->area->plane->time.hour >= 18 ) )
    {
	day = abs( ( ch->in_room->area->plane->time.day + 30 ) % 34 - 17 );
	if( day > 13 )
	    send_to_char( "&gThere is a full moon shining above.&n\n\r", ch );
	else if( day < 5 )
	    send_to_char( "&gYou can make out the thin sliver of the new moon above.&n\n\r", ch );
	else
	    send_to_char( "&gA half moon lends a pale glow to the night sky.&n\n\r", ch );
    }

    charprintf( ch, "&mDalekenMud&g started up %s\n\r", myctime( &boot_time ) );
    charprintf( ch, "The system time is %s&n\n\r", myctime( &current_time ) );
    if( SysInfo->down_time > 0 )
	charprintf( ch, "&r%s will be at %s&n\n\r",
		    IS_SET( SysInfo->flags, SYSINFO_REBOOT )
		    ? "Reboot" : "Shutdown", myctime( &SysInfo->down_time ) );
    return;
}


void do_weather( CHAR_DATA *ch, const char *argument )
{
    int temp;
    static const char *const sky_look[ 8 ] =
    {
	"cloudless", "cloudy", "rainy",	"lit by flashes of lightning",
	"slightly overcast", "scattered with a few flakes",
	"filled with flakes", "a blizzard of white"
    };

    if( IS_OUTSIDE( ch ) )
    {
	charprintf( ch, "The sky is %s ",
		    ( ch->in_room->area->plane->time.month <= 4
		      || ch->in_room->area->plane->time.month >= 15 )
		    ? sky_look[ch->in_room->area->plane->weather.sky + 4]
		    : sky_look[ch->in_room->area->plane->weather.sky] );
	charprintf( ch, "and a %s %sward %s blows.\n\r",
		    ( ch->in_room->area->plane->weather.temperature < -20 ) ? "icey"
		    : ( ch->in_room->area->plane->weather.temperature < 0 ) ? "cool"
		    : ( ch->in_room->area->plane->weather.temperature < 20 ) ? "warm"
		    : "boiling",
		    dir_name[abs( ch->in_room->area->plane->weather.winddir ) % 3],
		    ch->in_room->area->plane->weather.windspeed <= 20 ? "breeze"
		    : ch->in_room->area->plane->weather.windspeed <= 50 ? "wind"
		    : ch->in_room->area->plane->weather.windspeed <= 80 ? "gust"
		    : "torrent" );
    }
    temp = get_room_temp( ch->in_room ) + 20;
    charprintf( ch, "&gThe room seems to be about &r%d&g degree%s.&n\n\r",
		temp, ( temp == 1 ) ? "" : "s" );
    return;
}


bool help_keyword( const char *word, const char *list )
{
    char name[MAX_INPUT_LENGTH];

    for( ;; )
    {
	list = one_argument( list, name );
	if( name[0] == '\0' )
	    break;
	if( is_name( word, name ) )
	    return TRUE;
    }
    return FALSE;
}


void do_help( CHAR_DATA *ch, const char *argument )
{
    HELP_DATA *pHelp;
    bool hfound = FALSE;

    /* modification to show multiple helps by Robin Goodall
       from merc mailing list, then changed again by Symposium
       because of the changes to is_name( ) */

    int num;
    int count = 0;
    char arg[MAX_INPUT_LENGTH];
    BUFFER *buf = buffer_new( MAX_STRING_LENGTH );

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

    num = number_argument( argument, arg );

    for( pHelp = help_first; pHelp; pHelp = pHelp->next )
    {
	if( abs( pHelp->level ) > get_trust( ch ) )
	    continue;

	if( help_keyword( arg, pHelp->keyword )
	    && ( ++count == num || num == 0 ) )
	{
	    if( !num )
	    {
		bprintf( buf, "&b  %d&g: %s&n\n\r", count, pHelp->keyword );
		hfound = TRUE;
		continue;
	    }
	    if( num && pHelp->level >= 0 && str_cmp( argument, "imotd" ) )
	    {
		buffer_strcat( buf, "&c" );
		buffer_strcat( buf, pHelp->keyword );
		buffer_strcat( buf, "\n\r" );
		buffer_strcat( buf, LINE_SEPARATOR );
	    }

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

    if( !hfound )
    {
#if defined( NOHELP_FILE )
	if( IS_SET( SysInfo->flags, SYSINFO_LOGNOHELP ) )
	{
	    for( num = 0; argument[num]; num++ )
		if( !isalpha( argument[num] ) )
		    hfound = TRUE;
	    if( !hfound )
		append_file( ch, NOHELP_FILE, argument );
	}
#endif
	send_to_char( "&gNo help on that word, try 'help contents'.&n\n\r", ch );
    }
    else
	send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void add_side( BUFFER *buf, int side )
{
    switch( side )
    {
    case -1:
	break;
    default:
	buffer_strcat( buf, "&w  ||  " );
	break;
    case 1:
	buffer_strcat( buf, "&w )&K()&w( " );
	break;
    case 2:
	buffer_strcat( buf, "&w `][' " );
	break;
    }
}


void who_show_char( CHAR_DATA *ch, CHAR_DATA *wch, BUFFER *buf, int side )
{
    char const *class;
    char clan_title[50];
    char line[MAX_INPUT_LENGTH];
    char tmp[MAX_INPUT_LENGTH];

    add_side( buf, side );
    line[0] = '\0';
    if( wch->level < LEVEL_IMMORTAL && get_aspire_class( wch ) >= 0 )
	sprintf( line, "&g[( &y%3d %s&g )( &y%3d %s&g )&y %s&y %c&g]",
		 wch->level,
		 class_table[wch->class].who_name,
		 wch->sublevel,
		 class_table[get_aspire_class( wch )].who_name,
		 race_table[wch->race].who_name,
		 wch->sex == SEX_MALE ? 'M' :
		 wch->sex == SEX_FEMALE ? 'F' : 'N' );
    else if( wch->level < LEVEL_IMMORTAL )
	/* Mortals */
	sprintf( line, "&g[&y%3d %s %s&y %c&g]",
		 wch->level,
		 class_table[wch->class].who_name,
		 race_table[wch->race].who_name,
		 wch->sex == SEX_MALE ? 'M' :
		 wch->sex == SEX_FEMALE ? 'F' : 'N' );
    else			/* Immortals */
    {
	if( wch->level >= L_OVL )
	    class = "Overlord";
	else if( wch->level >= L_MAS )
	    class = "Master";
	else if( wch->level >= L_MBLD )
	    class = "Architect";
	else if( wch->level >= L_SEN )
	    class = "Deity";
	else if( wch->level >= L_ARCH )
	    class = "Archangel";
	else if( wch->level >= L_JUN )
	    class = "Acolyte";
	else if( wch->level >= L_APP )
	    class = "Apprentice";
	else if( wch->level >= L_BLD )
	    class = "Builder";
	else if( wch->level >= L_ANG )
	    class = "Angel";
	else
	    class = "Immortal";

	if( wch->pcdata->immname && wch->pcdata->immname[0] )
	    class = wch->pcdata->immname;

	sprintf( line, "&g[&y%s&n&y %s&g]",
		 colour_strpad( tmp, class, 12 ),
		 race_table[wch->race].who_name );
    }

    clan_title[0] = '\0';
    if( is_clan( wch ) && wch->pcdata->clan_rank >= RANK_CLANHERO )
	sprintf( clan_title, "%s ", rank_name( wch->pcdata->clan_rank,
					       wch->pcdata->clan->clan_type ) );
    sprintf( line + strlen( line ), " %s%s%s%s%s%s%s%s&g %s%s",
	     xIS_SET( wch->act, PLR_WIZINVIS ) ? "&C(WIZINVIS)" : "",
	     is_clan_enemy( wch, ch ) ? "&R<ENEMY>" : "",
	     wch->pcdata->bounty > 0 ? "&Y(BOUNTY)" : "",
	     xIS_SET( wch->act, PLR_OUTLAW ) ? "&R(OUTLAW)" : "",
	     xIS_SET( wch->act, PLR_BATTLE ) ? "&r(BATTLE)" : "",
	     xIS_SET( wch->act, PLR_AFK ) ? "<AFK>" : "",
	     xIS_SET( wch->act, PLR_BUSY ) ? "&w<BUSY>" : "",
	     xIS_SET( wch->act, PLR_KEYLOCK ) ? "&Y<KEYLOCK>" : "",
	     clan_title, wch->name );
    if( wch->pcdata->religion )
	sprintf( line + strlen( line ),
		 " &b<%s&b>", wch->pcdata->religion->display_name );
    if( wch->pcdata->clan )
	sprintf( line + strlen( line ), " &%c{%s&%c}",
		 is_clan_enemy( wch, ch ) ? 'R' : 'r',
		 wch->pcdata->clan->display_name,
		 is_clan_enemy( wch, ch ) ? 'R' : 'r' );
    buffer_strcat( buf, colour_strpad( tmp, line, 66 ) );
    add_side( buf, side );
    buffer_strcat( buf, "&n\n\r" );
}


/*
 * New 'who' command originally by Alander of Rivers of Mud.
 */
void do_who( CHAR_DATA *ch, const char *argument )
{
    DESCRIPTOR_DATA *d;
    BUFFER *buf = buffer_new( MAX_STRING_LENGTH );
    int iLevelLower = 0;
    int iLevelUpper = MAX_LEVEL;
    int nNumber = 0;
    int nMatch;
    int i, iLord, side = 0;
    bool fClass[MAX_CLASS];
    bool fClassRestrict = FALSE;
    bool fRace[MAX_RACE];
    bool fRaceRestrict = FALSE;
    bool fImmortal = FALSE;
    bool fMultiClass = FALSE;
    bool fLord = FALSE;
    bool found;
    CHAR_DATA *wch;

    /*
     * Set default arguments.
     */
    for( i = 0; i < MAX_CLASS; i++ )
	fClass[i] = FALSE;
    for( i = 0; i < MAX_RACE; i++ )
	fRace[i] = FALSE;

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

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

	if( is_number( arg ) )
	{
	    switch( ++nNumber )
	    {
	    case 1:
		iLevelLower = atoi( arg );
		break;
	    case 2:
		iLevelUpper = atoi( arg );
		break;
	    default:
		send_to_char( "Only two level numbers allowed.\n\r", ch );
		return;
	    }
	}
	else if( !str_cmp( arg, "imm" ) )	/* add flags here */
	    fImmortal = TRUE;
	else if( !str_cmp( arg, "multi" ) )
	    fMultiClass = TRUE;
	else if( !str_cmp( arg, "lord" ) )
	    fLord = TRUE;
	else
	{
	    if( strlen( arg ) < 3 )
	    {
		send_to_char( "Classes must be longer than that.\n\r", ch );
		return;
	    }

	    /*
	     * Look for classes to turn on.
	     */
	    arg[3] = '\0';
	    fClassRestrict = TRUE;
	    for( i = 0; i < MAX_CLASS; i++ )
	    {
		if( !str_cmp( arg, class_table[i].who_name ) )
		{
		    fClass[i] = TRUE;
		    break;
		}
	    }

	    if( i == MAX_CLASS )
	    {
		fClassRestrict = FALSE;
		fRaceRestrict = TRUE;
		for( i = 0; i < MAX_RACE; i++ )
		{
		    if( !str_cmp( arg, race_table[i].who_name ) )
		    {
			fRace[i] = TRUE;
			break;
		    }
		}
	    }

	    if( i == MAX_RACE )
	    {
		send_to_char( "Invalid who parameter.\n\r", ch );
		return;
	    }
	}
    }

    /*
     * Now show matching chars.
     */
    buffer_strcat( buf, "&w______________________________________________________________________________&n\n\r" );
    nMatch = 0;
    /* Immortals */
    found = FALSE;
    if( !fLord && !fMultiClass && !fClassRestrict )
	for( d = descriptor_list; d; d = d->next )
	{
	    wch = ( d->original ) ? d->original : d->character;

	    if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
		|| !can_see( ch, wch ) )
		continue;

	    if( wch->level < iLevelLower
		|| wch->level > iLevelUpper
		|| ( fRaceRestrict && !fRace[wch->race] )
		|| wch->level <= LEVEL_HERO )
		continue;

	    if( !found )
	    {
		add_side( buf, ++side );
		buffer_strcat(
		    buf, "&w--------------------------&K>>>&cIMMORTALS&K<<<&w-------------------------" );
		add_side( buf, side );
		buffer_strcat( buf, "&n\n\r" );
		found = TRUE;
	    }
	    nMatch++;

	    who_show_char( ch, wch, buf, ++side );
	}
    /* Multiclassing lords */
    if( !fImmortal && !fMultiClass )
	for( iLord = 5; iLord > 0; iLord-- )
	{
	    if( found )
		buffer_strcat( buf, "&w  ||                                                                      ||&n\n\r" );
	    found = FALSE;
	    for( d = descriptor_list; d; d = d->next )
	    {
		wch = ( d->original ) ? d->original : d->character;

		if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
		    || !can_see( ch, wch ) )
		    continue;

		if( wch->level < iLevelLower
		    || wch->level > iLevelUpper
		    || wch->level > LEVEL_HERO
		    || ( fClassRestrict && !fClass[wch->class] )
		    || ( fRaceRestrict && !fRace[wch->race] )
		    || get_first_class( wch ) == CLASS_NONE
		    || get_aspire_class( wch ) != CLASS_NONE
		    || get_lord_level( wch) != iLord )
		    continue;

		if( !found )
		{
		    add_side( buf, ++side );
		    bprintf(
			buf, "&w-----------------------&K>>>&r%d%s Order Lords&K<<<&w----------------------&n",
			iLord,
			(iLord == 1) ? "st" : (iLord == 2) ? "nd" :
			(iLord == 3) ? "rd" : "th" );
		    add_side( buf, side );
		    buffer_strcat( buf, "&n\n\r" );
		    found = TRUE;
		}
		nMatch++;

		who_show_char( ch, wch, buf, ++side );
	    }
	}
    /* Multiclassing dudes */
    if( found )
	buffer_strcat( buf, "&w  ||                                                                      ||&n\n\r" );
    found = FALSE;
    if( !fImmortal && !fLord )
	for( d = descriptor_list; d; d = d->next )
	{
	    wch = ( d->original ) ? d->original : d->character;

	    /*
	     * Check for match against restrictions.
	     * Don't use trust as that exposes trusted mortals.
	     */
	    if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
		|| !can_see( ch, wch ) )
		continue;

	    if( wch->level < iLevelLower
		|| wch->level > iLevelUpper
		|| wch->level > LEVEL_HERO
		|| ( fClassRestrict && !fClass[wch->class] )
		|| ( fRaceRestrict && !fRace[wch->race] )
		|| get_aspire_class( wch ) < 0 )
		continue;

	    if( !found )
	    {
		add_side( buf, ++side );
		buffer_strcat(
		    buf, "&w---------------------------&K>>>&mHEROES&K<<<&w---------------------------&n" );
		add_side( buf, side );
		buffer_strcat( buf, "&n\n\r" );
		found = TRUE;
	    }
	    nMatch++;

	    who_show_char( ch, wch, buf, ++side );
	}

    /* Low mortals */
    if( found )
	buffer_strcat( buf, "&w  ||                                                                      ||&n\n\r" );
    found = FALSE;
    if( !fImmortal && !fLord && !fMultiClass )
	for( d = descriptor_list; d; d = d->next )
	{
	    wch = ( d->original ) ? d->original : d->character;

	    if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
		|| !can_see( ch, wch ) )
		continue;

	    if( wch->level < iLevelLower
		|| wch->level > iLevelUpper
		|| wch->level > LEVEL_HERO
		|| ( fClassRestrict && !fClass[wch->class] )
		|| ( fRaceRestrict && !fRace[wch->race] )
		|| get_aspire_class( wch ) != CLASS_NONE
		|| get_first_class( wch ) != CLASS_NONE )
		continue;

	    if( !found )
	    {
		add_side( buf, ++side );
		buffer_strcat(
		    buf, "&w---------------------------&K>>>&wMORTALS&K<<<&w--------------------------&n" );
		add_side( buf, side );
		buffer_strcat( buf, "&n\n\r" );
		found = TRUE;
	    }
	    nMatch++;

	    who_show_char( ch, wch, buf, ++side );
	}
    if( found )
	buffer_strcat( buf, "&w  ||                                                                      ||&n\n\r" );
    bprintf( buf, "&w_/&K<>&w\\_________________________&b[&w%3d players found&b]&w________________________/&K<>&w\\_&n\n\r",
	     nMatch, nMatch == 1 ? "" : "s" );
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


/*
 * Contributed by Kaneda
 */
void do_whois( CHAR_DATA *ch, const char *argument )
{
    DESCRIPTOR_DATA *d;
    BUFFER *buf = buffer_new( MAX_INPUT_LENGTH );
    char name[MAX_INPUT_LENGTH];

    one_argument( argument, name );

    if( name[0] == '\0' )
    {
	send_to_char( "Usage:  whois <name>\n\r", ch );
	return;
    }

    name[0] = UPPER( name[0] );

    for( d = descriptor_list; d; d = d->next )
    {
	CHAR_DATA *wch;

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

	if( IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
	    || !can_see( ch, wch ) )
	    continue;

	if( str_prefix( name, wch->name ) )
	    continue;

	who_show_char( ch, wch, buf, -1 );
    }

    if( buf->len == '\0' )
	send_to_char( "&gNo one matches the given criteria.&n\n\r", ch );
    else
	send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_inventory( CHAR_DATA *ch, const char *argument )
{
    send_to_char( "&mYou are carrying:&n\n\r", ch );
    show_list_to_char( ch->carrying, ch, TRUE, TRUE );
    return;
}


void do_equipment( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int iWear;
    bool found;
    char buf[MAX_STRING_LENGTH];

    send_to_char( "&mYou are using:&n\n\r", ch );
    found = FALSE;
    for( iWear = 1; iWear < MAX_WEAR; iWear++ )
    {
	if( !( obj = get_eq_char( ch, iWear ) ) )
	    continue;
	do
	{
	    send_to_char( wear_table[iWear].where_name, ch );
	    if( can_see_obj( ch, obj ) )
	    {
		send_to_char( format_obj_to_char( buf, obj, ch, TRUE ), ch );
		send_to_char( "\n\r", ch );
	    }
	    else
	    {
		send_to_char( "something.\n\r", ch );
	    }
	    for( obj = obj->next_content; obj; obj = obj->next_content )
		if( !obj->deleted && obj->wear_loc == iWear )
		    break;
	}
	while( obj );
	found = TRUE;
    }

    if( !found )
	send_to_char( "Nothing.\n\r", ch );

    return;
}


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

    argument = one_argument( argument, arg1 );
    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 ) ) )
    {
	send_to_char( "You do not have that item.\n\r", ch );
	return;
    }

    if( arg2[0] == '\0' )
    {
	for( obj2 = ch->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 ) ) )
	{
	    char new_arg2[MAX_INPUT_LENGTH];
	    int number;

	    /*
	     * Strip off number argument, subtract one, paste it together
	     */
	    number = number_argument( arg2, arg2 );
	    if( number > 1 )
		number--;
	    sprintf( new_arg2, "%d.%s", number, arg2 );

	    if( !( obj2 = get_obj_wear( ch, new_arg2 ) ) )
	    {
		send_to_char( "You do not have that item.\n\r", ch );
		return;
	    }

	    if( ( obj1->wear_flags & obj2->wear_flags & ~ITEM_TAKE ) == 0 )
	    {
		send_to_char( "They are not comparable items.\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_ARMOUR:
	    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( obj2->wear_loc != WEAR_NONE )
	{
	    if( value1 == value2 )
		msg = "$p and $P (equipped) look about the same.";
	    else if( value1 > value2 )
		msg = "$p looks better than $P (equipped).";
	    else
		msg = "$p looks worse than $P (equipped).";
	}
	else
	{
	    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( msg, ch, obj1, obj2, TO_CHAR );
    return;
}


void do_credits( CHAR_DATA *ch, const char *argument )
{
    do_help( ch, "diku" );
    return;
}


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

    if( !check_blind( ch ) )
	return;

    buf = buffer_new( MAX_INPUT_LENGTH );
    num = number_argument( argument, arg );

    if( arg[0] == '\0' )
    {
	bprintf(
	    buf, "&b{&c%4d&b-&c%4d &r[&y%4d&r]&b} &m%s &r[&y%-10s&r]&n\n\r"
	    "&mPlayers near you:\n\r",
	    ch->in_room->area->min, ch->in_room->area->max,
	    ch->in_room->area->ave,
	    colour_strpad( arg, ch->in_room->area->name, 30 ),
	    ch->in_room->area->plane->name );
	if( ch->in_room->area->description
	    && ch->in_room->area->description[0] != '\0' )
	{
	    buffer_strcat( buf, "&w" );
	    if( ch->in_room->area->description[0] == '.' )
		buffer_strcat( buf, ch->in_room->area->description + 1 );
	    else
		buffer_strcat( buf, ch->in_room->area->description );
	    buffer_strcat( buf, "&n" );
	}

	found = FALSE;
	for( d = descriptor_list; d; d = d->next )
	{
	    if( !IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
		&& ( victim = d->character )
		&& !IS_NPC( victim )
		&& victim->in_room
		&& victim->in_room->area == ch->in_room->area
		&& can_see( ch, victim ) )
	    {
		found = TRUE;
		bprintf( buf, "&g%-28s &y%s&n\n\r",
			 capitalize( victim->name ), victim->in_room->name );
	    }
	}
	if( !found )
	    buffer_strcat( buf, "&gNone&n\n\r" );
    }
    else
    {
	found = FALSE;
	for( victim = char_list; victim; victim = victim->next )
	{
	    if( !victim->in_room
		|| IS_AFFECTED( victim, AFF_HIDE )
		|| IS_AFFECTED( victim, AFF_SNEAK ) )
		continue;

	    if( victim->in_room->area == ch->in_room->area
		&& can_see( ch, victim )
		&& is_char_name( victim, arg )
		&& --num == 0 )
	    {
		found = TRUE;
		bprintf( buf, "&g%-28s &y%s&n\n\r",
			 capitalize( PERS( victim, ch ) ), victim->in_room->name );
		break;
	    }
	}
	if( !found )
	    bprintf( buf, "&gYou didn't find any %s.&n\n\r", arg );
    }
    send_to_char( buf->data, ch );
    buffer_free( buf );

    return;
}


void do_consider( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;
    const char *msg;
    int diff;

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

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

    if( !IS_NPC( victim ) && !xIS_SET( victim->act, PLR_OUTLAW ) )
    {
	send_to_char( "The gods do not accept this type of sacrifice.\n\r",
		     ch );
	return;
    }

    diff = victim->level - ch->level;
    if( diff <= -10 )
	msg = "$N is young and green when compared to you.";
    else if( diff <= -5 )
	msg = "$N is a little less experienced than yourself.";
    else if( diff <= -2 )
	msg = "$N is less experienced but may still be a problem.";
    else if( diff <= 1 )
	msg = "$N is vaguely of the same caliber as yourself.";
    else if( diff <= 4 )
	msg = "$N is older and most probably, tougher.";
    else if( diff <= 9 )
	msg = "$N has been around a lot longer than yourself.";
    else if( diff < 20 )
	msg = "$N was beating up adventurers well before you were born.";
    else
	msg = "The look in $N's eyes translates to instant death.";
    act( msg, ch, NULL, victim, TO_CHAR );

    diff = victim->hit * 100 / ch->hit;
    if( diff > 300 )
	msg = "$N could die and have more hit points than you!";
    else if( diff > 200 )
	msg = "$N has way more hit points than you!";
    else if( diff > 150 )
	msg = "$N has heaps more hit points than you.";
    else if( diff > 125 )
	msg = "$N has a fair bit more hit points than you.";
    else if( diff > 110 )
	msg = "$N has a little more hit points than you.";
    else if( diff > 90 )
	msg = "$N has about the same hit points as you.";
    else if( diff > 75 )
	msg = "$N has a little less hit points than you.";
    else if( diff > 60 )
	msg = "$N has a fair bit less hit points than you.";
    else if( diff > 40 )
	msg = "$N has a lot less hit points than you.";
    else if( diff > 20 )
	msg = "$N has very little hit points compared to you.";
    else
	msg = "$N doesn't really have any hit points to mention.";
    act( msg, ch, NULL, victim, TO_CHAR );

    diff = GET_AC( ch ) - GET_AC( victim );
    diff = diff * 10 / ch->level;
    if( diff > 100 )
	msg = "You are a walking magnet for weaponry.";
    else if( diff > 50 )
	msg = "You are walking around naked in comparison.";
    else if( diff > 30 )
	msg = "You are not very well armoured next to $N.";
    else if( diff > 15 )
	msg = "You are less armoured than $N.";
    else if( diff > 5 )
	msg = "You are slightly less armoured than $N.";
    else if( diff > -5 )
	msg = "You are armoured to about the same degree as $N.";
    else if( diff > -15 )
	msg = "You are a little better armoured than $N.";
    else if( diff > -30 )
	msg = "You are well armoured compared to $N.";
    else if( diff > -50 )
	msg = "You are strongly armoured in comparison to $N.";
    else if( diff > -100 )
	msg = "You are plated in steel when compared to $N.";
    else
	msg = "You could stand motionless before $N would have a chance.";
    act( msg, ch, NULL, victim, TO_CHAR );

    diff = ( get_speed( ch ) - get_speed( victim ) ) * 2;
    diff += get_hitroll( ch ) - get_hitroll( victim );
    diff += get_damroll( ch ) - get_damroll( victim );
    diff = diff * 100 / ch->level;
    if( diff > 100 )
	msg = "You can hit HEAPS harder than $N.";
    else if( diff > 50 )
	msg = "You can hit a good bit harder than $N.";
    else if( diff > 30 )
	msg = "You can hit harder than $N.";
    else if( diff > 15 )
	msg = "You can hit a little harder than $N.";
    else if( diff > 5 )
	msg = "You can hit a tiny bit harder than $N.";
    else if( diff > -5 )
	msg = "You and $N hit with the same strength.";
    else if( diff > -15 )
	msg = "$N hits a little harder than you.";
    else if( diff > -30 )
	msg = "$N hits harder than you.";
    else if( diff > -50 )
	msg = "$N hits a good bit harder than you.";
    else if( diff > -100 )
	msg = "$N hits a lot harder than you.";
    else
	msg = "$N hits extremely hard when compared to you.";
    act( msg, ch, NULL, victim, TO_CHAR );
    return;
}


void set_title( CHAR_DATA *ch, char *title )
{
    char buf[MAX_STRING_LENGTH];

    if( IS_NPC( ch ) )
    {
	bug( "Set_title: NPC." );
	return;
    }

    buf[0] = '\0';

    if( isalpha( title[0] ) || isdigit( title[0] ) )
    {
	buf[0] = ' ';
	strcpy( buf + 1, title );
    }
    else
    {
	strcpy( buf, title );
    }

    free_string( ch->pcdata->title );
    ch->pcdata->title = str_dup( buf );
    return;
}


void do_title( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_INPUT_LENGTH];

    if( IS_NPC( ch ) )
	return;

    if( argument[0] != '\0' )
    {
	strcpy( buf, argument );
	str_limit( buf, 50 );

	set_title( ch, buf );
    }

    send_to_char( "You are now titled as:\n\r", ch );
    charprintf( ch, "    &g%s%s&n\n\r", ch->name, ch->pcdata->title );
    return;
}


void do_description( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    if( IS_NPC( ch ) )
	return;

    if( !str_cmp( argument, "write" ) )
    {
	string_edit( ch, &ch->description );
	return;
    }
    else if( argument[0] != '\0' )
    {
	buf[0] = '\0';
	if( argument[0] == '+' )
	{
	    if( ch->description )
		strcat( buf, ch->description );
	    argument++;
	    while( isspace( *argument ) )
		argument++;
	}

	if( strlen( buf ) + strlen( argument ) >= MAX_STRING_LENGTH - 2 )
	{
	    send_to_char( "Description too long.\n\r", ch );
	    return;
	}

	strcat( buf, argument );
	strcat( buf, "\n\r" );
	free_string( ch->description );
	ch->description = str_dup( buf );
    }

    send_to_char( "&gYour description is:&n\n\r", ch );
    send_to_char( ch->description ? ch->description : "( None ).\n\r", ch );
    return;
}



void do_report( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_INPUT_LENGTH];

    charprintf( ch, "&mYou report: %d/%d hp %d/%d mana %d/%d mv %d tnl.&n\n\r",
		ch->hit, ch->max_hit,
		total_mana( ch->mana ), total_mana( ch->max_mana ),
		ch->move, ch->max_move,
		ch->exp / 100 );

    sprintf( buf,
	     "&m$n reports: %d/%d hp %d/%d mana %d/%d mv %d tnl.&n",
	     ch->hit, ch->max_hit,
	     total_mana( ch->mana ), total_mana( ch->max_mana ),
	     ch->move, ch->max_move,
	     ch->exp / 100 );

    act( buf, ch, NULL, NULL, TO_ROOM );

    return;
}


int adept_level( CHAR_DATA *ch, int sn )
{
    int adept;
    if( !can_prac( ch, sn ) )
	return 0;
    adept = IS_NPC( ch ) ? 100 : class_table[ch->class].skill_adept;
    if( get_trust( ch ) > L_SEN )
    {
	adept = get_trust( ch ) - 750;
    }
    if( !IS_NPC( ch ) )
    {
	if( ch->class < AVAIL_CLASS
	    && ch->pcdata->multi_class[ch->class] == CLASS_ASPIRING )
	    if( ch->sublevel >= skill_table[sn].skill_level[ch->class] )
		adept = 100;
	if( get_first_class( ch ) != CLASS_NONE
	    && get_second_class( ch ) == CLASS_NONE )
	    if( ch->level >= skill_table[sn].skill_level[
		get_first_class( ch )] )
		adept = 100;
    }
    return adept;
}


void show_practice( CHAR_DATA *ch, bool zero )
{
    BUFFER *outbuf = buffer_new( MAX_STRING_LENGTH );
    CHAR_DATA *mob;
    int sn;
    int col, adept;

    for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
    {
	if( mob->deleted )
	    continue;
	if( IS_NPC( mob ) && xIS_SET( mob->act, ACT_PRACTICE ) )
	    break;
    }

    bprintf( outbuf, "&cSkill/Spell Name              Cost   %   | ...\n\r" );
    buffer_strcat( outbuf, LINE_SEPARATOR );

    col = 0;
    for( sn = 1; sn < MAX_SKILL; sn++ )
    {
	if( !skill_table[sn].name )
	    break;
	adept = adept_level( ch, sn );

	if( !can_prac( ch, sn ) && ch->pcdata->learned[sn] <= 0 )
	    continue;

	if( zero && ch->pcdata->learned[sn] >= UMIN( adept, 100 ) )
	    continue;

	if( ch->pcdata->learned[sn] <= 0 && !mob && !zero )
	    continue;

	bprintf( outbuf, "&g%-28.28s &y%4d %3d%%",
		 skill_table[sn].name,
		 total_mana_cost( ch, sn ),
		 ch->pcdata->learned[sn] );
	if( ++col % 2 == 0 )
	    buffer_strcat( outbuf, "&n\n\r" );
	else
	    buffer_strcat( outbuf, " &n| " );
    }

    if( col % 2 != 0 )
	buffer_strcat( outbuf, "&n\n\r" );

    buffer_strcat( outbuf, LINE_SEPARATOR );
    bprintf( outbuf, "&gYou have &c%d&g practice sessions left.&n\n\r",
	     ch->practice );
    send_to_char( outbuf->data, ch );
    buffer_free( outbuf );
    return;
}


void do_practice( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_STRING_LENGTH];
    char arg2[MAX_STRING_LENGTH];
    int numm = 0;
    int sn;
    bool zero = FALSE;

    if( IS_NPC( ch ) )
	return;

    if( !strcmp( argument, "0" ) )
	zero = TRUE;
    else
    {
	argument = one_argument( argument, arg1 );
	one_argument( argument, arg2 );
	if( arg1[0] != '\0' && !is_number( arg2 ) )
	{
	    send_to_char( "Syntax: practice <skill> <percentage>\n\r", ch );
	    return;
	}
	numm = atoi( arg2 );
	if( arg1[0] != '\0' && numm <= 0 )
	{
	    send_to_char( "Syntax: practice <skill> <percentage>\n\r", ch );
	    return;
	}
    }

    if( ch->level < 2 )
    {
	send_to_char(
	    "You must be second level to practice.  Go train instead!\n\r",
			ch );
	return;
    }

    if( zero || arg1[0] == '\0' )
    {
	show_practice( ch, zero );
	return;
    }
    else
    {
	CHAR_DATA *mob;
	int adept;

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

	for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
	{
	    if( mob->deleted )
		continue;
	    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 )
	{
	    send_to_char( "You have no practice sessions left.\n\r", ch );
	    return;
	}
	else if( ch->practice < UMIN( power( numm, 5, 15 - get_curr_int( ch ) ), numm ) )
	{
	    send_to_char( "You need a few more practices to do that.\n\r", ch );
	    return;
	}

	if( ( sn = skill_lookup( arg1 ) ) < 0
	    || !can_prac( ch, sn ) )
	{
	    send_to_char( "You can't practice that.\n\r", ch );
	    return;
	}

	/*
	 * Check that the mob knows the spell sphere required.
	 * This is tedious but I can't see another way at the moment.
	 * --Sym
	 */
	if( skill_table[sn].min_mana[MAGIC_AIR] > get_magic( ch, MAGIC_AIR ) * 5
	    && !xIS_SET( mob->act, ACT_PRAC_AIR ) )
	{
	    act( "&B$n tells you 'I know too little about air magic'.",
		 mob, NULL, ch, TO_VICT );
	    ch->reply = mob;
	    return;
	}
	if( skill_table[sn].min_mana[MAGIC_EARTH] > get_magic( ch, MAGIC_EARTH ) * 5
	    && !xIS_SET( mob->act, ACT_PRAC_EARTH ) )
	{
	    act( "&B$n tells you 'I know too little about earth magic'.",
		 mob, NULL, ch, TO_VICT );
	    ch->reply = mob;
	    return;
	}
	if( skill_table[sn].min_mana[MAGIC_FIRE] > get_magic( ch, MAGIC_FIRE ) * 5
	    && !xIS_SET( mob->act, ACT_PRAC_FIRE ) )
	{
	    act( "&B$n tells you 'I know too little about fire magic'.",
		 mob, NULL, ch, TO_VICT );
	    ch->reply = mob;
	    return;
	}
	if( skill_table[sn].min_mana[MAGIC_SPIRIT] > get_magic( ch, MAGIC_SPIRIT ) * 5
	    && !xIS_SET( mob->act, ACT_PRAC_SPIRIT ) )
	{
	    act( "&B$n tells you 'I know too little about spirit magic'.",
		 mob, NULL, ch, TO_VICT );
	    ch->reply = mob;
	    return;
	}
	if( skill_table[sn].min_mana[MAGIC_WATER] > get_magic( ch, MAGIC_WATER ) * 5
	    && !xIS_SET( mob->act, ACT_PRAC_WATER ) )
	{
	    act( "&B$n tells you 'I know too little about water magic'.",
		 mob, NULL, ch, TO_VICT );
	    ch->reply = mob;
	    return;
	}

	adept = adept_level( ch, sn );

	numm = ( numm > adept - ch->pcdata->learned[sn] )
	    ? adept - ch->pcdata->learned[sn] : numm;
	if( ch->pcdata->learned[sn] >= adept )
	{
	    charprintf( ch, "You are already an adept of %s.\n\r",
			skill_table[sn].name );
	}
	else
	{
	    ch->practice -= URANGE( 1, power( numm, 5, 15 - get_curr_int( ch ) ), UMIN( numm, ch->practice ) );
	    ch->pcdata->learned[sn] += numm;
	    if( ch->pcdata->learned[sn] < adept )
	    {
		act( "You practice $T.",
		    ch, NULL, skill_table[sn].name, TO_CHAR );
		act( "$n practices $T.",
		    ch, NULL, skill_table[sn].name, TO_ROOM );
	    }
	    else
	    {
		ch->pcdata->learned[sn] = adept;
		act( "You are now an adept of $T.",
		    ch, NULL, skill_table[sn].name, TO_CHAR );
		act( "$n is now an adept of $T.",
		    ch, NULL, skill_table[sn].name, TO_ROOM );
	    }
	}
    }
    return;
}


/*
 * 'Wimpy' originally by Dionysos.
 */
void do_wimpy( CHAR_DATA *ch, const char *argument )
{
    int wimpy;

    if( argument[0] == '\0' )
	wimpy = ch->max_hit / 5;
    else
    {
	if( is_number( argument ) )
	    wimpy = atoi( argument );
	else
	{
	    send_to_char( "Get it right, number only please.", ch );
	    return;
	}
    }

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

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

    ch->wimpy = wimpy;
    charprintf( ch, "Wimpy set to %d hit points.\n\r", wimpy );
    return;
}


void do_password( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];

    if( IS_NPC( ch ) )
	return;

    /*
     * Can't use one_argument here because it smashes case.
     * So we just steal all its code.  Bleagh.
     */
    argument = first_arg( argument, arg1, FALSE );
    first_arg( argument, arg2, FALSE );

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

    if( strcmp( crypt( arg1, ch->pcdata->pwd ), ch->pcdata->pwd ) )
    {
	WAIT_STATE( ch, 10 * PULSE_PER_SECOND );
	send_to_char( "Wrong password.	Wait 10 seconds.\n\r", ch );
	if( xIS_SET( ch->act, PLR_NO_LAG ) )
	{
	    send_to_char( "Removing no_lag flag.\n\r", ch );
	    xREMOVE_BIT( ch->act, PLR_NO_LAG );
	}
	return;
    }

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

    free_string( ch->pcdata->pwd );
    ch->pcdata->pwd = str_dup( crypt( arg2, ch->name ) );
    save_char_obj( ch );
    send_to_char( "Ok.\n\r", ch );
    return;
}


void do_socials( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH];
    SOCIAL_DATA *social;
    int i;
    int col;

    buf1[0] = '\0';
    col = 0;
    for( i = 0; i < 27; ++i )
    {
	for( social = social_table[i]; social; social = social->next )
	{
	    sprintf( buf, "%-12s", social->name );
	    strcat( buf1, buf );
	    if( ++col % 6 == 0 )
		strcat( buf1, "\n\r" );
	}
    }

    if( col % 6 != 0 )
	strcat( buf1, "\n\r" );

    send_to_char( buf1, ch );
    return;
}


/*
 * Contributed by Alander.
 */
void do_commands( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH];
    int cmd;
    int col;

    buf1[0] = '\0';
    col = 0;
    for( cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++ )
    {
	if( cmd_table[cmd].level < LEVEL_HERO
	    && cmd_table[cmd].level <= get_trust( ch ) )
	{
	    sprintf( buf, "%-15s", cmd_table[cmd].name );
	    strcat( buf1, buf );
	    if( ++col % 5 == 0 )
		strcat( buf1, "\n\r" );
	    else
		strcat( buf1, " " );
	}
    }

    if( col % 5 != 0 )
	strcat( buf1, "\n\r" );

    send_to_char( buf1, ch );
    return;
}


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

    one_argument( argument, arg );

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

	send_to_char( "Channels:\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_AUCTION )
		      ? "&r[+AUCTION   ]&n" : "[-auction   ]", ch );
	send_to_char( " Automated auction channel.\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_CHAT )
		      ? "&y[+CHAT      ]&n" : "[-chat      ]", ch );
	send_to_char( " Main communication channel.\n\r", ch );

	if( get_trust( ch ) >= L_SEN )
	{
	    send_to_char( !IS_SET( ch->deaf, CHANNEL_SENIORTALK )
			  ? "&m[+SENIOR	   ]&n" : "[-senior    ]", ch );
	    send_to_char( " Senior immortal talk.\n\r", ch );
	}
	if( IS_IMMORTAL( ch ) )
	{
	    send_to_char( !IS_SET( ch->deaf, CHANNEL_IMMTALK )
			  ? "&C[+IMMTALK   ]&n" : "[-immtalk   ]", ch );
	    send_to_char( " Immortal talk channel.\n\r", ch );
	}

	send_to_char( !IS_SET( ch->deaf, CHANNEL_INFO )
		      ? "&M[+INFO      ]&n" : "[-info      ]", ch );
	send_to_char( " Informational channel.\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_MUSIC )
		      ? "&m[+MUSIC     ]&n" : "[-music     ]", ch );
	send_to_char( " Sing-song channel.\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_QUESTION )
		      ? "&b[+QUESTION  ]&n" : "[-question  ]", ch );
	send_to_char( " Question and answer channel.\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_SHOUT )
		      ? "&Y[+SHOUT     ]&n" : "[-shout     ]", ch );
	send_to_char( " Shouting, ranting etc...\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_YELL )
		      ? "&r[+YELL      ]&n" : "[-yell      ]", ch );
	send_to_char( " Area only communication.\n\r", ch );

	send_to_char( !IS_SET( ch->deaf, CHANNEL_GRATZ )
		      ? "&g[+GRATZ     ]&n" : "[-gratz     ]", ch );
	send_to_char( " Congratulations channel.\n\r", ch );

	if( is_clan( ch ) )
	{
	    send_to_char( !IS_SET( ch->deaf, CHANNEL_YELL )
			  ? "&K[+CLANTALK  ]&n" : "[-clantalk  ]", ch );
	    send_to_char( " Clan communication.\n\r", ch );
	}

	send_to_char( !IS_SET( ch->deaf, CHANNEL_DEAF )
		      ? "MASTER SWITCH: &BON&n\n\r"
		      : "MASTER SWITCH: off\n\r", ch );
    }
    else
    {
	int bit;
	bool fClear;

	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, "chat" ) )	    bit = CHANNEL_CHAT;
	else if( !str_cmp( arg + 1, "immtalk" ) )   bit = CHANNEL_IMMTALK;
	else if( !str_cmp( arg + 1, "info" ) )	    bit = CHANNEL_INFO;
	else if( !str_cmp( arg + 1, "music" ) )	    bit = CHANNEL_MUSIC;
	else if( !str_cmp( arg + 1, "question" ) )  bit = CHANNEL_QUESTION;
	else if( !str_cmp( arg + 1, "senior" ) )    bit = CHANNEL_SENIORTALK;
	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, "gratz" ) )	    bit = CHANNEL_GRATZ;
	else if( !str_cmp( arg + 1, "clan" ) )	    bit = CHANNEL_CLANTALK;
	else if( !str_cmp( arg + 1, "clantalk" ) )  bit = CHANNEL_CLANTALK;
	else if( !str_cmp( arg + 1, "master" ) )    bit = CHANNEL_DEAF;
	else if( !str_cmp( arg + 1, "deaf" ) )	    bit = CHANNEL_DEAF;
	else
	{
	    send_to_char( "Set or clear which channel?\n\r", ch );
	    return;
	}

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

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

    return;
}


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

    if( IS_NPC( ch ) )
	return;

    one_argument( argument, arg );

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

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

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

	send_to_char( xIS_SET( ch->act, PLR_AUTOGOLD )
		      ? "[+AUTOGOLD   ] You automatically get gold from corpses.\n\r"
		      : "[-autogold   ] You don't automatically get gold from corpses.\n\r"
		      ,ch );

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

	send_to_char( xIS_SET( ch->act, PLR_AUTOSPLIT )
		      ? "[+AUTOSPLIT  ] You split your gold with your group.\n\r"
		      : "[-autosplit  ] You don't split your gold with your group, tightarse.\n\r"
		      ,ch );

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

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

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

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

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

	send_to_char( xIS_SET( ch->act, PLR_COLOUR )
		      ? "[+COLOUR     ] You receive ANSI colour codes.\n\r"
		      : "[-colour     ] You don't receive ANSI colour codes.\n\r"
		      ,ch );

	send_to_char( xIS_SET( ch->act, PLR_NO_BEEP )
		      ? "[+NOBEEP     ] Other players cannot beep you.\n\r"
		      : "[-nobeep     ] You can be beeped by other players.\n\r"
		      , ch );

	send_to_char( xIS_SET( ch->act, PLR_BATTLEOTHER )
		      ? "[+BATTLEOTHER] You receive compressed battle messages for others.\n\r"
		      : "[-battleother] You see full battle messages for others.\n\r"
		      , ch );

	send_to_char( xIS_SET( ch->act, PLR_BATTLESELF )
		      ? "[+BATTLESELF ] You receive compressed battle messages for yourself.\n\r"
		      : "[-battleself ] You see full battle message for yourself.\n\r"
		      , ch );
	send_to_char( xIS_SET( ch->act, PLR_VT100 )
		      ? "[+VT100      ] You receive vt100 codes.\n\r"
		      : "[-vt100      ] You don't receive vt100 codes.\n\r"
		      , ch );

	if( IS_IMMORTAL( ch ) )
	{
	    if( xIS_SET( ch->act, PLR_NO_LAG ) )
		send_to_char( "[+NOLAG      ] You don't get lagged.\n\r", ch );
	    else
		send_to_char( "[-nolag      ] You lag normally.\n\r", ch );
	}

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

	if( xIS_SET( ch->act, PLR_NO_EMOTE ) )
	    send_to_char( "[-emote      ] You can't emote.\n\r", ch );

	if( xIS_SET( ch->act, PLR_NO_TELL ) )
	    send_to_char( "[-tell       ] You can't use 'tell'.\n\r", ch );

	charprintf( ch, "Pagelength: %d lines.\n\r", ch->pcdata->pagelen );
    }
    else
    {
	char buf[MAX_STRING_LENGTH];
	int bit;
	bool fSet;

	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_cmp( arg + 1, "autoexit" ) )		bit = PLR_AUTOEXIT;
	else if( !str_cmp( arg + 1, "autoloot" ) )	bit = PLR_AUTOLOOT;
	else if( !str_cmp( arg + 1, "autogold" ) )	bit = PLR_AUTOGOLD;
	else if( !str_cmp( arg + 1, "autosac" ) )	bit = PLR_AUTOSAC;
	else if( !str_cmp( arg + 1, "autosplit" ) )	bit = PLR_AUTOSPLIT;
	else if( !str_cmp( arg + 1, "blank" ) )		bit = PLR_BLANK;
	else if( !str_cmp( arg + 1, "brief" ) )		bit = PLR_BRIEF;
	else if( !str_cmp( arg + 1, "colour" ) )	bit = PLR_COLOUR;
	else if( !str_cmp( arg + 1, "color" ) )		bit = PLR_COLOUR;
	else if( !str_cmp( arg + 1, "combine" ) )	bit = PLR_COMBINE;
	else if( !str_cmp( arg + 1, "prompt" ) )	bit = PLR_PROMPT;
	else if( !str_cmp( arg + 1, "nobeep" ) )	bit = PLR_NO_BEEP;
	else if( !str_cmp( arg + 1, "telnetga" ) )	bit = PLR_TELNET_GA;
	else if( !str_cmp( arg + 1, "battleother" ) )	bit = PLR_BATTLEOTHER;
	else if( !str_cmp( arg + 1, "battleself" ) )	bit = PLR_BATTLESELF;
	else if( !str_cmp( arg + 1, "vt100" ) )		bit = PLR_VT100;
	else
	{
	    send_to_char( "Config which option?\n\r", ch );
	    return;
	}

	if( fSet && !str_cmp( arg, "+vt100" ) )
	{
	    xSET_BIT( ch->act, bit );
	    send_to_char( VT_SETWIN_CLEAR VT_CLEAR_SCREEN, ch );
	    if( ch->pcdata->pagelen < 0 )
		ch->pcdata->pagelen = 20;
	    vt100_init( ch->desc );
	    send_to_char( "Vt100 terminal enabled.\n\r", ch );
	    return;
	}
	else if( !fSet && !str_cmp( arg, "-vt100" ) )
	{
	    send_to_char( VT_SETWIN_CLEAR VT_CLEAR_SCREEN, ch );
	}

	if( fSet )
	{
	    xSET_BIT( ch->act, bit );
	    sprintf( buf, "%s is now ON.\n\r", arg + 1 );
	    buf[0] = UPPER( buf[0] );
	    send_to_char( buf, ch );
	}
	else
	{
	    xREMOVE_BIT( ch->act, bit );
	    sprintf( buf, "%s is now OFF.\n\r", arg + 1 );
	    buf[0] = UPPER( buf[0] );
	    send_to_char( buf, ch );
	}
    }

    return;
}


void do_wizlist( CHAR_DATA *ch, const char *argument )
{
    do_help( ch, "wizlist" );
    return;
}


void do_spells( CHAR_DATA *ch, const char *argument )
{
    BUFFER *buf = buffer_new( MAX_STRING_LENGTH );
    char tmp[MAX_INPUT_LENGTH], tmp2[MAX_INPUT_LENGTH];
    int sn, sph;
    int col;
    bool printed = FALSE;

    if( IS_NPC( ch )
	|| ( !IS_NPC( ch ) && !class_table[ch->class].fMana ) )
    {
	send_to_char( "You don't need no stinking spells!\n\r", ch );
	return;
    }

    buffer_strcat( buf, "&gThe spells you can cast are:\n\r" );
    col = 0;
    for( sn = 0; sn < MAX_SKILL; sn++ )
    {
	int fill;
	if( !skill_table[sn].name )
	    break;
	if( !can_prac( ch, sn ) || skill_table[sn].spell_fun == spell_null )
	    continue;
	if( argument[0] != '\0' && str_prefix( argument, skill_table[sn].name ) )
	    continue;

	printed = TRUE;
	tmp[0] = '\0';
	for( sph = 0; sph < MAGIC_MAX + 1; ++sph )
	    sprintf( tmp + strlen( tmp ), "&g/%s%d", magic_colour[sph],
		     mana_cost( ch, sn, sph ) );
	fill = ( ( argument[0] != '\0' ) ? 100 : 62 ) - strlen( tmp ),
	sprintf( tmp2, "&g%%-%d.%ds%%s&gma", fill, fill );
	bprintf( buf, tmp2, skill_table[sn].name, &tmp[3] );

	if( ++col % 2 == 0 || argument[0] != '\0' )
	    buffer_strcat( buf, "&n\n\r" );
	else
	    buffer_strcat( buf, " " );
    }
    if( !printed )
    {
	if( argument[0] != '\0' )
	    send_to_char( "&gYou don't know any spells like that.&n\n\r", ch );
	else
	    send_to_char( "&gYou dont know any spells.&n\n\r", ch );
	return;
    }
    else if( col % 3 != 0 )
	buffer_strcat( buf, "&n\n\r" );

    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_slist( CHAR_DATA *ch, const char *argument )
{
    BUFFER *buf;
    int sn;
    int col;
    int Class, secClass = -1;
    int level;
    bool pSpell, subSkill;

    if( IS_NPC( ch ) )
    {
	send_to_char( "You do not need any stinking skills!\n\r", ch );
	return;
    }

    if( argument[0] == '\0' )
    {
	Class = ch->class;
	secClass = get_aspire_class( ch );
    }
    else
    {
	for( Class = 0; Class < MAX_CLASS; Class++ )
	{
	    if( !str_prefix( class_table[Class].who_name, argument ) )
		break;
	}
	if( Class == MAX_CLASS )
	{
	    send_to_char( "That class doesn't exist.\n\r", ch );
	    return;
	}
    }
    buf = buffer_new( MAX_STRING_LENGTH );

    if( !*argument && secClass >= 0 && secClass != Class )
	bprintf( buf, "&bALL abilities available for &y%s&b"
		" ( and &y%s&b ) classes.\n\r",
		class_table[Class].name, class_table[secClass].name );
    else
	bprintf( buf, "&bALL Abilities available for the &y%s&b class.\n\r\n\r",
		class_table[Class].name );
    buffer_strcat( buf, "&bLv          Abilities&n\n\r\n\r" );

    for( level = 1; level <= LEVEL_HERO; level++ )
    {
	col = 0;
	pSpell = TRUE;

	for( sn = 0; sn < MAX_SKILL; sn++ )
	{
	    subSkill = FALSE;
	    if( !skill_table[sn].name )
		break;
	    if( secClass >= 0 &&
		skill_table[sn].skill_level[secClass] == level )
	    {
		if( skill_table[sn].skill_level[Class] != level )
		    subSkill = TRUE;
	    }
	    else if( skill_table[sn].skill_level[Class] != level )
		continue;

	    /*
	     * format fix by Koala
	     */
	    if( ++col % 3 == 1 && !pSpell )
		buffer_strcat( buf, "    &g" );
	    if( pSpell )
	    {
		bprintf( buf, "&b%3d:&g", level );
		pSpell = FALSE;
	    }

	    if( subSkill )
		bprintf( buf, "%22s&m*&g", skill_table[sn].name );
	    else
		bprintf( buf, "%23s", skill_table[sn].name );

	    if( col % 3 == 0 )
		buffer_strcat( buf, "\n\r" );

	}

	if( col % 3 != 0 )
	    buffer_strcat( buf, "\n\r" );
    }
    buffer_strcat( buf, "&n" );

    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;

}


/*
 * bypassing the config command - Kahn
 */

void do_autoexit( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_AUTOEXIT )
     ? sprintf( buf, "-autoexit" )
     : sprintf( buf, "+autoexit" ) );

    do_config( ch, buf );

    return;

}


void do_autoloot( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_AUTOLOOT )
     ? sprintf( buf, "-autoloot" )
     : sprintf( buf, "+autoloot" ) );

    do_config( ch, buf );

    return;
}


void do_autogold( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_AUTOGOLD )
     ? sprintf( buf, "-autogold" )
     : sprintf( buf, "+autogold" ) );

    do_config( ch, buf );

    return;
}


void do_autosac( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_AUTOSAC )
     ? sprintf( buf, "-autosac" )
     : sprintf( buf, "+autosac" ) );

    do_config( ch, buf );

    return;
}


void do_autosplit( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_AUTOSPLIT )
     ? sprintf( buf, "-autosplit" )
     : sprintf( buf, "+autosplit" ) );

    do_config( ch, buf );

    return;
}


void do_blank( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_BLANK )
     ? sprintf( buf, "-blank" )
     : sprintf( buf, "+blank" ) );

    do_config( ch, buf );

    return;
}


void do_brief( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_BRIEF )
     ? sprintf( buf, "-brief" )
     : sprintf( buf, "+brief" ) );

    do_config( ch, buf );

    return;
}


void do_combine( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    ( xIS_SET( ch->act, PLR_COMBINE )
     ? sprintf( buf, "-combine" )
     : sprintf( buf, "+combine" ) );

    do_config( ch, buf );

    return;
}


void do_colour( CHAR_DATA *ch, const char *argument )
{
    bool on;

    if( IS_NPC( ch ) )
	return;
    if( !str_cmp( argument, "on" ) )
	on = TRUE;
    else if( !str_cmp( argument, "off" ) )
	on = FALSE;
    else if( xIS_SET( ch->act, PLR_COLOUR ) )
	on = FALSE;
    else
	on = TRUE;

    if( on )
    {
	xSET_BIT( ch->act, PLR_COLOUR );
	send_to_char( "&rC&yo&Yl&go&bu&mr&g is &WON&g, excellent!&n\n\r", ch );
    }
    else
    {
	xREMOVE_BIT( ch->act, PLR_COLOUR );
	send_to_char( "Aww shucks you dont want colour.\n\r", ch );
    }
    return;
}


void do_busy( CHAR_DATA *ch, const char *argument )
{
    bool on;

    if( IS_NPC( ch ) )
	return;
    if( !str_cmp( argument, "on" ) )
	on = TRUE;
    else if( !str_cmp( argument, "off" ) )
	on = FALSE;
    else if( xIS_SET( ch->act, PLR_BUSY ) )
	on = FALSE;
    else
	on = TRUE;

    if( on )
    {
	send_to_char( "Ok, you are now marked as busy.\n\r", ch );
	xSET_BIT( ch->act, PLR_BUSY );
    }
    else
    {
	send_to_char( "You are no longer marked as busy.\n\r", ch );
	xREMOVE_BIT( ch->act, PLR_BUSY );
    }
    return;
}


void do_pagelen( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    int lines;

    if( IS_NPC( ch ) )
	return;

    one_argument( argument, arg );

    if( arg[0] == '\0' )
	lines = 20;
    else if( !str_cmp( arg, "off" ) || !str_cmp( arg, "-1" ) )
	lines = -99;
    else
	lines = atoi( arg );

    if( lines < 19 && lines != -99 )
    {
	send_to_char(
	    "Less than 19 lines of paged text is not allowed.\n\r", ch );
	return;
    }

    if( lines > 60 )
    {
	send_to_char(
	    "I don't know of a screen that is larger than 60 lines!\n\r", ch );
	lines = 60;
    }

    if( xIS_SET( ch->act, PLR_VT100 ) && lines < 0 )
    {
	send_to_char( "You can't have no pager with vt100.\n\r", ch );
	return;
    }
    ch->pcdata->pagelen = lines;
    vt100_init( ch->desc );
    charprintf( ch, "&gPage pause set to &c%d&g lines.&n\n\r", lines );
    return;
}


/*
 * Do_prompt from Morgenes from Aldara Mud
 */
void do_prompt( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_STRING_LENGTH];

    /* Unswitched NPC's get kicked out */
    if( !ch->desc )
	return;

    /* Will always have a pc ch after this */
    ch = ( ch->desc->original ? ch->desc->original : ch->desc->character );

    if( argument[0] == '\0' )
    {
	( xIS_SET( ch->act, PLR_PROMPT )
	 ? strcpy( buf, "-prompt" )
	 : strcpy( buf, "+prompt" ) );

	do_config( ch, buf );
	return;
    }

    if( !str_cmp( argument, "all" ) )
    {
	if( ch->pcdata->security > 0 )
	    strcpy( buf, "<%hhp %mma %R:%r %C:%cOLC> " );
	else
	    strcpy( buf, "<%h/%Hhp %m/%Mm %v/%Vmv %xtnl> " );
    }
    else
    {
	strcpy( buf, argument );
	str_limit( buf, 60 );
    }

    free_string( ch->pcdata->prompt );
    ch->pcdata->prompt = str_dup( buf );
    send_to_char( "Ok.\n\r", ch );
    return;
}


void do_auto( CHAR_DATA *ch, const char *argument )
{
    do_config( ch, "" );
    return;
}


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

    /* may not be possible */
    if( xIS_SET( ch->act, PLR_AFK ) )
    {
	xREMOVE_BIT( ch->act, PLR_AFK );
	send_to_char( "You are back at your keyboard.\n\r", ch );
	act( "$n has returned to $s keyboard.", ch, NULL, ch, TO_ROOM );
    }
    else
    {
	xSET_BIT( ch->act, PLR_AFK );
	send_to_char( "You are now away from keyboard.\n\r", ch );
	act( "$n has left $s keyboard.", ch, NULL, ch, TO_ROOM );
    }

    return;
}


/*
 * Scan command coded by Martek of Glass Dragon
 */
void do_scan( CHAR_DATA *ch, const char *argument )
{
    ROOM_INDEX_DATA *pRoom;
    ROOM_INDEX_DATA *toRoom;
    int d;
    bool nexit;
    bool seen;
    char buf[MAX_STRING_LENGTH];
    char buf1[MAX_STRING_LENGTH * 2];
    char *p;
    bool extra;

    if( !check_blind( ch ) )
	return;

    /*
     * Look in all directions and assume we have no exits until we find one
     */
    if( ch->desc && xIS_SET( get_char( ch )->act, PLR_INFO ) )
	extra = TRUE;
    else
	extra = FALSE;
    buf1[0] = '\0';
    nexit = TRUE;
    pRoom = ch->in_room;
    for( d = 0; pRoom && d <= 5; d++ )
    {
	if( pRoom->exit[d] == NULL
	    || ( IS_SET( pRoom->exit[d]->exit_info, EX_HIDDEN )
		&& !IS_AFFECTED( ch, AFF_DETECT_HIDDEN )
		&& !IS_SET( race_table[ch->race].race_abilities,
			   RACE_DETECT_HIDDEN ) )
	    || ( toRoom = pRoom->exit[d]->to_room ) == NULL )
	    continue;
	nexit = FALSE;

	sprintf( buf, "&g%s you see:&n%s%s%s%s\n\r",
		 capitalize( dir_name[d] ),
		 ( toRoom && !IS_SET( pRoom->exit[d]->exit_info, EX_CLOSED )
		   && room_is_dark( toRoom ) )
		 ? " (Room is dark)" : "",
		 extra ? " &y[" : "",
		 extra ? flag_string( exit_flags, &pRoom->exit[d]->exit_info ) : "",
		 extra ? "]&n" : "" );
	strcat( buf1, buf );

	if( !IS_NPC( ch ) && !xIS_SET( ch->act, PLR_BRIEF ) )
	    strcat( buf1, pRoom->exit[d]->description );

	/*
	 * We found an exit, so let's look in that direction
	 */
	if( IS_SET( pRoom->exit[d]->exit_info, EX_CLOSED ) )
	{
	    sprintf( buf, "    &g-%s &m[closed]&g\n\r",
		    pRoom->exit[d]->keyword );
	    while( ( p = strstr( buf, "&x" ) ) )
		*(p + 1) = 'g';
	    strcat( buf1, buf );
	    if( extra && pRoom->exit[d]->key > 0 )
	    {
		sprintf( buf, "    &g-door key &m[%d]&g\n\r",
			 pRoom->exit[d]->key );
		strcat( buf1, buf );
	    }
	    continue;
	}

	/*
	 * If the room is valid we show the player who's inside
	 */
	seen = FALSE;
	if( toRoom != NULL && toRoom->people != NULL )
	{
	    CHAR_DATA *rch;

	    for( rch = toRoom->people; rch != NULL;
		 rch = rch->next_in_room )
	    {
		if( !can_see( ch, rch ) )
		    continue;

		seen = TRUE;
		sprintf( buf, "    &g-%s&n&g.\n\r", PERS( rch, ch ) );
		while( ( p = strstr( buf, "&x" ) ) )
		    *( p + 1 ) = 'g';
		strcat( buf1, buf );
	    }
	}
	if( !seen )
	{
	    strcat( buf1, "    &g-&y[empty]&g.\n\r" );
	}
    }

    strcat( buf1, "&n" );
    /*
     * Inform the character of an isolated room
     */
    if( nexit )
	send_to_char( "&gThis room has no exits!&n\n\r", ch );
    else
	send_to_char( buf1, ch );

    WAIT_STATE( ch, PULSE_PER_SECOND );
    return;
}


void do_bank( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *mob;
    char buf[MAX_STRING_LENGTH];
    int deposit = 0;

    if( IS_NPC( ch ) )
	return;

    for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
    {
	if( mob->deleted )
	    continue;
	if( IS_NPC( mob ) && xIS_SET( mob->act, ACT_BANKER ) )
	    break;
    }
    if( !mob )
    {
	send_to_char( "Sorry you cant do that here.\n\r", ch );
	return;
    }

    if( !str_cmp( argument, "+all" ) )
    {
	deposit = ch->gold;
    }
    else if( !str_cmp( argument, "-all" ) )
    {
	deposit = ch->pcdata->banked;
    }
    else if( argument[0] == '\0' ||
	     ( ( argument[0] != '+' || argument[0] != '-' )
	       && !is_number_special( argument ) ) )
    {
	if( mob )
	{
	    sprintf( buf, "%s You currently have %d coins in the bank.",
		     ch->name, ch->pcdata->banked );
	    do_tell( mob, buf );
	}
	else
	{
	    charprintf( ch, "You currently have %d coins in the bank.\n\r",
			ch->pcdata->banked );
	}
	return;
    }

    /* get amount if it wasn't +all or -all */
    if( !deposit )
    {
	if( !is_number_special( argument ) )
	{
	    strcpy( buf, &argument[1] );
	    if( !is_number_special( buf ) )
	    {
		send_to_char( "Try using numbers only please.\n\r", ch );
	    }

	    deposit = atoi_special( buf );
	}
	else
	    deposit = atoi_special( argument );
    }

    /* check amounts */
    if( deposit > 0 && ch->gold < deposit )
    {
	send_to_char( "You don't have enough money for that.\n\r", ch );
	return;
    }
    if( deposit == 0 || ( deposit < 0 && deposit + ch->pcdata->banked < 0 ) )
    {
	send_to_char( "You can't withdraw that amount.\n\r", ch );
	return;
    }

    /* make the transaction */
    ch->gold -= deposit;
    ch->pcdata->banked += deposit;
    if( deposit < 0 )
    {
	sprintf( buf, "%s You have withdrawn %d coins from your account.",
		 ch->name, deposit * ( -1 ) );
	do_tell( mob, buf );
	sprintf( buf, "%s That leaves you with a total of %d coins.",
		 ch->name, ch->pcdata->banked );
	do_tell( mob, buf );
    }
    else
    {
	sprintf( buf, "%s You have deposited %d coins into your account.",
		 ch->name, deposit );
	do_tell( mob, buf );
	sprintf( buf, "%s Your new balance is a total of %d coins.",
		 ch->name, ch->pcdata->banked );
	do_tell( mob, buf );
    }
    sprintf( buf, "%s Thank you %s, for using our facility.",
	     ch->name, ch->name );
    do_tell( mob, buf );
    return;
}


void do_raceinfo( CHAR_DATA *ch, const char *argument )
{
    char *p, *q;
    const char *ski;
    char arg[MAX_INPUT_LENGTH];
    char temp[MAX_STRING_LENGTH];
    BUFFER *buf = buffer_new( MAX_STRING_LENGTH * 2 );
    int rc;
    bool full = FALSE;
    bool star;
    int tmp;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' || !str_cmp( arg, "list" ) )
    {
	if( IS_IMMORTAL( ch ) && !str_cmp( argument, "all" ) )
	    full = TRUE;
	buffer_strcat( buf, "The races currently available for Daleken are:\n\r" );
	for( rc = 0; rc < MAX_RACE; rc++ )
	{
	    if( IS_SET( race_table[rc].race_abilities, RACE_NPC_ONLY )
		&& !full )
		continue;
	    if( !IS_SET( race_table[rc].race_abilities, RACE_NPC_ONLY )
		&& full )
		star = TRUE;
	    else
		star = FALSE;
	    bprintf( buf, "&R%s&g%-16s &bSTR(&c%2d&b) INT(&c%2d&b) "
		     "WIS(&c%2d&b) DEX(&c%2d&b) CON(&c%2d&b) "
		     "RESIL(&c%4d&b) &rTNL(&y%4d&r)&n\n\r",
		     star ? "*" : full ? " " : "",
		     race_table[rc].name, 15 + race_table[rc].str_mod,
		     15 + race_table[rc].int_mod, 15 + race_table[rc].wis_mod,
		     15 + race_table[rc].dex_mod, 15 + race_table[rc].con_mod,
		     race_table[rc].resil, race_tnl( rc ) );
	}
	send_to_char( buf->data, ch );
	buffer_free( buf );
	return;
    }
    else if( ch->pcdata && ch->pcdata->security && !str_cmp( arg, "short" ) )
    {
	int col = 0;

	for( rc = 0; rc < MAX_RACE; rc++ )
	{
	    bprintf( buf, "&R%s&g%-17s ",
		     !IS_SET( race_table[rc].race_abilities, RACE_NPC_ONLY )
		     ? "*" : " ",
		     race_table[rc].name );
	    if( ++col % 4 == 0 )
		buffer_strcat( buf, "&n\n\r" );
	}
	if( col % 4 != 0 )
	    buffer_strcat( buf, "&n\n\r" );
	send_to_char( buf->data, ch );
	buffer_free( buf );
	return;
    }

    rc = race_lookup( arg );
    if( rc < 0 )
    {
	send_to_char( "That race doesn't exist.\n\r", ch );
	return;
    }

    if( IS_SET( race_table[rc].race_abilities, RACE_NPC_ONLY )
	&& !IS_IMMORTAL( ch ) )
    {
	send_to_char( "You don't have permission.\n\r", ch );
	return;
    }

    bprintf( buf, "&gStatistics for the %s race:\n\r", race_table[rc].name );
    bprintf( buf, "&g%-16s &bSTR(&c%2d&b) INT(&c%2d&b) "
	     "WIS(&c%2d&b) DEX(&c%2d&b) CON(&c%2d&b) "
	     "RESIL(&c%4d&b) &rTNL(&y%4d&r)&n\n\r",
	     race_table[rc].name, 15 + race_table[rc].str_mod,
	     15 + race_table[rc].int_mod, 15 + race_table[rc].wis_mod,
	     15 + race_table[rc].dex_mod, 15 + race_table[rc].con_mod,
	     race_table[rc].resil, race_tnl( rc ) );
    if( ( ski = race_table[rc].racial_skill ) )
    {
	bprintf( buf, "&gA %s gets the following skills:\n\r",
		 race_table[rc].name );
	while( ski && *ski )
	{
	    ski = one_argument( ski, arg );
	    bprintf( buf, "    &g'&y%s&g'&n\n\r", capitalize( arg ) );
	}
    }

    if( race_table[rc].race_abilities != RACE_NO_ABILITIES )
    {
	bprintf( buf, "&gA %s gets the following intrinsic affects and abilities:"
		 "\n\r    '&y", race_table[rc].name );

	q = p = &temp[0];
	tmp = race_table[rc].race_abilities;
	strcpy( temp, flag_string( race_perm_flags, &tmp ) );
	while( ( q = strchr( q, ' ' ) ) )
	{
	    *q = '\0';
	    q = p;
	    while( ( q = strchr( q, '_' ) ) )
		*q = ' ';
	    buffer_strcat( buf, p );
	    buffer_strcat( buf, "&g'\n\r    '&y" );
	    q = p = strchr( p, '\0' ) + 1;
	}
	q = p;
	while( ( q = strchr( q, '_' ) ) )
	    *q = ' ';
	bprintf( buf, "%s&g'&n\n\r", p );
    }

    bprintf( buf, "&gThis race has a normal body temperature of %d degrees.\n\r",
	     37 + race_table[rc].body_temp );

    buffer_strcat( buf, "This race has " );
    if( race_table[rc].magic_resist > 10 )
	buffer_strcat( buf, "an INCREDIBLE" );
    else if( race_table[rc].magic_resist > 7 )
	buffer_strcat( buf, "an extremely good" );
    else if( race_table[rc].magic_resist > 5 )
	buffer_strcat( buf, "a very good" );
    else if( race_table[rc].magic_resist > 3 )
	buffer_strcat( buf, "a good" );
    else if( race_table[rc].magic_resist > 1 )
	buffer_strcat( buf, "a fair" );
    else if( race_table[rc].magic_resist >= -1 )
	buffer_strcat( buf, "an average" );
    else if( race_table[rc].magic_resist >= -3 )
	buffer_strcat( buf, "a poor" );
    else if( race_table[rc].magic_resist >= -5 )
	buffer_strcat( buf, "a bad" );
    else if( race_table[rc].magic_resist >= -10 )
	buffer_strcat( buf, "a rotten" );
    else
	buffer_strcat( buf, "an absolutely TERRIBLE" );
    buffer_strcat( buf, " magic resistance.&n\n\r" );

    buffer_strcat( buf, "&gThis race can speak the following languages:\n\r    &yCommon " );
    if( race_table[rc].languages != LANG_COMMON )
    {
	tmp = race_table[rc].languages;
	buffer_strcat( buf, flag_string( language_flags, &tmp ) );
    }
    buffer_strcat( buf, "&n\n\r" );
    if( IS_IMMORTAL( ch ) )
    {
	if( race_table[rc].hate[0] != '\0' )
	    bprintf( buf, "&gThis race hates the races:&n\n\r    &y%s&n",
		     multi_line( temp, race_table[rc].hate, 74, 4 ) );
	bprintf( buf, "&gRacial size: &y%3d&g  Natural Alignment: &y%d&n\n\r",
		 race_table[rc].size, race_table[rc].natural_align );
	bprintf( buf, "&gPercentage gain: Hp &y%2d&g Move &y%d&n\n\r&g  Mana:",
		 race_table[rc].hp_gain, race_table[rc].move_gain );
	for( tmp = 0; tmp < MAGIC_MAX; ++tmp )
	    bprintf( buf, " %s%s: %d", magic_colour[tmp], magic_name[tmp],
		     race_table[rc].mana_gain[tmp] );
	bprintf( buf, ".&n\n\r&gMax Full:   &y%4d&g  Max Thirst: &y%d&n\n\r",
		 race_table[rc].hunger_mod, race_table[rc].thirst_mod );
    }
    send_to_char( buf->data, ch );
    buffer_free( buf );
    return;
}


void do_highest( CHAR_DATA *ch, const char *argument )
{
    HIGHEST_DATA *high;
    int i;
    bool topten = FALSE;
    char buf1[MAX_INPUT_LENGTH];
    char buf2[MAX_INPUT_LENGTH];

    if( *argument && strlen( argument ) < 3 )
    {
	send_to_char( "That was too short, try a longer word.\n\r", ch );
	return;
    }
    if( !str_prefix( "purge ", argument ) && get_trust( ch ) >= L_SEN )
    {
	for( high = highest_first; high; high = high->next )
	{
	    if( !str_cmp( argument + 6, "all" )
		|| !str_prefix( argument + 6, high->type ) )
		for( i = 0; i < 10; ++i )
		    if( high->entries[i] )
		    {
			free_highent( high->entries[i] );
			high->entries[i] = NULL;
		    }
	}
	log_string( "[%s] highest list %s", ch->name, argument );
	wiznetf( ch, WIZ_MISC, get_trust( ch ), "%s purging highest list %s.",
		ch->name, argument );
	send_to_char( "Usage: high purge [all|list].\n\r", ch );
	send_to_char( "Purging high lists.\n\r", ch );
	return;
    }
    if( !str_prefix( argument, "topten" ) || !*argument )
	topten = TRUE;
    for( high = highest_first; high; high = high->next )
    {
	if( ( topten && !str_cmp( high->type, "topten" ) )
	    || !str_prefix( argument, high->type ) )
	    break;
    }
    if( !high )
    {
	send_to_char( "Sorry, there is no highest list for that.\n\r", ch );
	return;
    }

    sprintf( buf1, "/(&rHighest %s&w)\\", high->type );
    strcpy( buf2, "  &w<" );
    for( i = 5; i < 28 - (int)strlen( buf1 ) / 2; ++i )
	buf2[i] = '_';
    strcpy( &buf2[i], buf1 );
    i += strlen( buf1 );
    for( ; i < 51; ++i )
	buf2[i] = '_';
    strcpy( &buf2[i], ">&n\n\r" );

    send_to_char( buf2, ch );
    send_to_char(
	"   &wY &bName              Class Race Lvl (sub) &wY&n\n\r", ch );
    send_to_char(
	"   &w|---==================================---|&n\n\r", ch );
    for( i = 0; i < 10 && high->entries[i]; ++i )
    {
	if( high->entries[i]->level <= 0 )
	    continue;

	if( high->entries[i]->sublevel )
	    sprintf( buf1, " (%3d)", high->entries[i]->sublevel );
	else
	    buf1[0] = '\0';
	charprintf( ch, "   &w| &g%-19.19s %s %s&g %4d%-6.6s &w|&n\n\r",
			high->entries[i]->name,
			class_table[high->entries[i]->class].who_name,
			race_table[high->entries[i]->race].who_name,
			high->entries[i]->level, buf1 );
    }
    send_to_char(
	" &w_.O.______________________________________.O._&n\n\r",
	ch );
    return;
}


void do_condition( CHAR_DATA *ch, const char *argument )
{
    const char *dil;
    int cond;

    if( IS_NPC( ch ) )
    {
	send_to_char( "You are fit as a fiddle.\n\r", ch );
	return;
    }
    send_to_char( "&g", ch );
    cond = ch->pcdata->condition[COND_FULL] * 10
	/ race_table[ch->race].hunger_mod;
    if( cond > 80 )
	dil = "You are stuffed full of food.\n\r";
    else if( cond > 50 )
	dil = "Your stomach is content.\n\r";
    else if( cond > 25 )
	dil = "You could go a while before eating yet.\n\r";
    else if( cond > 5 )
	dil = "You can hear your stomach growling.\n\r";
    else
	dil = "&rYou feel quite hungry.&g\n\r";
    send_to_char( dil, ch );


    cond = ch->pcdata->condition[COND_THIRST] * 10
	/ race_table[ch->race].thirst_mod;
    if( cond > 80 )
	dil = "You are so full of water that you slosh as you walk.\n\r";
    else if( cond > 50 )
	dil = "You feel no need to drink at the moment.\n\r";
    else if( cond > 25 )
	dil = "You could just about go a drink.\n\r";
    else if( cond > 5 )
	dil = "Your throat is dry and parched.\n\r";
    else
	dil = "&rYou feel totally dehydrated.&g\n\r";
    send_to_char( dil, ch );

    cond = ch->pcdata->condition[COND_DRUNK];
    if( cond > 800 )
	dil = "You are almost unconcious.\n\r";
    else if( cond > 600 )
	dil = "You are staggeringly drunk.\n\r";
    else if( cond > 400 )
	dil = "The world sways side to side shakily.\n\r";
    else if( cond > 200 )
	dil = "You are merry as a lord.\n\r";
    else if( cond > 50 )
	dil = "You are slightly tipsy.\n\r";
    else
	dil = "You are quite sober.\n\r";
    send_to_char( dil, ch );

    if( IS_AFFECTED( ch, AFF_BLEEDING ) )
	send_to_char( "You are leaking &rblood&g quite badly.\n\r", ch );

    cond = ( ~ch->body_parts ) & race_table[ch->race].body_parts;
    if( cond )
    {
	send_to_char( "&yYou are missing the following parts:\n\r&r\t", ch );
	send_to_char( flag_string( body_part_flags, &cond ), ch );
	send_to_char( "\n\r", ch );
    }
    cond = ch->damaged_parts & ~cond;
    if( cond )
    {
	send_to_char( "&yThe following body parts have been damaged:\n\r&r\t", ch );
	send_to_char( flag_string( body_part_flags, &cond ), ch );
	send_to_char( "\n\r", ch );
    }

    send_to_char( "&n", ch );
    return;
}


void do_last( CHAR_DATA *ch, const char *argument )
{
    DESCRIPTOR_DATA d;
    CHAR_DATA *ch_on, *chlast;
    bool remove = FALSE;

    if( argument[0] == '\0' )
    {
	send_to_char( "Which person did you wish to know about?\n\r", ch );
	return;
    }

    if( get_char_world( ch, argument ) )
    {
	send_to_char( "Use the command: whois.\n\r", ch );
	return;
    }

    if( !check_parse_name( argument ) )
    {
	send_to_char( "It is not possible that a character exists by that name.\n\r",
		      ch );
	return;
    }

    for( ch_on = char_list; ch_on; ch_on = ch_on->next )
    {
	if( !ch_on->deleted && !IS_NPC( ch_on )
	    && !str_cmp( ch_on->name, argument ) )
	    break;
    }
    if( ch_on )
	chlast = ch_on;
    else
    {
	memset( &d, 0, sizeof( DESCRIPTOR_DATA ) );
	if( !load_char_obj( &d, argument ) )
	{
	    charprintf( ch, "The histories of this world contain no "
			"reference to %s.\n\r",	capitalize( argument ) );
	    return;
	}
	chlast = d.character;
	chlast->desc = NULL;
	char_to_room( chlast, chlast->in_room );
	remove = TRUE;
    }

    if( get_trust( chlast ) >= L_APP && get_trust( chlast ) > get_trust( ch ) )
    {
	send_to_char( "The answer is withheld from you by a powerful force.\n\r", ch );
	if( remove )
	    extract_char( chlast, TRUE );
	return;
    }

    charprintf( ch, "%s is a level %d", capitalize( chlast->name ),
		chlast->level );
    if( chlast->sublevel > 0 )
	charprintf( ch, " (%d)", chlast->sublevel );
    charprintf( ch, " %s %s.\n\r", race_table[chlast->race].name,
		class_table[chlast->class].name );
    charprintf( ch, "They were last seen on Daleken on %s\n\r",
		myctime( &chlast->logon ) );

/*    if( Host[0] != '\0' && get_trust( ch ) >= L_MAS )
      charprintf( ch, "They were last logged on to Daleken from %s\n\r",
      Host );
*/
    if( remove )
	extract_char( chlast, TRUE );
    return;
}


void do_worth( CHAR_DATA *ch, const char *argument )
{
    charprintf( ch, "Level: %2d(%d)  Practices: %3d  "
		    "Quest points: %3d	Gold: %3d(%d).\n\r",
		    ch->level, ch->sublevel, ch->practice,
		    IS_NPC( ch ) ? 0 : ch->pcdata->quest->score,
		    ch->gold, IS_NPC( ch ) ? 0 : ch->pcdata->banked );
    return;
}


void do_random( CHAR_DATA *ch, const char *argument )
{
    int low = 0, high = 100;
    int result;
    char arg[MAX_INPUT_LENGTH];

    argument = one_argument( argument, arg );
    if( arg[0] != '\0' )
    {
	if( !is_number( arg ) )
	{
	    send_to_char( "Usage: random [low] [high]\n\r", ch );
	    return;
	}
	high = atoi( arg );
    }
    if( argument[0] != '\0' )
    {
	if( !is_number( argument ) )
	{
	    send_to_char( "Usage: random [low] [high]\n\r", ch );
	    return;
	}
	low = high;
	high = atoi( argument );
    }
    if( low > high )
    {
	send_to_char( "Usage: random [low] [high]\n\r", ch );
	return;
    }
    result = number_range( low, high );
    charprintf( ch, "You use your random number generator to get %d out of %d to %d.\n\r",
	     result, low, high );
    sprintf( arg, "$n uses $s random number generator to get %d out of %d to %d.",
	     result, low, high );
    act( arg, ch, NULL, NULL, TO_CANSEE );
    return;
}


/* A little saying, make 'em snappy please... */
void do_quote( CHAR_DATA *ch, const char *argument )
{
    int count;
    const char * const mesg[] =
    {
	"Quidquid latine dictum sit, altum viditur.\n\r"
	"    [Whatever is said in Latin sounds profound.]\n\r",

	"In vino veritas.\n\r"
	"    [In wine there is truth.]\n\r"
	"        -- Pliny\n\r",

	"The Pig, if I am not mistaken,\n\r"
	"Gives us ham and pork and Bacon.\n\r"
	"Let others think his heart is big,\n\r"
	"I think it stupid of the Pig.\n\r"
	"    -- Ogden Nash\n\r",

	NULL
    };

#if defined( HAVE_FORTUNE )
    if( IS_SET( SysInfo->flags, SYSINFO_FORTUNES ) )
    {
	char cmdbuf[32];
	char buf[MAX_STRING_LENGTH];
	FILE *fp;

	strcpy( cmdbuf, "fortune -n 160" );
	if( !str_cmp( argument, "-o" ) )
	    strcat( cmdbuf, " -o" );

	fp = popen( cmdbuf, "r" );

	fgetf( buf, MAX_STRING_LENGTH, fp );
	send_to_char( buf, ch );

	pclose( fp );
	return;
    }
#endif

    for( count = 0; mesg[count]; count++ )
	;
    charprintf( ch, "%s\n\r", mesg[number_range( 0, count - 1 )] );
}


#if defined( HAVE_FORTUNE )
char *fgetf( char *s, int n, register FILE *iop )
{
    register int c;
    register char *cs;

    c = '\0';
    cs = s;
    while( --n > 0 && ( c = getc( iop ) ) != EOF )
	if( ( *cs++ = c ) == '\0' )
	    break;
    *cs = '\0';
    return ( ( c == EOF && cs == s ) ? NULL : s );

}
#endif