SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |   (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 ****************************************************************************/


#include	"stdafx.h"
#include	"smaug.h"
#include	"BitVector.h"
#include	"SysData.h"
#include	"skill.h"
#include	"social.h"
#include	"commands.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"deity.h"
#include	"area.h"
#include	"boards.h"
#include	"help.h"
#include	"class.h"
#include	"races.h"
#include	"SmaugWizDoc.h"
#include	"SmaugFiles.h"
#include	"descriptor.h"
#include	"character.h"
#include	"smaugx.h"
#include	"Exits.h"


char* GetPositionName (UINT pos);


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


// Local functions.
void	show_char_to_char_0 (CCharacter *victim, CCharacter *ch);
void	show_char_to_char_1 (CCharacter *victim, CCharacter *ch);
BOOL	check_blind (CCharacter *ch);
void    show_condition (CCharacter* ch, CCharacter* victim);
void	show_race_line (CCharacter* ch, CCharacter* victim);


CString FormatObjToChar (const CObjData& Obj, CCharacter *ch, BOOL bShort)
{
	CString	Str;

	if (Obj.IsInvisible ())
		Str = "(Invis) ";

	if (ch->CanSeeEvil () && Obj.IsEvil ())
		Str += "(Red Aura) ";

	if (ch->CanSeeMagic () && Obj.IsMagic ())
		Str += "(Magical) ";

	if (Obj.IsGlowing ())
		Str += "(Glowing) ";

	if (Obj.IsHumming ())
		Str += "(Humming) ";

	if (Obj.IsHidden ())
		Str += "(Hidden) ";

	if (Obj.IsBuried ())
		Str += "(Buried) ";

	if (ch->IsImmortal () && Obj.IsPrototype ())
		Str += "(PROTO) ";

	if (ch->IsAffected (AFF_DETECTTRAPS) && Obj.IsTrapped ())
		Str += "(Trap) ";

	if (bShort) {
		if (Obj.GetShortDescr ())
			Str += Obj.GetShortDescr ();
	}
	else if (Obj.GetDescription ())
		Str += Obj.GetDescription ();

	return Str;
}


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

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


class CShowItem {
public:
	CString		Name;
	int			Count;
	int			Type;
};

// Show a list to a character.
// Can coalesce duplicated items.
void ShowListToChar (const CObjectList& List, CCharacter *ch,
						BOOL bShort, BOOL bShowNothing)
{
	CString		StrShow;
	int			nShow;
	int			iShow;
	int			count, offcount, tmp, cnt;
	BOOL		bCombine;

	if (! ch->GetDesc ())
		return;

	// if there's no list... then don't do all this crap!  -Thoric
	if (List.IsEmpty ()) {
		if (bShowNothing) {
			if (ch->IsNpc () || ch->IsAction (PLR_COMBINE))
				ch->SendText ("     ");
			ch->SendText ("Nothing.\n\r");
		}
		return;
	}

	// Alloc space for output lines.
	count = List.GetCount ();

	int	ms = (ch->GetMentalState () ? ch->GetMentalState () : 1)
		* (ch->IsNpc () ? 1 : (ch->GetPcData ()->condition [COND_DRUNK]
		? (ch->GetPcData ()->condition [COND_DRUNK] / 12) : 1));

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

	if (count + offcount <= 0) {
		if (bShowNothing) {
			if (ch->IsNpc () || ch->IsAction (PLR_COMBINE))
				ch->SendText ("     ");
			ch->SendText ("Nothing.\n\r");
		}
		return;
	}

	int		aSiz = count +  ((offcount > 0) ? offcount : 0);
	CShowItem	*ShowList = new CShowItem [aSiz];

	nShow = 0;
	tmp = (offcount > 0) ? offcount : 0;
	cnt = 0;

	// Format the list of objects.
	POSITION	pos = List.GetHeadPosition ();
	while (pos) {
		const CObjData	&Obj = *List.GetNext (pos);
		if (offcount < 0 && ++cnt > (count + offcount))
			break;
		if (tmp > 0 && number_bits (1) == 0) {
			ShowList [nShow].Name = halucinated_object (ms, bShort);
			ShowList [nShow].Count = 1;
			ShowList [nShow].Type = number_range (ITEM_LIGHT, ITEM_BOOK);
			++nShow;
			--tmp;
		}
		if (Obj.wear_loc == WEAR_NONE && can_see_obj (ch, Obj)
		  && (Obj.item_type != ITEM_TRAP
		  || ch->IsAffected (AFF_DETECTTRAPS))) {
			StrShow = FormatObjToChar (Obj, ch, bShort);
			bCombine = FALSE;

			if (ch->IsNpc () || ch->IsAction (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 (ShowList [iShow].Name == StrShow) {
						ShowList [iShow].Count += Obj.count;
						bCombine = TRUE;
						break;
					}
				}
			}

			ShowList [nShow].Type = Obj.item_type;
			// Couldn't combine, or didn't want to.
			if (! bCombine) {
				ShowList [nShow].Name = StrShow;
				ShowList [nShow].Count = Obj.count;
				++nShow;
			}
		}
	}

	if (tmp > 0) {
		int	x;
		for (x = 0; x < tmp; ++x) {
			ShowList [nShow].Name = halucinated_object (ms, bShort);
			ShowList [nShow].Count = 1;
			ShowList [nShow].Type = number_range (ITEM_LIGHT, ITEM_BOOK);
			++nShow;
		}
	}

	// Output the formatted list.		-Color support by Thoric
	for (iShow = 0; iShow < nShow; ++iShow) {
		switch (ShowList [iShow].Type) {
		  default:
			set_char_color (AT_OBJECT, ch);
			break;
		  case ITEM_BLOOD:
			set_char_color (AT_BLOOD, ch);
			break;
		  case ITEM_MONEY:
		  case ITEM_TREASURE:
			set_char_color (AT_YELLOW, ch);
			break;
		  case ITEM_FOOD:
			set_char_color (AT_HUNGRY, ch);
			break;
		  case ITEM_DRINK_CON:
		  case ITEM_FOUNTAIN:
			set_char_color (AT_THIRSTY, ch);
			break;
		  case ITEM_FIRE:
			set_char_color (AT_FIRE, ch);
			break;
		  case ITEM_SCROLL:
		  case ITEM_WAND:
		  case ITEM_STAFF:
			set_char_color (AT_MAGIC, ch);
			break;
		}
		if (bShowNothing)
			ch->SendText ("     ");
		ch->SendColor (ShowList [iShow].Name);
		if (ShowList [iShow].Count != 1)
			ch->SendTextf (" (%d)", ShowList [iShow].Count);

		ch->SendText ("\n\r");
	}

	if (bShowNothing && nShow == 0) {
		if (ch->IsNpc () || ch->IsAction (PLR_COMBINE))
			ch->SendText ("     ");
		ch->SendText ("Nothing.\n\r");
	}

	// Clean up.
	delete [] ShowList;
}


/*
 * Show fancy descriptions for certain spell affects		-Thoric
 */
void show_visible_affects_to_char (CCharacter *victim, CCharacter *ch)
{
	char	buf [MAX_STRING_LENGTH];
	const char	*name =
				victim->IsNpc () ? victim->GetShortDescr () : victim->GetName ();

	if (victim->HasSanctuary ()) {
		if (victim->IsGood ()) {
			set_char_color (AT_WHITE, ch);
			ch->SendTextf ("%s glows with an aura of divine radiance.\n\r",
				name);
		}
		else if (victim->IsEvil ()) {
			set_char_color (AT_WHITE, ch);
			ch->SendTextf ("%s shimmers beneath an aura of dark energy.\n\r",
				name);
		}
		else {
			set_char_color (AT_WHITE, ch);
			ch->SendTextf ("%s is shrouded in flowing shadow and light.\n\r",
				name);
		}
	}

	if (victim->IsAffected (AFF_FIRESHIELD)) {
		set_char_color (AT_FIRE, ch);
		ch->SendTextf ("%s is engulfed within a blaze of mystical flame.\n\r",
			name);
	}

	if (victim->IsAffected (AFF_SHOCKSHIELD)) {
		set_char_color (AT_BLUE, ch);
		ch->SendTextf ("%s is surrounded by cascading torrents of energy.\n\r",
			name);
	}

	// Scryn 8/13
	if (victim->IsAffected (AFF_ICESHIELD)) {
		set_char_color (AT_LBLUE, ch);
		ch->SendTextf ("%s is ensphered by shards of glistening ice.\n\r",
			name);
	}

	if (victim->IsPet ()) {
		set_char_color (AT_MAGIC, ch);
		CCharacter	&Master	= *victim->GetMaster ();
		if (&Master && Master.GetInRoom () == victim->GetInRoom ())
			ch->SendTextf ("%s seems very attached to %s.\r\n",
				capitalize (name), Master.GetName ());
		else
			ch->SendTextf ("%s appears to be lost.\r\n", capitalize (name));
	}
	else if (victim->IsAffected (AFF_CHARM)) {
		set_char_color (AT_MAGIC, ch);
		ch->SendTextf ("%s wanders in a dazed, zombie-like state.\n\r",
			name);
	}

	if (! victim->IsNpc () && !victim->GetDesc ()
	  && victim->switched && victim->switched->IsAffected (AFF_POSSESS)) {
		set_char_color (AT_MAGIC, ch);
		strcpy (buf, PERS (victim, ch));
		strcat (buf, " appears to be in a deep trance...\n\r");
	}
}


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

	buf [0] = '\0';

	pc_data		&vPc = *victim->GetPcData ();

	set_char_color (AT_PERSON, ch);
	if (! victim->IsNpc () && ! victim->GetDesc ()) {
		if (! victim->switched)
			ch->SendColor ("&P[(Link Dead)] ");
		else
			if (! victim->switched->IsAffected (AFF_POSSESS))
				strcat (buf, "(Switched) ");
	}

	if (victim->IsNpc () && victim->IsPossessed () && ch->IsImmortal ()
	  && victim->GetDesc ()) {
		sprintf (buf1, "(%s)", victim->GetDesc ()->m_pOriginal->GetName ());
		strcat (buf, buf1);
	}

	if (! victim->IsNpc () && victim->IsAfk ())
		strcat (buf, "[AFK] ");        

	if ((! victim->IsNpc () && victim->IsWizInvis ())
	  || (victim->IsNpc () && victim->IsMobInvis ())) {
		if (! victim->IsNpc ())
			sprintf (buf1, "(Invis %d) ", vPc.wizinvis);
		else
			sprintf (buf1, "(Mobinvis %d) ", victim->GetMobInvisLevel ());
		strcat (buf, buf1);
	}

	// Have no clan badge in SmaugWiz right now - maybe add this later
//	if (! victim->IsNpc () && vPc.clan && vPc.clan->badge
//		&& (vPc.clan->clan_type != CLAN_ORDER
//		&& vPc.clan->clan_type != CLAN_GUILD))
//			ch_printf_color (ch, "%s ", vPc.clan->badge);
//	else if (CAN_PKILL (victim))
//		send_to_char_color ("&P(&wUnclanned&P) ", ch);
//	else
//		set_char_color (AT_PERSON, ch);

	if (victim->IsAffected (AFF_INVISIBLE)) strcat (buf, "(Invis) ");
	if (victim->IsAffected (AFF_HIDE)) strcat (buf, "(Hide) ");
	if (victim->IsAffected (AFF_PASS_DOOR)) strcat (buf, "(Translucent) ");
	if (victim->IsAffected (AFF_FAERIE_FIRE)) strcat (buf, "(Pink Aura) ");
	if (victim->IsEvil () && ch->CanSeeEvil ())
		strcat (buf, "(Red Aura) ");

	if (victim->IsAffected (AFF_BERSERK))
		strcat ( buf, "(Wild-eyed) "  );
	if (! victim->IsNpc () && victim->IsAttacker ())
		strcat (buf, "(ATTACKER) ");
	if (! victim->IsNpc () && victim->IsKiller ())
		strcat (buf, "(KILLER) ");
	if (! victim->IsNpc () && victim->IsThief ())
		strcat (buf, "(THIEF) ");
	if (! victim->IsNpc () && victim->IsLitterBug ())
		strcat (buf, "(LITTERBUG) ");
	if (victim->IsNpc () && ch->IsImmortal () && victim->IsPrototype ())
		strcat (buf, "(PROTO) ");
	if (victim->IsNpc () && ch->mount && ch->mount == victim
		&& ch->GetInRoom () == ch->mount->GetInRoom ())
			strcat (buf, "(Mount) ");
	if (victim->GetDesc () && victim->GetDesc ()->m_Connected == CON_EDITING)
		strcat (buf, "(Writing) ");
	if (victim->IsPolymorphed ())
		strcat (buf, "(Morphed) ");

	set_char_color (AT_PERSON, ch);
	if (victim->GetPosition () == victim->defposition
	  && victim->HasLongDescription ()) {
		strcat (buf, victim->GetLongDescr ());
		ch->SendColor (buf);
		show_visible_affects_to_char (victim, ch);
		return;
	}

	strcat (buf, PERS (victim, ch));
	if (! victim->IsNpc () && !ch->IsAction (PLR_BRIEF))
		strcat (buf, vPc.GetTitle ());

	switch (victim->GetPosition ()) {
	  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 (ch->GetPosition () == POS_SITTING
			|| ch->GetPosition () == POS_RESTING)
				strcat (buf, " is sleeping nearby.");
		else
			strcat (buf, " is deep in slumber here.");
		break;
	  case POS_RESTING:
		if (ch->GetPosition () == POS_RESTING)
			strcat (buf, " is sprawled out alongside you.");
		else
			if (ch->GetPosition () == POS_MOUNTED)
				strcat  (buf, " is sprawled out at the foot of your mount.");
		else
			strcat  (buf, " is sprawled out here.");
		break;
	  case POS_SITTING:
		if (ch->GetPosition () == POS_SITTING)
			strcat (buf, " sits here with you.");
		else
			if (ch->GetPosition () == POS_RESTING)
				strcat (buf, " sits nearby as you lie around.");
		else
			strcat (buf, " sits upright here.");
		break;
	  case POS_STANDING:
		if (victim->IsImmortal ())
			strcat (buf, " is here before you.");
		else
			if ((victim->GetInRoom ()->sector_type == SECT_UNDERWATER)
				&& !victim->IsAffected (AFF_AQUA_BREATH) && !victim->IsNpc ())
					strcat (buf, " is drowning here.");
		else
			if (victim->GetInRoom ()->sector_type == SECT_UNDERWATER)
				strcat (buf, " is here in the water.");
		else
			if ((victim->GetInRoom ()->sector_type == SECT_OCEANFLOOR)
				&& !victim->IsAffected (AFF_AQUA_BREATH) && !victim->IsNpc ())
					strcat (buf, " is drowning here.");
		else
			if (victim->GetInRoom ()->sector_type == SECT_OCEANFLOOR)
				strcat (buf, " is standing here in the water.");
		else
			if (victim->IsAffected (AFF_FLOATING)
				|| victim->IsAffected (AFF_FLYING))
					strcat (buf, " is hovering here.");
		else
			strcat (buf, " is standing here.");
		break;

	  case POS_SHOVE:
		strcat (buf, " is being shoved around.");
		break;
	  case POS_DRAG:
		strcat (buf, " is being dragged around.");
		break;
	  case POS_MOUNTED:
		strcat (buf, " is here, upon ");
		if (! victim->mount)
			strcat (buf, "thin air???");
		else
			if (victim->mount == ch)
				strcat (buf, "your back.");
		else
			if (victim->GetInRoom () == victim->mount->GetInRoom ()) {
				strcat (buf, PERS (victim->mount, ch));
				strcat (buf, ".");
			}
		else
			strcat (buf, "someone who left??");
		break;

	  case POS_FIGHTING:
	  case POS_EVASIVE:
	  case POS_DEFENSIVE:
	  case POS_AGGRESSIVE:
	  case POS_BERSERK:
		strcat (buf, " is here, fighting ");
		if (! victim->GetFightData ()) {
			strcat (buf, "thin air???");
			victim->SetPosition (victim->mount ? POS_MOUNTED : POS_STANDING);
		}
		else if (victim->GetFightWho () == ch)
			strcat (buf, "YOU!");
		else if (victim->GetInRoom ()
		  == victim->GetFightData ()->who->GetInRoom ()) {
			strcat (buf, PERS (victim->GetFightData ()->who, ch));
			strcat (buf, ".");
		}
		else
			strcat (buf, "someone who left??");
		break;
	}

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


void show_char_to_char_1 (CCharacter *victim, CCharacter *ch)
{
	CObjData	*obj;
	int			iWear;
	BOOL		found;

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

	if (victim->HasDescription ())
		ch->SendText (victim->GetDescription ());
	else if (victim->IsNpc ())
		act (AT_PLAIN, "You see nothing special about $M.",ch,NULL,victim,TO_CHAR);
	else if (ch != victim)
		act (AT_PLAIN, "$E isn't much to look at...",ch,NULL,victim,TO_CHAR);
	else
		act (AT_PLAIN, "You're not much to look at...",ch,NULL,NULL,TO_CHAR);

	show_race_line (ch, victim);
	show_condition (ch, victim);

	found = FALSE;
	for (iWear = 0; iWear < MAX_WEAR; iWear++) {
		if ((obj = get_eq_char (victim, iWear)) && can_see_obj (ch, *obj)) {
			if (! found) {
				ch->SendText ("\n\r");
				if (victim != ch)
					act (AT_PLAIN, "$N is using:", ch,NULL,victim,TO_CHAR);
				else
					act (AT_PLAIN, "You are using:", ch,NULL,NULL,TO_CHAR);
				found = TRUE;
			}

			const char	*pWhere = NULL;
			if (! victim->IsNpc () && victim->HasValidRace ())
				pWhere = RaceTable.GetWhereName (victim->GetRace (), iWear);
			if (! pWhere)
				pWhere = where_name [iWear];
			ch->SendText (pWhere);

			ch->SendColor (FormatObjToChar (*obj, ch, TRUE));
			ch->SendText ("\n\r");
		}
	}

	// Crash fix here by Thoric
	if (ch->IsNpc () || victim == ch)
		return;

	if (ch->IsImmortal ()) {
		if (victim->IsNpc ()) {
			ch->SendTextf ("\n\rMobile #%d '%s' ",
				victim->GetMobIndex ()->vnum, victim->GetName ());

			ch->SendTextf ("is a level %d %s %s.\n\r", victim->GetLevel (),
				RaceTable.GetNpcRaceName (victim->GetRace ()),
				ClassTable.GetNpcClassName (victim->GetClass ()));
		} else {
			ch->SendTextf ("\n\r%s ", victim->GetName ());

			ch->SendTextf ("is a level %d %s %s.\n\r", victim->GetLevel (),
				RaceTable.GetName (victim->GetRace ()),
				ClassTable.GetName (victim->GetClass ()));
		}
	}

	if (number_percent () < ch->GetPcData ()->learned [gsn_peek]) {
		ch->SendText ("\n\rYou peek at the inventory:\n\r");
		ShowListToChar (victim->GetCarryList (), ch, TRUE, TRUE);
		learn_from_success (ch, gsn_peek);
	}
	else
		if (ch->GetPcData ()->learned [gsn_peek])
			learn_from_failure (ch, gsn_peek);
}


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

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

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

    return;
}



BOOL check_blind (CCharacter *ch)
{
    if (!ch->IsNpc () && ch->IsAction (PLR_HOLYLIGHT))
	return TRUE;
	
    if (ch->IsAffected (AFF_TRUESIGHT))
      return TRUE;

    if (ch->IsAffected (AFF_BLIND))
    {
	ch->SendText ("You can't see a thing!\n\r");
	return FALSE;
    }

    return TRUE;
}


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

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

	return door;
}


void do_look (CCharacter *ch, char *argument)
{
	char		arg  [MAX_INPUT_LENGTH];
	char		arg1 [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];
	char		arg3 [MAX_INPUT_LENGTH];
	CExitData	*pExit;
	CCharacter	*victim;
	CObjData	*obj;
	CRoomIndexData	*original;
	char		*pdesc;
	BOOL		doexaprog; 
	short		door;
	int			number, cnt;

	if (!ch->GetDesc ())
		return;

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

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

	if (!check_blind (ch))
		return;

	if (!ch->IsNpc ()
	  && !ch->IsAction (PLR_HOLYLIGHT)
	  && !ch->IsAffected (AFF_TRUESIGHT)
	  && room_is_dark (ch->GetInRoom ())) {
		set_char_color (AT_DGREY, ch);
		ch->SendText ("It is pitch black ... \n\r");
		show_char_to_char (ch->GetInRoom ()->first_person, ch);
		return;
	}

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

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

	if (arg1 [0] == '\0' || !str_cmp (arg1, "auto")) {
		// 'look' or 'look auto'
		set_char_color (AT_RMNAME, ch);
		ch->SendColor (ch->GetInRoom ()->GetName ());
		ch->SendText ("\n\r");
		set_char_color (AT_RMDESC, ch);

		if (arg1[0] == '\0'
		  || (!ch->IsNpc () && !ch->IsAction (PLR_BRIEF)))
			ch->SendColor (ch->GetInRoom ()->GetDescription ());

		if (!ch->IsNpc () && ch->IsAction (PLR_AUTOEXIT))
			do_exits (ch, "auto");


		ShowListToChar (ch->GetInRoom ()->GetContentList (),
			ch, FALSE, FALSE);
		show_char_to_char (ch->GetInRoom ()->first_person,  ch);
		return;
	}

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

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

		if ((obj = get_obj_here (ch, arg2)) == NULL) {
			ch->SendText ("You do not see that here.\n\r");
			return;
		}
		if (ch->GetCarryWeight () + obj->weight > can_carry_w (ch)) {
			ch->SendText ("It's too heavy for you to look under.\n\r");
			return;
		}

		count = obj->count;
		obj->count = 1;
		act (AT_PLAIN, "You lift $p and look beneath it:", ch, obj, NULL,
			TO_CHAR);
		act (AT_PLAIN, "$n lifts $p and looks beneath it:", ch, obj, NULL,
			TO_ROOM);
		obj->count = count;
		if (obj->IsCovering ())
			ShowListToChar (obj->GetContentList (), ch, TRUE, TRUE);
		else
			ch->SendText ("Nothing.\n\r");
		if (doexaprog) oprog_examine_trigger (ch, obj);
		return;
	}

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

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

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

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

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

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

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

		  case ITEM_PORTAL:
			pExit = ch->GetInRoom ()->first_exit;
			for (; pExit; pExit = pExit->GetNext ()) {
				if (pExit->vdir == DIR_PORTAL
				  && pExit->IsPortal () && ! pExit->IsDisabled ()) {
					if (room_is_private (pExit->GetToRoom ())
					  && ch->GetTrustLevel () < SysData.OverridePrivateLev) {
						set_char_color (AT_WHITE, ch);
						ch->SendText ("That room is private buster!\n\r");
						return;
					}
					original = ch->GetInRoom ();
					ch->RemoveFromRoom ();
					ch->SendToRoom (pExit->GetToRoom ());
					do_look (ch, "auto");
					ch->RemoveFromRoom ();
					ch->SendToRoom (original);
					return;
				}
			}
			ch->SendText ("You see a swirling chaos...\n\r");
			break;
		  case ITEM_CONTAINER:
		  case ITEM_CORPSE_NPC:
		  case ITEM_CORPSE_PC:
			if (IS_SET (obj->value [1], CONT_CLOSED)) {
				ch->SendText ("It is closed.\n\r");
				break;
			}

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

	if (pdesc = GetExtraDescr (arg1, ch->GetInRoom ()->ExDesList)) {
		ch->SendColor (pdesc);
		return;
	}

	door = get_door (arg1);
	if (pExit = find_door (ch, arg1, TRUE)) {
		if (pExit->keyword
		  && pExit->keyword[0] != '\0' && pExit->keyword[0] != ' ') {
			if (pExit->IsClosed () && ! pExit->IsWindow ()) {
				if (pExit->IsSecret () && door != -1)
					ch->SendText ("Nothing special there.\n\r");
				else
					act (AT_PLAIN, "The $d is closed.", ch, NULL,
						pExit->keyword, TO_CHAR);
				return;
			}
			if (pExit->IsBashed ())
				act (AT_RED, "The $d has been bashed from its hinges!", ch,
					NULL, pExit->keyword, TO_CHAR);
		}

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

		// Ability to look into the next room			-Thoric
		if (pExit->GetToRoom () && (ch->IsAffected (AFF_SCRYING)
		  || pExit->CanLook () || ch->GetTrustLevel () >= LEVEL_IMMORTAL)) {
			if (! pExit->CanLook ()
			  && ch->GetTrustLevel () < LEVEL_IMMORTAL) {
				set_char_color (AT_MAGIC, ch);
				ch->SendText ("You attempt to scry...\n\r");
				// Change by Narn, Sept 96 to allow characters who don't
				// have the scry spell to benefit from objects that are
				// affected by scry.
				if (!ch->IsNpc ()) {
					int		percent =
						ch->GetPcData ()->learned [SkillTable.Lookup ("scry")];
					if (! percent)
						percent = 55;		// 95 was too good -Thoric

					if (number_percent () > percent) {
						ch->SendText ("You fail.\n\r");
						return;
					}
				}
			}
			if (room_is_private (pExit->GetToRoom ())
			  && ch->GetTrustLevel () < SysData.OverridePrivateLev) {
				set_char_color (AT_WHITE, ch);
				ch->SendText ("That room is private buster!\n\r");
				return;
			}
			original = ch->GetInRoom ();
			ch->RemoveFromRoom ();
			ch->SendToRoom (pExit->GetToRoom ());
			do_look (ch, "auto");
			ch->RemoveFromRoom ();
			ch->SendToRoom (original);
		}
		return;
	}
	else if (door != -1) {
		ch->SendText ("Nothing special there.\n\r");
		return;
	}

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


	// finally fixed the annoying look 2.obj desc bug - Thoric
	number = number_argument (arg1, arg);
	POSITION	pos = ch->GetTailCarryPos ();
	for (cnt=0; pos; ) {
		CObjData	&Obj = *ch->GetPreviousCarrying (pos);
		if (can_see_obj (ch, Obj)) {
			if (pdesc = GetExtraDescr (arg, Obj.ExDesList)) {
				if ((cnt += Obj.count) < number)
					continue;
				ch->SendColor (pdesc);
				if (doexaprog) then oprog_examine_trigger (ch, &Obj);
				return;
			}

			if (pdesc =
			  GetExtraDescr (arg, Obj.pIndexData->ExDesList)) {
				if ((cnt += Obj.count) < number)
					continue;
				ch->SendColor (pdesc);
				if (doexaprog)
					oprog_examine_trigger (ch, &Obj);
				return;
			}
			if (nifty_is_name_prefix (arg, Obj.GetName ())) {
				if ((cnt += Obj.count) < number)
					continue;
				pdesc = GetExtraDescr (Obj.GetName (),
					Obj.pIndexData->ExDesList);
				if (!pdesc)
					pdesc = GetExtraDescr (Obj.GetName (),
						Obj.ExDesList);
				if (!pdesc)
					ch->SendText ("You see nothing special.\r\n");
				else
					ch->SendColor (pdesc);
				if (doexaprog)
					oprog_examine_trigger (ch, &Obj);
				return;
			}
		}
	}

	CObjectList	&List = ch->GetInRoom ()->GetContentList ();
	POSITION	Cpos = List.GetTailPosition ();
	while (obj = List.GetPrev (Cpos)) {
		if (can_see_obj (ch, *obj)) {
			if (pdesc = GetExtraDescr (arg, obj->ExDesList)) {
				if ((cnt += obj->count) < number)
					continue;
				ch->SendColor (pdesc);
				if (doexaprog)
					oprog_examine_trigger (ch, obj);
				return;
			}

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

	ch->SendText ("You do not see that here.\n\r");
}


void show_race_line (CCharacter* ch, CCharacter* victim)
{
	if (victim->IsNpc ()) then return;

	int		feet = victim->GetHeight () / 12;
	int		inches = victim->GetHeight () % 12;

	if (victim != ch)
		ch->SendTextf ("%s is %d'%d\" and weighs %d pounds.\n\r",
			PERS (victim, ch), feet, inches, victim->GetWeight ());
	else
		ch->SendTextf ("You are %d'%d\" and weigh %d pounds.\n\r",
			feet, inches, victim->GetWeight ());
}


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

    if (victim->GetMaxHp () > 0)
        percent =  (100 * victim->GetHp ()) / victim->GetMaxHp ();
    else
        percent = -1;


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

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

    buf[0] = UPPER (buf[0]);
    ch->SendColor (buf);
    return;
}


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

	if (! ch->GetDesc ())
		return;

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

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

	if (! check_blind (ch))
		return;

	argument = one_argument (argument, arg1);

	if (arg1 [0] == '\0') {
		CActFlags	SaveAct = ch->GetActFlags ();
		ch->SetBrief ();
		do_look (ch, "auto");
		ch->SetActFlags (SaveAct);
		return;
	}

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


void do_examine (CCharacter *ch, char *argument)
{
	char		buf [MAX_STRING_LENGTH];
	char		arg [MAX_INPUT_LENGTH];
	CObjData	*obj;
	CBoardData	*board;
	short		dam;

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

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

	one_argument (argument, arg);

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

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

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

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

		  case ITEM_ARMOR:
			if (obj->value [1] == 0)
				obj->value [1] = obj->value [0];
			if (obj->value [1] == 0)
				obj->value [1] = 1;
			dam = (short) ((obj->value [0] * 10) / obj->value [1]);

			strcpy (buf, "As you look more closely, you notice that it is ");
			if (dam >= 10) strcat (buf, "in superb condition.");
			else if (dam ==  9) strcat (buf, "in very good condition.");
			else if (dam ==  8) strcat (buf, "in good shape.");
			else if (dam ==  7) strcat (buf, "showing a bit of wear.");
			else if (dam ==  6) strcat (buf, "a little run down.");
			else if (dam ==  5) strcat (buf, "in need of repair.");
			else if (dam ==  4) strcat (buf, "in great need of repair.");
			else if (dam ==  3) strcat (buf, "in dire need of repair.");
			else if (dam ==  2) strcat (buf, "very badly worn.");
			else if (dam ==  1) strcat (buf, "practically worthless.");
			else if (dam <=  0) strcat (buf, "broken.");
			strcat (buf, "\n\r");
			ch->SendText (buf);
			break;

		  case ITEM_WEAPON:
			dam = INIT_WEAPON_CONDITION - obj->value [0];
			strcpy (buf, "As you look more closely, you notice that it is ");
			if (dam ==  0) strcat (buf, "in superb condition.");
			else if (dam ==  1) strcat (buf, "in excellent condition.");
			else if (dam ==  2) strcat (buf, "in very good condition.");
			else if (dam ==  3) strcat (buf, "in good shape.");
			else if (dam ==  4) strcat (buf, "showing a bit of wear.");
			else if (dam ==  5) strcat (buf, "a little run down.");
			else if (dam ==  6) strcat (buf, "in need of repair.");
			else if (dam ==  7) strcat (buf, "in great need of repair.");
			else if (dam ==  8) strcat (buf, "in dire need of repair.");
			else if (dam ==  9) strcat (buf, "very badly worn.");
			else if (dam == 10) strcat (buf, "practically worthless.");
			else if (dam == 11) strcat (buf, "almost broken.");
			else if (dam == 12) strcat (buf, "broken.");
			strcat (buf, "\n\r");
			ch->SendText (buf);
			break;

		  case ITEM_FOOD:
			if (obj->timer > 0 && obj->value [1] > 0)
				dam = (obj->timer * 10) / obj->value [1];
			else
				dam = 10;
			strcpy (buf, "As you examine it carefully you notice that it ");
			if (dam >= 10) strcat (buf, "is fresh.");
			else if (dam ==  9) strcat (buf, "is nearly fresh.");
			else if (dam ==  8) strcat (buf, "is perfectly fine.");
			else if (dam ==  7) strcat (buf, "looks good.");
			else if (dam ==  6) strcat (buf, "looks ok.");
			else if (dam ==  5) strcat (buf, "is a little stale.");
			else if (dam ==  4) strcat (buf, "is a bit stale.");
			else if (dam ==  3) strcat (buf, "smells slightly off.");
			else if (dam ==  2) strcat (buf, "smells quite rank.");
			else if (dam ==  1) strcat (buf, "smells revolting.");
			else if (dam <=  0) strcat (buf, "is crawling with maggots.");
			strcat (buf, "\n\r");
			ch->SendText (buf);
			break;

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

		  case ITEM_BUTTON:
			if (IS_SET (obj->value [0], TRIG_UP))
				ch->SendText ("You notice that it is depressed.\n\r");
			else
				ch->SendText ("You notice that it is not depressed.\n\r");
			break;

		  case ITEM_CORPSE_PC:
		  case ITEM_CORPSE_NPC:
			{
				short timerfrac = obj->timer;
				if (obj->item_type == ITEM_CORPSE_PC)
					timerfrac = (int) obj->timer / 8 + 1; 

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

		  case ITEM_DRINK_CON:
			ch->SendText ("When you look inside, you see:\n\r");
			sprintf (buf, "in %s noprog", arg);
			do_look (ch, buf);
		}

		if (obj->IsCovering ()) {
			sprintf (buf, "under %s noprog", arg);
			do_look (ch, buf);
		}
		oprog_examine_trigger (ch, obj);
		if (char_died (ch) || obj_extracted (obj))
			return;

		check_for_trap (ch, obj, TRAP_EXAMINE);
	}
}


void do_exits (CCharacter *ch, char *argument)
{
	char		buf [MAX_STRING_LENGTH];
	CExitData	*pExit;
	BOOL		found;
	BOOL		fAuto;

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

	if (! check_blind (ch))
		return;

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

	found = FALSE;
	pExit = ch->GetInRoom ()->first_exit;
	for ( ; pExit; pExit = pExit->GetNext ()) {
		if (! pExit->IsDisabled () && pExit->GetToRoom ()
		  && ! pExit->IsClosed ()
		  && (! pExit->IsWindow () || pExit->IsDoor ())
		  && ! pExit->IsHidden ()) {
			found = TRUE;
			if (fAuto) {
				strcat (buf, " ");
				strcat (buf, dir_name [pExit->vdir]);
			} else {
				sprintf (buf + strlen (buf), "%-5s - %s\n\r",
					capitalize (dir_name [pExit->vdir]),
					room_is_dark (pExit->GetToRoom ())
					?  "Too dark to tell" : pExit->GetToRoom ()->GetName ());
			}
		}
	}

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

	ch->SendText (buf);
}


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

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"
};


void do_versio (CCharacter *ch, char *argument)
{
	ch->SendText ("Huh?\n\r");
}


void do_version (CCharacter *ch, char *argument)
{
extern	char*	ProgVersion;

	set_char_color (AT_YELLOW, ch);
	ch->SendTextf ("Version = %s\n\r", ProgVersion);
}


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

	day = time_info.day + 1;

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

	set_char_color (AT_YELLOW, ch);
	ch->SendTextf (
		"It is %d o'clock %s, Day of %s, %d%s the Month of %s.\n"  
		"The mud started up at:    %s\n"
		"The system time (G.M.T.): %s\n"
		"Next Reboot is set for:   %s\n",
			(time_info.hour % 12 == 0) ? 12 : time_info.hour % 12,
			time_info.hour >= 12 ? "pm" : "am",
			day_name [day % 7], day, suf,
			month_name [time_info.month],
			NCCP gpDoc->m_BootTime.GetString (),
			NCCP CurrentTime.GetString (),
			NCCP gpDoc->m_RebootTime.GetString ());
}



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

    if (ch->IsIndoors ())
    {
	ch->SendText ("You can't see the sky from here.\n\r");
	return;
    }

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


// Moved into a separate function so it can be used for other things
// ie: online help editing				-Thoric
CHelpData *get_help (CCharacter *ch, char *argument)
{
	char		argall [MAX_INPUT_LENGTH];
	char		argone [MAX_INPUT_LENGTH];
	char		argnew [MAX_INPUT_LENGTH];
	int			lev;

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

	if (isdigit (argument [0])) {
		lev = number_argument (argument, argnew);
		argument = argnew;
	}
	else lev = -2;

	// Tricky argument handling so 'help a b' doesn't match a.
	argall [0] = '\0';
	while  (argument [0] != '\0') {
		argument = one_argument (argument, argone);
		if (argall [0] != '\0')
			strcat (argall, " ");
		strcat (argall, argone);
	}

	return HelpList.Find (argall, lev, ch->GetTrustLevel ());
}


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

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

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

	// Strip leading '.' to allow initial blanks.
	const char	*help = pHelp->GetTextPtr ();
	if (help [0] == '.') then ++help;
	ch->PageColor (help);
}


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

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

	switch (ch->GetSubstate ()) {
	  default:
		break;
	  case SUB_HELP_EDIT:
		if ((pHelp = (CHelpData*) ch->dest_buf) == NULL) {
			bug ("hedit: sub_help_edit: NULL ch->dest_buf");
			ch->StopEditing ();
			return;
		}
		pHelp->SetText (ch->GetEditBuffer ());
		ch->StopEditing ();
		return;
	}

	if ((pHelp = get_help (ch, argument)) == NULL) {	// new help
		char	argnew [MAX_INPUT_LENGTH];
		int		lev;

		if (isdigit (argument [0])) {
			lev = number_argument (argument, argnew);
			argument = argnew;
		}
		else
			lev = ch->GetTrustLevel ();

		pHelp = new CHelpData;
		pHelp->SetKey (STRALLOC (strupper (argument)));
		pHelp->SetText (STRALLOC (""));
		pHelp->SetLevel (lev);
		HelpList.Add (pHelp);
	}
	ch->SetSubstate (SUB_HELP_EDIT);
	ch->dest_buf = pHelp;
	start_editing (ch, pHelp->GetTextPtr ());
}


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

	if (! text)
		return "";

	fixed = strip_cr (text);
	if (fixed [0] == ' ')
		fixed [0] = '.';
	return fixed;
}


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

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

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

		gpDoc->LogString ("Saving help.are...", LOG_BUILD, LEVEL_GREATER);

		CString	Hname = FileTable.MakeAreaName ("help");
		CString	Bname = FileTable.MakeBackupName (Hname);
		rename (Hname, Bname);
		fclose (fpReserve);
		if ((fp = fopen (Hname, "w")) == NULL) {
			bug ("hset save: fopen %s", NCCP Hname);
			fpReserve = fopen (FileTable.GetName (SM_NULL_FILE), "r");
			return;
		}

		fprintf (fp, "#VERSION   %d\n", SW_CURRENT_AV);
		fprintf (fp, "#HELPS\n\n");
		POSITION	pos = HelpList.GetHeadPosition ();
		while (pos) {
			CHelpData	&Help = *HelpList.GetNext (pos);
			fprintf (fp, "%d %s~\n%s~\n\n", Help.GetLevel (),
				NCCP Help.GetKey (), help_fix (Help.GetTextPtr ()));
		}
		fprintf (fp, "0 $~\n\n\n#$\n");
		fclose (fp);
		fpReserve = fopen (FileTable.GetName (SM_NULL_FILE), "r");
		ch->SendText ("Saved.\n\r");
		return;
	}

	if (str_cmp (arg1, "remove"))
		argument = one_argument (argument, arg2);

	if ((pHelp = get_help (ch, argument)) == NULL) {
		ch->SendText ("Cannot find help on that subject.\n\r");
		return;
	}

	if (!str_cmp (arg1, "remove")) {
		HelpList.Remove (pHelp);
		ch->SendText ("Removed.\n\r");
		return;
	}

	if (!str_cmp (arg1, "level")) {
		pHelp->SetLevel (atoi (arg2));
		ch->SendText ("Done.\n\r");
		return;
	}

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

	do_hset (ch, "");
}


// Show help topics in a level range				-Thoric
// Idea suggested by Gorog
// prefix keyword indexing added by Fireblade
void do_hlist (CCharacter* ch, char* argument)
{
	char		arg [MAX_INPUT_LENGTH];
	BOOL		bMinfound = FALSE, bMaxfound = FALSE;
	CString		Index;

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

	int	min = minlimit;
	int	max = maxlimit;

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

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

		min = max;
		max = temp;
	}

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

	POSITION	pos = HelpList.GetHeadPosition ();
	int			count = 0;
	while (pos) {
		CHelpData	&Help = *HelpList.GetNext (pos);
		if (Help.GetLevel () >= min && Help.GetLevel () <= max
		  && (Index.IsEmpty ()
		  || nifty_is_name_prefix (Index, Help.GetKey ()))) {
			pager_printf (ch, "  %3d %s\n\r", Help.GetLevel (),
				Help.GetKey ());
			++count;
		}
	}

	if (count)
		pager_printf (ch, "\n\r%d pages found.\n\r", count);
	else
		ch->SendText ("None found.\n\r");
}


// New do_who with WHO REQUEST, clan, race and homepage support.  -Thoric
//
// Latest version of do_who eliminates redundant code by using linked lists.
// Shows imms separately, indicates guest and retired immortals.
// Narn, Oct/96

// do_who output structure -- Narn
class CWhoData {
public:
			CWhoData () {}

	void	SetClass (const char* s) { sa [0] = s; }
	void	SetInvis (const char* s) { sa [1] = s; }
	void	SetFlags (const char* s) { sa [2] = s; }
	void	SetName (const char* n) { sa [3] = n; }
	void	SetTitle (const char* s) { sa [4] = s; }
	void	SetExtra (const char* s) { sa [5] = s; }
	void	SetClan (const char* s) { sa [6] = s; }
	void	SetCouncil (const char* s) { sa [7] = s; }

	void	Print (CCharacter& Ch, FILE* fp);
	void	Output (CCharacter& Ch, FILE* fp, const char* str);

private:
	CString	sa [8];
};


void CWhoData::Print (CCharacter& Ch, FILE* fp)
{
	int	len = 0;
	for (int i=0; i < DIM (sa); ++i) {
		if ((len + sa [i].GetLength ()) > 76) {
			Output (Ch, fp, "\n\r                ");
			len = 16;
		}
		Output (Ch, fp, sa [i]);
		len += sa [i].GetLength ();
	}
	Ch.SendText ("\n\r");
}
//		sprintf (buf, "%-15s %s%s%s%s%s%s%s.%s%s%s\n\r", pClass, invis_str,
//			char_name, wch->GetPcData ()->title, extra_title,
//			clan_name, council_name);


void CWhoData::Output (CCharacter& Ch, FILE* fp, const char* str)
{
	if (fp)
		fprintf (fp, str);
	else Ch.SendText (str);
}

void do_who (CCharacter *ch, char *argument)
{
	char	buf [MAX_STRING_LENGTH];
	int     i;
	int		iLevelLower;
	int		iLevelUpper;
	int		nNumber;
	int		nMatch;
	BOOL	fClassRestrict;
	BOOL	fRaceRestrict;
	BOOL	fImmortalOnly;
	BOOL	fPkill;
	BOOL	fShowHomepage;
	BOOL	fClanMatch;		// SB who clan (order),who guild, and who council
	BOOL	fCouncilMatch;
	BOOL	fDeityMatch;
	CClanData		*pClan;
	CCouncilData	*pCouncil;
	CDeityData		*pDeity;

	// Set default arguments.
	iLevelLower    = 0;
	iLevelUpper    = MAX_LEVEL;
	fClassRestrict = FALSE;
	fRaceRestrict  = FALSE;
	fImmortalOnly  = FALSE;
	fPkill         = FALSE;
	fShowHomepage  = FALSE;
	fClanMatch	   = FALSE;	// SB who clan (order), who guild, who council
	fCouncilMatch  = FALSE;
	fDeityMatch    = FALSE;

	int		nClasses = ClassTable.GetCount ();
	BOOL	*pClassMatch = new BOOL [nClasses];
	for (i = 0; i < nClasses; ++i)
		pClassMatch [i] = FALSE;

	int		nRaces = RaceTable.GetCount ();
	BOOL	*pRaceMatch = new BOOL [nRaces];
	for (i = 0; i < nRaces; ++i)
		pRaceMatch [i] = FALSE;

	// Parse arguments.
	nNumber = 0;
	for (;;) {
		char arg [MAX_STRING_LENGTH];

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

		if (is_number (arg)) {
			switch (++nNumber) {
			  case 1: iLevelLower = atoi (arg); break;
			  case 2: iLevelUpper = atoi (arg); break;
			  default:
				ch->SendText ("Only two level numbers allowed.\n\r");
				return;
			}
		} else {
			if (strlen (arg) < 3) {
				ch->SendText ("Classes must be longer than that.\n\r");
				return;
			}

			// Look for classes to turn on.
			if (!str_cmp (arg, "deadly") || !str_cmp (arg, "pkill"))
				fPkill = TRUE;
			else if (!str_cmp (arg, "imm") || !str_cmp (arg, "gods"))
				fImmortalOnly = TRUE;
			else if (!str_cmp (arg, "www"))
				fShowHomepage = TRUE;
			else		 // SB who clan  (order), guild, council
				if ((pClan = ClanList.Find (arg, CLAN_ALL)))
					fClanMatch = TRUE;
			else if ((pCouncil = get_council  (arg)))
				fCouncilMatch = TRUE;
			else if ((pDeity = get_deity (arg)))
				fDeityMatch = TRUE;
			else {
				CClassData	&Cl = *ClassTable.Find (arg);
				if (&Cl) {
					pClassMatch [Cl.GetClass ()] = TRUE;
					fClassRestrict = TRUE;
				}

				CRaceData	&Ra = *RaceTable.Find (arg);
				if (&Ra) {
					pRaceMatch [Ra.GetRace ()] = TRUE;
					fRaceRestrict = TRUE;
				}

				if (! fClassRestrict && ! fRaceRestrict
				  && ! fClanMatch && ! fCouncilMatch && ! fDeityMatch) {
					ch->SendText ("That's not a class, race, order, guild,"
					" council or deity.\n\r");
					return;
				}
			}
		}
	}

	// Now find matching chars.
	nMatch = 0;
	buf [0] = '\0';
	FILE	*fp = NULL;
	if (ch)
		set_pager_color (AT_GREEN, ch);
	else {
		if (fShowHomepage)
			fp = fopen (WEBWHO_FILE, "w");
		else fp = fopen (WHO_FILE, "w");
	}

	CPtrList	MortalList, ImmList, DeadlyList;


	// start from last to first to get it in the proper order
	POSITION	pos = DList.GetTailPosition ();
	while (pos) {
		CDescriptor	&Ds = *DList.GetPrev (pos);
		if (Ds.IsDisconnecting ())
			continue;

		CCharacter	*wch = Ds.m_pOriginal ? Ds.m_pOriginal : Ds.m_pCharacter;

		if ((Ds.m_Connected != CON_PLAYING && Ds.m_Connected != CON_EDITING)
			|| ! can_see (ch, wch))
				continue;

		if (wch->GetLevel () < iLevelLower
		  || wch->GetLevel () > iLevelUpper
		  || (fPkill && ! wch->CanPkill ()) 
		  || (fImmortalOnly && wch->GetLevel () < LEVEL_IMMORTAL)
		  || (fClassRestrict && ! pClassMatch [wch->GetClass ()])
		  || (fRaceRestrict && ! pRaceMatch [wch->GetRace ()])
		  || (fClanMatch && (pClan != wch->GetPcData ()->GetClan ()))  // SB
		  || (fCouncilMatch && (pCouncil != wch->GetPcData ()->council)) //SB
		  || (fDeityMatch && (pDeity != wch->GetPcData ()->deity)))
			continue;

		nMatch++;

		CWhoData	*pNew = new CWhoData;

		if (fShowHomepage && wch->GetPcData ()->GetHomepage ()
		  && wch->GetPcData ()->HasHomepage ()) {
			sprintf (buf, "<A HREF=\"%s\">%s</A>",
				show_tilde (wch->GetPcData ()->GetHomepage ()), wch->GetName ());
			pNew->SetName (buf);
		}
		else pNew->SetName (wch->GetName ());

		CString	Class;
		switch (wch->GetLevel ()) {
		  case MAX_LEVEL -  0: Class = "Supreme Entity";	break;
		  case MAX_LEVEL -  1: Class = "Infinite";		break;
		  case MAX_LEVEL -  2: Class = "Eternal";		break;
		  case MAX_LEVEL -  3: Class = "Ancient";		break;
		  case MAX_LEVEL -  4: Class = "Exalted God";	break;
		  case MAX_LEVEL -  5: Class = "Ascendant God";	break;
		  case MAX_LEVEL -  6: Class = "Greater God";	break;
		  case MAX_LEVEL -  7: Class = "God";			break;
		  case MAX_LEVEL -  8: Class = "Lesser God";	break;
		  case MAX_LEVEL -  9: Class = "Immortal";		break;
		  case MAX_LEVEL - 10: Class = "Demi God";		break;
		  case MAX_LEVEL - 11: Class = "Savior";		break;
		  case MAX_LEVEL - 12: Class = "Creator";		break;
		  case MAX_LEVEL - 13: Class = "Acolyte";		break;
		  case MAX_LEVEL - 14: Class = "Neophyte";		break;
		  case MAX_LEVEL - 15: Class = "Avatar";		break;
		  default:
			Class.Format ("%c%2d %s", !wch->IsAuthed () ? 'N' : ' ',
				wch->GetLevel (), ClassTable.GetName (wch->GetClass ()));
			break;
		}

		if (wch->IsRetired ())
			Class = "Retired"; 
		else if (wch->IsGuest ())
			Class = "Guest"; 
		else if (wch->GetPcData ()->HasRank ())
			Class = wch->GetPcData ()->GetRank ();

		Class += "                ";
		pNew->SetClass (Class.Left (16));

		if (! str_cmp (wch->GetName (), SysData.pGuildOverseer))
			pNew->SetExtra (" [Overseer of Guilds]");
		else if (!str_cmp (wch->GetName (), SysData.pGuildAdvisor))
			pNew->SetExtra (" [Advisor to Guilds]");

		*buf = 0;
		if (wch->GetPcData ()->GetClan ()) {
			CClanData *pclan = wch->GetPcData ()->GetClan ();
			if (pclan->GetType () == CLAN_GUILD)
				strcpy (buf, " <");
			else strcpy (buf, " (");

			if (pclan->GetType () == CLAN_ORDER) {
				if (! str_cmp (wch->GetName (), pclan->deity))
					strcat (buf, "Deity, Order of ");
				else if (! str_cmp (wch->GetName (), pclan->leader))
					strcat (buf, "Leader, Order of ");
				else if (! str_cmp (wch->GetName (), pclan->number1))
					strcat (buf, "Number One, Order of ");
				else if (! str_cmp (wch->GetName (), pclan->number2))
					strcat (buf, "Number Two, Order of ");
				else
					strcat (buf, "Order of ");
			}
			else if (pclan->GetType () == CLAN_GUILD) {
				if (! str_cmp (wch->GetName (), pclan->leader))
					strcat (buf, "Leader, ");
				if (! str_cmp (wch->GetName (), pclan->number1))
					strcat (buf, "First, ");
				if (! str_cmp (wch->GetName (), pclan->number2))
					strcat (buf, "Second, ");
			}
			else {
				if (! str_cmp (wch->GetName (), pclan->deity))
					strcat (buf, "Deity of ");
				else if (! str_cmp (wch->GetName (), pclan->leader))
					strcat (buf, "Leader of ");
				else if (! str_cmp (wch->GetName (), pclan->number1))
					strcat (buf, "Number One ");
				else if (! str_cmp (wch->GetName (), pclan->number2))
					strcat (buf, "Number Two ");
			} 
			strcat (buf, pclan->GetName ());
			if (pclan->GetType () == CLAN_GUILD)
				strcat (buf, ">");
			else
				strcat (buf, ")");
		}
		pNew->SetClan (buf);

		*buf = 0;
		if (wch->GetPcData ()->council) {
			strcpy (buf, " [");
			if (!str_cmp (wch->GetName (), wch->GetPcData ()->council->head))
				strcat (buf, "Head of ");
			strcat (buf, wch->GetPcData ()->GetCouncilName ());
			strcat (buf, "]");
		}
		pNew->SetCouncil (buf);

		*buf = 0;
		if (wch->IsWizInvis ())
			sprintf (buf, "(%d) ", wch->GetPcData ()->wizinvis);
		pNew->SetInvis (buf);

		sprintf (buf, "%s%s%s%s",
			wch->IsAction (PLR_AFK) ? "[AFK] " : "",
			wch->IsAttacker () ? "(ATTACKER) " : "",
			wch->IsKiller () ? "(KILLER) " : "",
			wch->IsThief ()  ? "(THIEF) "  : "");
		pNew->SetFlags (buf);

		pNew->SetTitle (wch->GetPcData ()->GetTitle ());

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

		if (wch->IsImmortal ())
			ImmList.AddHead (pNew);
		else if (wch->CanPkill ())
			DeadlyList.AddHead (pNew);
		else
			MortalList.AddHead (pNew);
	}

	// Ok, now we have three separate linked lists and what remains is to 
	// display the information and clean up.
	while (! MortalList.IsEmpty ()) {
		CWhoData	*wh = (CWhoData*) MortalList.RemoveHead ();
		wh->Print (*ch, fp);
		delete wh; 
	}
	MortalList.RemoveAll ();		// MFC needs this for housekeeping 

	if (! DeadlyList.IsEmpty ()) {
		if (fp)
			fprintf (fp, "\n\r-----------------------------"
				"[ DEADLY CHARACTERS ]-----------------------\n\r\n\r");
		else
			ch->SendText ("\n\r-----------------------------"
				"[ DEADLY CHARACTERS ]------------------------\n\r\n\r");
	}

	while (! DeadlyList.IsEmpty ()) {
		CWhoData	*wh = (CWhoData*) DeadlyList.RemoveHead ();
		wh->Print (*ch, fp);
		delete wh; 
	} 
	DeadlyList.RemoveAll ();		// MFC needs this for housekeeping 

	if (! ImmList.IsEmpty ()) {
		if (fp)
			fprintf (fp, "\n\r---------------------------------"
				"[ IMMORTALS ]---------------------------\n\r\n\r");
		else
			send_to_pager ("\n\r---------------------------------"
				"[ IMMORTALS ]----------------------------\n\r\n\r", ch);
	}

	while (! ImmList.IsEmpty ()) {
		CWhoData	*wh = (CWhoData*) ImmList.RemoveHead ();
		wh->Print (*ch, fp);
		delete wh; 
	} 
	ImmList.RemoveAll ();			// MFC needs this for housekeeping 

	if (fp) {
		fprintf (fp, "%d player%s.\n\r", nMatch, nMatch == 1 ? "" : "s");
		fclose (fp);
	} else {
		set_char_color (AT_YELLOW, ch);
		ch->SendTextf ("%d player%s.\n\r", nMatch, nMatch == 1 ? "" : "s");
	}

	delete pClassMatch;
	delete pRaceMatch;
}


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

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

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

	if (arg2 [0] == '\0') {
		POSITION	pos = ch->GetHeadCarryPos ();
		while (obj2 = ch->GetNextCarrying (pos)) {
			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) {
			ch->SendText ("You aren't wearing anything comparable.\n\r");
			return;
		}
	} else {
		if ((obj2 = get_obj_carry (ch, arg2)) == NULL) {
			ch->SendText ("You do not have that item.\n\r");
			return;
		}
	}

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

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

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

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

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

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



void do_where (CCharacter *ch, char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CCharacter *victim;
    BOOL found;

    one_argument (argument, arg);

    set_pager_color (AT_PERSON, ch);
    if (arg [0] == '\0') {
		pager_printf (ch, "Players near you in %s:\n\r",
			NCCP ch->GetInRoom ()->GetArea ()->GetName ());
		found = FALSE;

		POSITION	pos = DList.GetHeadPosition ();
		while (pos) {
			CDescriptor	&Ds = *DList.GetNext (pos);
			if ((Ds.m_Connected == CON_PLAYING || Ds.m_Connected == CON_EDITING)
			  &&  (victim = Ds.m_pCharacter) != NULL
			  &&   !victim->IsNpc ()
			  &&   victim->GetInRoom ()
			  &&   victim->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()
			  &&   can_see (ch, victim)) {
				found = TRUE;
				pager_printf (ch, "%-28s %s\n\r",
					victim->GetName (), victim->GetInRoom ()->GetName ());
			}
		}
		if (!found)
			ch->SendText ("None\n\r");
    } else {
		found = FALSE;
		for (victim = first_char; victim; victim = victim->GetNext ())
			if (victim->GetInRoom ()
			  &&   victim->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()
			  &&   !victim->IsAffected (AFF_HIDE)
			  &&   !victim->IsAffected (AFF_SNEAK)
			  &&   can_see (ch, victim)
			  &&   is_name (arg, victim->GetName ())) {
				found = TRUE;
				pager_printf (ch, "%-28s %s\n\r",
					PERS (victim, ch), victim->GetInRoom ()->GetName ());
				break;
			}
		if (!found)
			act (AT_PLAIN, "You didn't find any $T.", ch, NULL, arg, TO_CHAR);
    }
}




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

    one_argument (argument, arg);

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

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

    diff = victim->GetLevel () - ch->GetLevel ();

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

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

	 if (diff <= -200) msg = "$N looks like a feather!";
    else if (diff <= -150) msg = "You could kill $N with your hands tied!";
    else if (diff <= -100) msg = "Hey! Where'd $N go?";
    else if (diff <=  -50) msg = "$N is a wimp.";
    else if (diff <=    0) msg = "$N looks weaker than you.";
    else if (diff <=   50) msg = "$N looks about as strong as you.";
    else if (diff <=  100) msg = "It would take a bit of luck...";
    else if (diff <=  150) msg = "It would take a lot of luck, and equipment!";
    else if (diff <=  200) msg = "Why don't you dig a grave for yourself first?";
    else                    msg = "$N is built like a TANK!";
    act (AT_CONSIDER, msg, ch, NULL, victim, TO_CHAR);
}



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

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

    if (ch->IsNpc ())
	return;

/*
    if (ch->GetLevel () < 2)
    {
	ch->SendText (
	"You must be second level to practice.  Seek out monsters to kill!\n\r");
	return;
    }
*/
    if (argument[0] == '\0')
    {
	int	col;
	short	lasttype, cnt;

	col = cnt = 0;	lasttype = SKILL_SPELL;
	set_pager_color (AT_MAGIC, ch);
	for (sn = 0; sn < SkillTable.GetCount (); sn++)
	{
	    if (!SkillTable.GetName (sn))
		break;

	    if (strcmp (SkillTable.GetName (sn), "reserved") == 0
	    &&  (ch->IsImmortal () || ch->CanCast ()))
	    {
		if (col % 3 != 0)
		    send_to_pager ("\n\r", ch);
	      send_to_pager (
"--------------------------------[Spells]---------------------------------\n\r", ch);
		col = 0;
	    }
	    if (SkillTable.GetType (sn) != lasttype)
	    {
		if (!cnt)
		    send_to_pager ("                                 (none)\n\r", ch);
		else
		if (col % 3 != 0)
		    send_to_pager ("\n\r", ch);
		pager_printf (ch,
"--------------------------------%ss---------------------------------\n\r",
			 skill_tname[SkillTable.GetType (sn)]);
		col = cnt = 0;
	    }
	    lasttype = SkillTable.GetType (sn);

	    if (!ch->IsImmortal () 
	    &&  (SkillTable.GetGuild (sn) != CLASS_NONE 
	       &&  (! ch->IsGuilded ()
		  ||  (ch->GetPcData ()->GetClan ()->m_Class != SkillTable.GetGuild (sn)))))
		continue;

	    if (ch->GetLevel () < SkillTable.GetClassLevel (sn,
			ch->GetClass ())
			|| (!ch->IsImmortal () && SkillTable.GetClassLevel (sn,
				ch->GetClass ()) == 0))
 					continue;

	    if (ch->GetPcData ()->learned [sn] == 0
			&& SkillTable.GetSkill (sn)->IsSecret ())
				continue;

	    ++cnt;
	    pager_printf (ch, "%18s %3d%%  ",
		SkillTable.GetName (sn), ch->GetPcData ()->learned[sn]);
	    if (++col % 3 == 0)
		send_to_pager ("\n\r", ch);
	}

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

	pager_printf (ch, "You have %d practice sessions left.\n\r",
	    ch->GetPractices ());
    }
    else
    {
	CCharacter *mob;
	int adept;
	BOOL can_prac = TRUE;

	if (!ch->IsAwake ())
	{
	    ch->SendText ("In your dreams, or what?\n\r");
	    return;
	}

	for (mob = ch->GetInRoom ()->first_person; mob; mob = mob->GetNextInRoom ())
	    if (mob->IsNpc () && mob->CanPractice ())
		break;

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

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

	sn = SkillTable.Lookup (argument);
	if (can_prac &&  ((sn == -1)
	||  (!ch->IsNpc ()
	&&   ch->GetLevel () < SkillTable.GetClassLevel (sn, ch->GetClass ()))))
	{
	    act (AT_TELL, "$n tells you 'You're not ready to learn that yet...'",
		mob, NULL, ch, TO_VICT);
	    return;
	}

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

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

/*
 * Guild checks - right now, cant practice guild skills - done on 
 * induct/outcast
 */
/*	
	if (!ch->IsNpc () && !IS_GUILDED (ch)
	&&    skill_table[sn].guild != CLASS_NONE)
	{
	    act (AT_TELL, "$n tells you 'Only guild members can use that..'"
		mob, NULL, ch, TO_VICT);
	    return;
	}

	if (!ch->IsNpc () && skill_table[sn].guild != CLASS_NONE 
	     && ch->GetPcData ()->clan->GetClass () != skill_table[sn].guild)
	{
	    act (AT_TELL, "$n tells you 'That can not be used by your guild.'"
		mob, NULL, ch, TO_VICT);
	    return;
	}
*/
	if (!ch->IsNpc () && SkillTable.GetGuild (sn) != CLASS_NONE)
	{
	    act (AT_TELL, "$n tells you 'That is only for members of guilds...'",
		mob, NULL, ch, TO_VICT);
	    return;
	}

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

	adept = (int) (ClassTable.GetSkillAdept (ch->GetClass ()) * 0.2);

	if (ch->GetPcData ()->learned[sn] >= adept)
	{
	    sprintf (buf, "$n tells you, 'I've taught you everything I can about %s.'",
		SkillTable.GetName (sn));
	    act (AT_TELL, buf, mob, NULL, ch, TO_VICT);
	    act (AT_TELL, "$n tells you, 'You'll have to practice it on your own now...'",
		mob, NULL, ch, TO_VICT);
	}
	else
	{
	    ch->AddPractices (-1);
	    ch->GetPcData ()->learned[sn] += int_app[ch->GetIntelligence ()].learn;
	    act (AT_ACTION, "You practice $T.",
		    ch, NULL, SkillTable.GetName (sn), TO_CHAR);
	    act (AT_ACTION, "$n practices $T.",
		    ch, NULL, SkillTable.GetName (sn), TO_ROOM);
	    if (ch->GetPcData ()->learned[sn] >= adept)
	    {
		ch->GetPcData ()->learned[sn] = adept;
		act (AT_TELL,
		 "$n tells you. 'You'll have to practice it on your own now...'",
		 mob, NULL, ch, TO_VICT);
	    }
	}
    }
    return;
}


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

    one_argument (argument, arg);

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

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

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

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


void do_password (CCharacter *ch, char *argument)
{
	char	arg1 [MAX_INPUT_LENGTH];
	char	arg2 [MAX_INPUT_LENGTH];
	char	*pArg;
	char	cEnd;

	if (ch->IsNpc ())
		return;

	// Can't use one_argument here because it smashes case.
	// So we just steal all its code.  Bleagh.
	pArg = arg1;
	while  (isspace (*argument))
		argument++;

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

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

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

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

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

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

	// This should stop all the mistyped password problems --Shaddai
    if (strcmp (arg1, arg2)) {
		ch->SendText ("Passwords don't match try again.\n\r");
		return;
    }

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

	// No tilde allowed because of player file format.
	CString NewPwd = Crypt (arg2, ch->GetName ());
	if (NewPwd.Find ('~') >= 0) {
		ch->SendText ("New password not acceptable, try again.\n\r");
		return;
	}

	delete ch->GetPcData ()->GetPassWord ();
	ch->GetPcData ()->SetPassWord (str_dup (NewPwd));
	if (SysData.IsSavePasswordOnChange ())
		save_char_obj (ch);

	ch->SendText ("Ok.\n\r");
}


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

	set_pager_color (AT_PLAIN, ch);
	for (iHash = 0; iHash < MAX_SOCIALS; ++iHash)
		for (social = SocialTable.GetSocial (iHash); social;
		  social = social->GetNext ()) {
			pager_printf (ch, "%-12s", social->GetName ());
			if (++col % 6 == 0) then
				send_to_pager ("\n\r", ch);
		}

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


void do_commands (CCharacter *ch, char *argument)
{
	int			col;
	BOOL		found;
	int			hash;
	CCmdType	*command;

	col = 0;
	set_pager_color (AT_PLAIN, ch);
	if (argument[0] == '\0') {
		for (hash = 0; hash < MAX_COMMANDS; hash++) {
			for (command = CommandTable.GetCommand (hash); command;
			  command = command->GetNext ()) {
				if (command->level < LEVEL_HERO
				  && command->level <= ch->GetTrustLevel ()
				  && (command->GetName ()[0] != 'm'
				  && command->GetName ()[1] != 'p')) {
					pager_printf (ch, "%-12s", command->GetName ());
					if (++col % 6 == 0)
						send_to_pager ("\n\r", ch);
				}
			}
		}
		if (col % 6 != 0)
			send_to_pager ("\n\r", ch);
	} else {
		found = FALSE;
		for (hash = 0; hash < MAX_COMMANDS; hash++) {
			for (command = CommandTable.GetCommand (hash); command;
			  command = command->GetNext ()) {
				if (command->level <  LEVEL_HERO
				  && command->level <= ch->GetTrustLevel ()
				  && !str_prefix (argument, command->GetName ())
				  && (command->GetName ()[0] != 'm'
				  && command->GetName ()[1] != 'p'))
				{
				pager_printf (ch, "%-12s", command->GetName ());
				found = TRUE;
				if (++col % 6 == 0)
					send_to_pager ("\n\r", ch);
				}
			}
		}

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


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

    one_argument (argument, arg);

    if (arg[0] == '\0')
    {
	if (!ch->IsNpc () && ch->IsAction (PLR_SILENCE))
	{
	    ch->SendText ("You are silenced.\n\r");
	    return;
	}

	ch->SendText ("Channels:");

        if (ch->GetTrustLevel () > 2 && ch->IsAuthed ())
        {
	 ch->SendText (!IS_SET (ch->deaf, CHANNEL_AUCTION)
	    ? " +AUCTION"
	    : " -auction");
        }

	ch->SendText (!IS_SET (ch->deaf, CHANNEL_CHAT)
	    ? " +CHAT"
	    : " -chat");

	if (!ch->IsNpc () && ch->GetPcData ()->GetClan ())
	{
          if (ch->GetPcData ()->GetClan ()->GetType () == CLAN_ORDER)
          {
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_ORDER)
	        ? " +ORDER"
	        : " -order");
          }
	  else
	  if (ch->GetPcData ()->GetClan ()->GetType () == CLAN_GUILD)
	  {
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_GUILD)
                ? " +GUILD"
                : " -guild");
	  }
          else
          {          
 	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_CLAN)
	        ? " +CLAN"
	        : " -clan");
          }
	}
	
	if (!ch->IsNpc () && ch->GetPcData ()->council)
	{
	  ch->SendText (!IS_SET (ch->deaf, CHANNEL_COUNCIL)
	      ? " +COUNCIL"
	      : " -council");
	}

	ch->SendText (!IS_SET (ch->deaf, CHANNEL_QUEST)
	    ? " +QUEST"
	    : " -quest");

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

        ch->SendText (!IS_SET (ch->deaf, CHANNEL_WARTALK)
            ? " +WARTALK"
            : " -wartalk");

	if (ch->IsHero ())
	{
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_AVTALK)
		? " +AVATAR"
		: " -avatar");
	}

	if (ch->IsImmortal ())
	{
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_IMMTALK)
		? " +IMMTALK"
		: " -immtalk");

	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_PRAY)
		? " +PRAY"
		: " -pray");
	}

	if (ch->GetTrustLevel () >= SysData.MuseLevel)
	{
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_HIGHGOD)
		? " +MUSE"
		: " -muse");
	}

	ch->SendText (!IS_SET (ch->deaf, CHANNEL_MUSIC)
	    ? " +MUSIC"
	    : " -music");

	ch->SendText (!IS_SET (ch->deaf, CHANNEL_ASK)
	    ? " +ASK"
	    : " -ask");

	ch->SendText (!IS_SET (ch->deaf, CHANNEL_SHOUT)
	    ? " +SHOUT"
	    : " -shout");

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

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

        if (ch->IsImmortal () || ch->GetPcData ()->council)
	{
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_NEWBIE)
	      	? " +NEWBIE"
		: " -newbie");
   	}

	if (ch->GetTrustLevel () >= SysData.LogLevel)
	{
	    ch->SendText (!IS_SET (ch->deaf, CHANNEL_LOG)
		? " +LOG"
		: " -log");

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

            ch->SendText (!IS_SET (ch->deaf, CHANNEL_COMM)
	        ? " +COMM"
	        : " -comm");

	    if (ch->GetTrustLevel () >= SysData.ThinkLevel)
	      ch->SendText (!IS_SET (ch->deaf, CHANNEL_HIGH)
		? " +HIGH"
		: " -high");
	}
	ch->SendText (".\n\r");
    }
    else
    {
	BOOL fClear;
	BOOL ClearAll;
	int bit;

        bit=0;
        ClearAll = FALSE;

	     if (arg[0] == '+') fClear = TRUE;
	else if (arg[0] == '-') fClear = FALSE;
	else
	{
	    ch->SendText ("Channels -channel or +channel?\n\r");
	    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, "clan"    )) bit = CHANNEL_CLAN;
	else if (!str_cmp (arg+1, "council" )) bit = CHANNEL_COUNCIL;
        else if (!str_cmp (arg+1, "guild"   )) bit = CHANNEL_GUILD;  
	else if (!str_cmp (arg+1, "quest"   )) bit = CHANNEL_QUEST;
	else if (!str_cmp (arg+1, "tells"   )) bit = CHANNEL_TELLS;
	else if (!str_cmp (arg+1, "immtalk" )) bit = CHANNEL_IMMTALK;
	else if (!str_cmp (arg+1, "log"     )) bit = CHANNEL_LOG;
	else if (!str_cmp (arg+1, "build"   )) bit = CHANNEL_BUILD;
	else if (!str_cmp (arg+1, "high"    )) bit = CHANNEL_HIGH;
	else if (!str_cmp (arg+1, "pray"    )) bit = CHANNEL_PRAY;
	else if (!str_cmp (arg+1, "avatar"  )) bit = CHANNEL_AVTALK;
	else if (!str_cmp (arg+1, "monitor" )) bit = CHANNEL_MONITOR;
	else if (!str_cmp (arg+1, "newbie"  )) bit = CHANNEL_NEWBIE;
	else if (!str_cmp (arg+1, "music"   )) bit = CHANNEL_MUSIC;
	else if (!str_cmp (arg+1, "muse"    )) bit = CHANNEL_HIGHGOD;
	else if (!str_cmp (arg+1, "ask"     )) bit = CHANNEL_ASK;
	else if (!str_cmp (arg+1, "shout"   )) bit = CHANNEL_SHOUT;
	else if (!str_cmp (arg+1, "yell"    )) bit = CHANNEL_YELL;
	else if (!str_cmp (arg+1, "comm"    )) bit = CHANNEL_COMM;
	else if (!str_cmp (arg+1, "order"   )) bit = CHANNEL_ORDER;
        else if (!str_cmp (arg+1, "wartalk" )) bit = CHANNEL_WARTALK;
	else if (!str_cmp (arg+1, "all"     )) ClearAll = TRUE;
	else
	{
	    ch->SendText ("Set or clear which channel?\n\r");
	    return;
	}

	if ((fClear) &&  (ClearAll))
	{
            REMOVE_BIT  (ch->deaf, CHANNEL_AUCTION);
            REMOVE_BIT  (ch->deaf, CHANNEL_CHAT);
            REMOVE_BIT  (ch->deaf, CHANNEL_QUEST);
       /*     REMOVE_BIT  (ch->deaf, CHANNEL_IMMTALK); */
            REMOVE_BIT  (ch->deaf, CHANNEL_PRAY);
            REMOVE_BIT  (ch->deaf, CHANNEL_MUSIC);
            REMOVE_BIT  (ch->deaf, CHANNEL_ASK);
            REMOVE_BIT  (ch->deaf, CHANNEL_SHOUT);
            REMOVE_BIT  (ch->deaf, CHANNEL_YELL);

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

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

            if (ch->GetPcData ()->guild)
              REMOVE_BIT  (ch->deaf, CHANNEL_GUILD);
       */
            if (ch->GetLevel () >= LEVEL_IMMORTAL)
              REMOVE_BIT  (ch->deaf, CHANNEL_AVTALK);
 
	    if (ch->GetLevel () >= SysData.LogLevel)
	      REMOVE_BIT  (ch->deaf, CHANNEL_COMM);

        } else if ((!fClear) &&  (ClearAll))
        {
            SET_BIT  (ch->deaf, CHANNEL_AUCTION);
            SET_BIT  (ch->deaf, CHANNEL_CHAT);
            SET_BIT  (ch->deaf, CHANNEL_QUEST);
       /*     SET_BIT  (ch->deaf, CHANNEL_IMMTALK); */
            SET_BIT  (ch->deaf, CHANNEL_PRAY);
            SET_BIT  (ch->deaf, CHANNEL_MUSIC);
            SET_BIT  (ch->deaf, CHANNEL_ASK);
            SET_BIT  (ch->deaf, CHANNEL_SHOUT);
            SET_BIT  (ch->deaf, CHANNEL_YELL);
          
       /*     if (ch->GetPcData ()->clan)
              SET_BIT  (ch->deaf, CHANNEL_CLAN);

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

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

	    if (ch->GetLevel () >= SysData.LogLevel)
	      SET_BIT  (ch->deaf, CHANNEL_COMM);

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

	  ch->SendText ("Ok.\n\r");
    }

    return;
}


// display WIZLIST file						-Thoric
void do_wizlist (CCharacter *ch, char *argument)
{
	set_pager_color (AT_IMMORT, ch);
	show_file (ch, FileTable.GetName (SM_WIZLIST_FILE));
}


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

	if (ch->IsNpc ())
		return;

	one_argument (argument, arg);

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

		if (! ch->IsPkiller ()) {
			ch->SendText (ch->IsAction (PLR_NICE)
			  ? "[+NICE     ] You are nice to other players.\n\r"
			  : "[-nice     ] You are not nice to other players.\n\r");

			ch->SendText (ch->IsAction (PLR_FLEE)
			  ? "[+FLEE     ] You flee if you get attacked.\n\r"
			  : "[-flee     ] You fight back if you get attacked.\n\r");
		}

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

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

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

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

		ch->SendText (ch->IsAction (PLR_AUTOGOLD)
		  ? "[+AUTOGOLD ] You automatically split gold from kills in groups.\n\r"
		  : "[-autogold ] You don't automatically split gold from kills in groups.\n\r");

		ch->SendText (ch->IsGagged ()       
		  ? "[+GAG      ] You see only necessary battle text.\n\r"
		  : "[-gag      ] You see full battle text.\n\r");

		ch->SendText (ch->IsPagerOn ()
		  ? "[+PAGER    ] Long output is page-paused.\n\r"
		  : "[-pager    ] Long output scrolls to the end.\n\r");

		ch->SendText (ch->IsAction (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->SendText (ch->IsAction (PLR_BRIEF)
		  ? "[+BRIEF    ] You see brief descriptions.\n\r"
		  : "[-brief    ] You see long descriptions.\n\r");

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

		ch->SendText (ch->IsNoIntro ()
		  ? "[+NOINTRO  ] You don't see the ascii intro screen on login.\n\r"
		  : "[-nointro  ] You see the ascii intro screen on login.\n\r");

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

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

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

		ch->SendText (ch->IsAction (PLR_RIP)
		  ? "[+RIP      ] You receive RIP graphic sequences.\n\r"
		  : "[-rip      ] You don't receive RIP graphics.\n\r");

		if (! ch->IsPkiller ())
			ch->SendText (ch->IsAction (PLR_SHOVEDRAG)
			  ? "[+SHOVEDRAG] You allow yourself to be shoved and dragged around.\n\r"
			  : "[-shovedrag] You'd rather not be shoved or dragged around.\n\r");

		ch->SendText (ch->IsNoSummon ()
		  ? "[+NOSUMMON ] You do not allow other players to summon you.\n\r"
		  : "[-nosummon ] You allow other players to summon you.\n\r");

		if (ch->IsImmortal ())
			ch->SendText (ch->IsAction (PLR_ROOMVNUM)
				? "[+VNUM     ] You can see the VNUM of a room.\n\r"
				: "[-vnum     ] You do not see the VNUM of a room.\n\r");

		ch->SendText (ch->IsAction (PLR_SILENCE)
			? "[+SILENCE  ] You are silenced.\n\r" : "");

		ch->SendText (!ch->IsAction (PLR_NO_EMOTE)
			? "" : "[-emote    ] You can't emote.\n\r");

		ch->SendText (!ch->IsAction (PLR_NO_TELL)
			? "" : "[-tell     ] You can't use 'tell'.\n\r");
		ch->SendText (!ch->IsAction (PLR_LITTERBUG) ? ""
			: "[-litter  ] A convicted litterbug. You cannot drop anything.\n\r");
	} else {
		BOOL	fSet;
		int		bit = 0;

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

		if (!str_prefix (arg+1, "autoexit")) bit = PLR_AUTOEXIT;
		else if (!str_prefix (arg+1, "autoloot")) bit = PLR_AUTOLOOT;
		else if (!str_prefix (arg+1, "autosac" )) bit = PLR_AUTOSAC;
		else if (!str_prefix (arg+1, "autogold")) bit = PLR_AUTOGOLD;
		else if (!str_prefix (arg+1, "blank"   )) bit = PLR_BLANK;
		else if (!str_prefix (arg+1, "brief"   )) bit = PLR_BRIEF;
		else if (!str_prefix (arg+1, "combine" )) bit = PLR_COMBINE;
		else if (!str_prefix (arg+1, "prompt"  )) bit = PLR_PROMPT;
		else if (!str_prefix (arg+1, "telnetga")) bit = PLR_TELNET_GA;
		else if (!str_prefix (arg+1, "ansi"    )) bit = PLR_ANSI;
		else if (!str_prefix (arg+1, "rip"     )) bit = PLR_RIP;
		else if (!str_prefix (arg+1, "flee"    )) bit = PLR_FLEE;
		else if (!str_prefix (arg+1, "nice"    )) bit = PLR_NICE;
		else if (!str_prefix (arg+1, "shovedrag")) bit = PLR_SHOVEDRAG;
		else if (ch->IsImmortal ()
		  && !str_prefix (arg+1, "vnum"    )) bit = PLR_ROOMVNUM;

		if (bit) {
			if ((bit == PLR_FLEE || bit == PLR_NICE || bit == PLR_SHOVEDRAG) 
			  && ch->IsPkiller ()) {
				ch->SendText (
					"Pkill characters can not config that option.\n\r");
				return;
			}

			if (fSet)
				ch->SetActBit (bit);
			else ch->ClrActBit (bit);
			ch->SendText ("Ok.\n\r");
			return;
		} else {
			if (!str_prefix (arg+1, "norecall")) bit = PCFLAG_NORECALL;
			else if (!str_prefix (arg+1, "nointro" )) bit = PCFLAG_NOINTRO;
			else if (!str_prefix (arg+1, "nosummon")) bit = PCFLAG_NOSUMMON;
			else if (!str_prefix (arg+1, "gag"     )) bit = PCFLAG_GAG; 
			else if (!str_prefix (arg+1, "pager"   )) bit = PCFLAG_PAGERON;
			else {
				ch->SendText ("Config which option?\n\r");
				return;
			}

			if (fSet)
				ch->SetPcFlag (bit);
			else
				ch->ClrPcFlag (bit);

			ch->SendText ("Ok.\n\r");
			return;
		}
	}
}


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


/*
void do_areas (CCharacter *ch, char *argument)
{
    CAreaData *pArea1;
    CAreaData *pArea2;
    int iArea;
    int iAreaHalf;

    iAreaHalf =  (top_area + 1) / 2;
    pArea1    = first_area;
    pArea2    = first_area;
    for (iArea = 0; iArea < iAreaHalf; iArea++)
	pArea2 = pArea2->next;

    for (iArea = 0; iArea < iAreaHalf; iArea++)
    {
	ch->SendTextf ("%-39s%-39s\n\r",
	    pArea1->name, pArea2 ? pArea2->name : "");
	pArea1 = pArea1->next;
	if (pArea2)
	    pArea2 = pArea2->next;
    }

    return;
}
*/

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

void do_areas (CCharacter *ch, char *argument)
{
	set_pager_color (AT_PLAIN, ch);
	send_to_pager ("\n\r   &GAuthor    &Y|&G              Area                    &Y| &GRecommended &Y|  &GEnforced &Y|\n\r", ch);
	send_to_pager ("-------------+--------------------------------------+-------------+-----------+\n\r", ch);

	POSITION	pos = AreaList.GetHeadPosition ();
	while (pos) {
		CAreaData	&Area = *AreaList.GetNext (pos);
		pager_printf (ch, "&G%-12s &Y| &z%-36s &Y| &W%4d - %-4d &Y| &W%3d - %-3d &Y|\n\r", 
			NCCP Area.m_Author, NCCP Area.GetName (),
			Area.low_soft_range, Area.hi_soft_range,
			Area.low_hard_range, Area.hi_hard_range);
	}
}


void do_afk (CCharacter *ch, char *argument)
{
	if (ch->IsNpc ())
		return;
     
	if (ch->IsAction (PLR_AFK)) {
		ch->ClrActBit (PLR_AFK);
		ch->SendText ("You are no longer afk.\n\r");
		act (AT_GREY,"$n is no longer afk.", ch, NULL, NULL, TO_ROOM);
	} else {
		ch->SetActBit (PLR_AFK);
		ch->SendText ("You are now afk.\n\r");
		act (AT_GREY,"$n is now afk.", ch, NULL, NULL, TO_ROOM);
		return;
	}
#ifdef _DEBUG
	CSwException	ex;
	ex.Printf ("Test Exception from AFK");
	throw ex;
#endif
}


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

	if (ch->IsNpc ())
		return;

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

	lowlev = 1;
	hilev = 50;

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

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

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

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

	if (lowlev > hilev)
		lowlev = hilev;

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

	for (i=lowlev; i <= hilev; ++i) {
		BOOL	bNewLevel = FALSE;
		sprintf (skn, "Spell");   
		for (sn = 0; sn < SkillTable.GetCount (); sn++) {
			CSkill	*pSkill = SkillTable.GetSkill (sn);
			if (! pSkill)
				break;

			if (pSkill->GetType () != lasttype) {
				lasttype = pSkill->GetType ();
				strcpy (skn, skill_tname [lasttype]);
			}

			if (ch->GetPcData ()->learned [sn] == 0 && pSkill->IsSecret ())
				continue;

			if (i == pSkill->GetClassLevel (ch->GetClass ())) {
				if (! bNewLevel) {
					bNewLevel = TRUE;
					pager_printf (ch, "Level %d\n\r", i);
				}
				pager_printf (ch,
					"%7s: %20.20s \t Current: %-3d Max: %-3d  MinPos: %s\n\r",
					skn, pSkill->GetName (), 
					ch->GetPcData ()->learned [sn],
					pSkill->GetClassAdept (ch->GetClass ()),
					GetPositionName (pSkill->GetMinimumPosition ()));
			}
		}
	}
}


char* GetPositionName (UINT pos)
{
	static char	*PositionNames [POS_MAX] = {
		"any", "mortally wounded", "incapacitated", "stunned", "sleeping",
		"fighting (berserk)", "resting", "fighting (aggressive)", "sitting",
		"fighting", "fighting (defensive)", "fighting (evasive)", "standing",
		"mounted", "shoving", "dragging" };

	return (pos < POS_MAX) ? PositionNames [pos] : NULL;
}


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

	buf [0] = '\0';

	if (ch->IsNpc ())
		return;

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

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

	if (victim->IsNpc ()) {
		ch->SendText ("That's not a player!\n\r");
		return;
	}

	ch->SendTextf ("%s is a %s level %d %s %s", victim->GetName (), 
		victim->GetSex () == SEX_MALE ? "male" : 
		victim->GetSex () == SEX_FEMALE ? "female" : "neutral",
		victim->GetLevel (), 
		RaceTable.GetName (victim->GetRace ()),
		ClassTable.GetName (victim->GetClass ()));

	if (ch->IsImmortal ())
		ch->SendTextf (" in room %d.\n\r", victim->GetInRoom ()->vnum);
	else
		ch->SendTextf (".\n\r");

	ch->SendTextf ("%s is a %sdeadly player",
		victim->GetSex () == SEX_MALE ? "He" :
		victim->GetSex () == SEX_FEMALE ? "She" : "It",
		victim->IsPkiller () ? "" : "non-");

	if (victim->GetPcData ()->GetClan ()) {
		if (victim->GetPcData ()->GetClan ()->GetType () == CLAN_ORDER)
			ch->SendText (", and belongs to the Order ");
		else if (victim->GetPcData ()->GetClan ()->GetType () == CLAN_GUILD)
			ch->SendText (", and belongs to the ");
		else
			ch->SendText (", and belongs to Clan ");
		ch->SendText (victim->GetPcData ()->GetClan ()->GetName ());
	}
	ch->SendText (".\n\r");

	if (victim->GetPcData ()->council)
		ch->SendTextf ("%s belongs to the %s.\n\r", 
			victim->GetName (), victim->GetPcData ()->council->name);

	if (victim->GetPcData ()->deity)
		ch->SendTextf ("%s has found succor in the deity %s.\n\r",
			victim->GetName (), victim->GetPcData ()->deity->GetName ());

	if (victim->GetPcData ()->HasHomepage ())
		ch->SendTextf ("%s's homepage can be found at %s.\n\r", 
			victim->GetName (), victim->GetPcData ()->GetHomepage ());

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

	if (ch->IsImmortal ()) {
		ch->SendText ("----------------------------------------------------\n\r");
		ch->SendText ("Info for immortals:\n\r");

		pc_data	&Pc = *victim->GetPcData ();

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

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

		ch->SendTextf ("%s is %shelled at the moment.\n\r",
			victim->GetName (), (Pc.release_date.IsZero ()) ? "not " : "");

		if (! Pc.release_date.IsZero ())
			ch->SendTextf ("%s was helled by %s, and will be released on %24.24s.\n\r",
				victim->GetSex () == SEX_MALE ? "He" :
				victim->GetSex () == SEX_FEMALE ? "She" : "It",
				Pc.helled_by, NCCP Pc.release_date.GetString ());

		if (victim->GetTrustLevel () < ch->GetTrustLevel ()) {
			sprintf (buf2, "list %s", buf);
			do_comment (ch, buf2);
		}

		if (victim->IsAction (PLR_SILENCE) || victim->IsAction (PLR_NO_EMOTE) 
		  || victim->IsAction (PLR_NO_TELL) || victim->IsAction (PLR_THIEF) 
		  || victim->IsAction (PLR_KILLER)) {
			sprintf (buf2, "This player has the following flags set:");
			if (victim->IsAction (PLR_SILENCE)) 
				strcat (buf2, " silence");
			if (victim->IsAction (PLR_NO_EMOTE)) 
				strcat (buf2, " noemote");
			if (victim->IsAction (PLR_NO_TELL))
				strcat (buf2, " notell");
			if (victim->IsAction (PLR_THIEF))
				strcat (buf2, " thief");
			if (victim->IsAction (PLR_KILLER))
				strcat (buf2, " killer");
			strcat (buf2, ".\n\r");
			ch->SendText (buf2);
		}
		// added by Gorog
		if (victim->GetDesc () && victim->GetDesc ()->IsHost ()) {
			sprintf (buf2, "%s's IP info: %s ", victim->GetName (),
				victim->GetDesc ()->m_pHost);
			if (ch->GetTrustLevel () >= LEVEL_GOD)
				strcat (buf2, victim->GetDesc ()->user);
					strcat (buf2, "\n\r");
			ch->SendText (buf2);
		}
	}
}


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

	if (ch->IsNpc ())
		return;

	argument = one_argument (argument, arg);
	if (! *arg) {
		if (ch->IsPagerOn ())
			do_config (ch, "-pager");
		else
			do_config (ch, "+pager");
		return;
	}

	if (! is_number (arg)) {
		ch->SendText ("Set page pausing to how many lines?\n\r");
		return;
	}

	ch->GetPcData ()->pagerlen = atoi (arg);
	if (ch->GetPcData ()->pagerlen < 5)
		ch->GetPcData ()->pagerlen = 5;

	ch->SendTextf ("Page pausing set to %d lines.\n\r",
		ch->GetPcData ()->pagerlen);
}


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

	if (ch->IsNpc ())
		return;

	CStringList	&List = ch->GetPcData ()->m_IgnoreList;

	argument = one_argument (argument, arg);

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

		if (List.IsEmpty ()) {
			ch->SendText ("\t    no one\n\r");
			return;
		}

		pos = List.GetHeadPosition ();
		while (pos)
			ch->SendTextf ("\t  - %s\n\r", NCCP List.GetNext (pos));

		return;
	}

	// Clear players ignored if given arg "none"
	else if (! strcmp (arg, "none")) {
		List.RemoveAll ();
		set_char_color (AT_IGNORE, ch);
		ch->SendText ("You now ignore no one.\n\r");
		return;
	}

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

	} else {

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

		// Loop through the list of ignored players
		pos = List.GetHeadPosition ();
		while (pos) {
			// If the argument matches a name in list remove it
			CurPos = pos;
			if (! strcmp (List.GetNext (pos), capitalize (arg))) {
				List.RemoveAt (CurPos);
				set_char_color (AT_IGNORE, ch);
				ch->SendTextf ("You no longer ignore %s.\n\r", arg);
				return;
			}
		}

		// if there wasn't a match check to see if the name
		// is valid. This if-statement may seem like overkill
		// but it is intended to prevent people from doing the
		// spam and log thing while still allowing ya to
		// ignore new chars without pfiles yet...

		CString PFname = FileTable.PlayerName (arg);
		CCharacter	*victim = NULL;

		if (! FileTable.Exists (PFname) &&
		  (! (victim = get_char_world (ch, arg)) || victim->IsNpc () ||
		  strcmp (capitalize (arg), victim->GetName ()))) {
			set_char_color (AT_IGNORE, ch);
			ch->SendText ("No player exists by that name.\n\r");
			return;
		}

		if (victim)
			strcpy (arg, victim->GetName ());

		// Add it to the list
		List.AddTail (capitalize (arg));
		set_char_color (AT_IGNORE, ch);
		ch->SendTextf ("You now ignore %s.\n\r", arg);
	}
}