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.    *
 * ------------------------------------------------------------------------ *
 *			     Player skills module									    *
 ****************************************************************************/

#include	"stdafx.h"
#include	"smaug.h"
#include	"SysData.h"
#include	"area.h"
#include	"skill.h"
#include	"mobiles.h"
#include	"objects.h"
#include	"rooms.h"
#include	"deity.h"
#include	"races.h"
#include	"class.h"
#include	"Exits.h"
#include	"character.h"
#include	"SmaugWizDoc.h"

char * const spell_flag[] =
{ "water", "earth", "air", "astral", "area", "distant", "reverse",
"noself", "_unused2_", "accumulative", "recastable", "noscribe",
"nobrew", "group", "object", "character", "secretskill", "pksensitive",
"stoponfail", "nofight", "nodispel", "r1", "r2", "r3", "r4", "r5", "r6",
"r7", "r8", "r9", "r10", "r11"
};

char * const spell_saves[] =
{ "none", "poison_death", "wands", "para_petri", "breath", "spell_staff" };

char * const spell_damage[] =
{ "none", "fire", "cold", "electricity", "energy", "acid", "poison", "drain" };

char * const spell_action[] =
{ "none", "create", "destroy", "resist", "suscept", "divinate", "obscure",
"change" };

char * const spell_power[] =
{ "none", "minor", "greater", "major" };

char * const spell_class[] =
{ "none", "lunar", "solar", "travel", "summon", "life", "death", "illusion" };

char * const target_type[] =
{ "ignore", "offensive", "defensive", "self", "objinv" };


int		ris_save (CCharacter *ch, int chance, int ris);
BOOL	check_illegal_psteal (CCharacter *ch, CCharacter *victim);
BOOL	can_use_skill (CCharacter* ch, int percent, int gsn);
char	*myobj (CObjData *obj);
void	SkillHelp (CCharacter* ch);

ch_ret	ranged_got_target (CCharacter *ch, CCharacter *victim,
						  CObjData *weapon, CObjData *projectile,
						  short dist, short dt, char *stxt, short color);

CObjData *find_projectile (CCharacter* ch, int type);


// Dummy function
void skill_notfound (CCharacter *ch, char *argument)
{
    ch->SendText ("Huh?\n\r");
    return;
}


int get_ssave (char *name)
{
    int x;

    for (x = 0; x < DIM (spell_saves); x++)
      if (!str_cmp (name, spell_saves[x]))
        return x;
    return -1;
}

int get_starget (char *name)
{
    int x;

    for (x = 0; x < DIM (target_type); x++)
      if (!str_cmp (name, target_type[x]))
        return x;
    return -1;
}

int get_sflag (char *name)
{
    int x;

    for (x = 0; x < DIM (spell_flag); x++)
      if (!str_cmp (name, spell_flag[x]))
        return x;
    return -1;
}

int get_sdamage (char *name)
{
    int x;

    for (x = 0; x < DIM (spell_damage); x++)
      if (!str_cmp (name, spell_damage[x]))
        return x;
    return -1;
}

int get_saction (char *name)
{
    int x;

    for (x = 0; x < DIM (spell_action); x++)
      if (!str_cmp (name, spell_action[x]))
        return x;
    return -1;
}

int get_spower (char *name)
{
    int x;

    for (x = 0; x < DIM (spell_power); x++)
      if (!str_cmp (name, spell_power[x]))
        return x;
    return -1;
}

int get_sclass (char *name)
{
    int x;

    for (x = 0; x < DIM (spell_class); x++)
      if (!str_cmp (name, spell_class[x]))
        return x;
    return -1;
}

BOOL is_legal_kill (CCharacter *ch, CCharacter *vch)
{
  if (ch->IsNpc () || vch->IsNpc ())
    return TRUE;
  if (!ch->IsPkiller () || !vch->IsPkiller ())
    return FALSE;
  if (ch->GetPcData ()->GetClan () && ch->GetPcData ()->GetClan () == vch->GetPcData ()->GetClan ())
    return FALSE;
  return TRUE;
}


extern char *target_name;	/* from magic.c */

// Perform a binary search on a section of the skill table
// Each different section of the skill table is sorted alphabetically
// Only match skills player knows				-Thoric
BOOL check_skill (CCharacter *ch, char *command, char *argument)
{
	int		sn;
	int		first = gsn_first_skill;
	int		top = gsn_first_weapon-1;
	int		mana, blood;
	struct	timeval time_used;

	// bsearch for the skill
	for (;;) {
		sn = (first + top) >> 1;

		if (LOWER (command[0]) == LOWER (SkillTable.GetName (sn)[0])
			&& !str_prefix (command, SkillTable.GetName (sn))
			&& (SkillTable.GetSkillFunction (sn) ||
			SkillTable.GetSpellFunction (sn) != spell_null)
			&& (can_use_skill (ch, 0, sn)))
				break;
		if (first >= top)
			return FALSE;
		if (strcmp (command, SkillTable.GetName (sn)) < 1)
			top = sn - 1;
		else
			first = sn + 1;
	}

	if (! check_pos (ch, SkillTable.GetMinimumPosition (sn)))
		return TRUE;

	if (ch->IsNpc () && (ch->IsCharmed () || ch->IsPossessed ())) {
		ch->SendText ("For some reason, you seem unable to perform that...\n\r");
		act (AT_GREY,"$n wanders around aimlessly.", ch, NULL, NULL, TO_ROOM);
		return TRUE;
	}

	// check if mana is required
	if (SkillTable.GetMinMana (sn)) {
		mana = ch->IsNpc () ? 0 : UMAX (SkillTable.GetMinMana (sn),
			100 / (2 + ch->GetLevel ()
			- SkillTable.GetClassLevel (sn, ch->GetClass ())));
		blood = UMAX (1, (mana+4) / 8);	// NPCs don't have PCDatas. -- Altrag

		if (ch->IsVampire ()) {
			if (ch->GetPcData ()->condition [COND_BLOODTHIRST] < blood) {
				ch->SendText ("You don't have enough blood power.\n\r");
				return TRUE;
			}
		}
		else if (! ch->IsNpc () && ch->GetMana () < mana) {
			ch->SendText ("You don't have enough mana.\n\r");
			return TRUE;
		}
	} else {
		mana = 0;
		blood = 0;
	}

	// Is this a real do-fun, or a really a spell?
	if (! SkillTable.GetSkillFunction (sn)) {
		ch_ret		retcode = rNONE;
		void		*vo = NULL;
		CCharacter	*victim = NULL;
		CObjData	*obj = NULL;

		target_name = "";

		switch (SkillTable.GetTarget (sn)) {
		  default:
			bug ("Check_skill: bad target for sn %d.", sn);
			ch->SendText ("Something went wrong...\n\r");
			return TRUE;

		  case TAR_IGNORE:
			vo = NULL;
			if (argument [0] == '\0') {
				if (victim = ch->GetFightWho ())
					target_name = NCCP victim->GetName ();
			}
			else
				target_name = argument;
			break;

		  case TAR_CHAR_OFFENSIVE:
			if (argument [0] == '\0'
			  && (victim = ch->GetFightWho ()) == NULL) {
				ch->SendTextf ("%s who?\n\r",
					capitalize (SkillTable.GetName (sn)));
				return TRUE;
			}
			else if (argument[0] != '\0'
			  && (victim=get_char_room (ch, argument)) == NULL) {
				ch->SendText ("They aren't here.\n\r");
				return TRUE;
			}
			if (is_safe (ch, victim))
				return TRUE;
			vo = victim;
			break;

		  case TAR_CHAR_DEFENSIVE:
			if (argument [0] != '\0'
			  && (victim=get_char_room (ch, argument)) == NULL) {
				ch->SendText ("They aren't here.\n\r");
				return TRUE;
			}
			if (! victim)
				victim = ch;
			vo = victim;
			break;

		  case TAR_CHAR_SELF:
			vo = ch;
			break;

		  case TAR_OBJ_INV:
			if ((obj = get_obj_carry (ch, argument)) == NULL) {
				ch->SendText ("You can't find that.\n\r");
				return TRUE;
			}
			vo = obj;
			break;
		}

		// waitstate
		WAIT_STATE (ch, SkillTable.GetBeats (sn));
		// check for failure
		if ((number_percent () + SkillTable.GetDifficulty (sn) * 5)
		  > (ch->IsNpc () ? 75 : ch->GetPcData ()->learned [sn])) {
			failed_casting (SkillTable.GetSkill (sn), ch, (CCharacter*) vo,
				obj);
			learn_from_failure (ch, sn);
			if (mana) {
				if (ch->IsVampire ())
					gain_condition (ch, COND_BLOODTHIRST, - blood/2);
				else
					ch->AddMana (-mana/2);
			}
			return TRUE;
		}
		if (mana) {
			if (ch->IsVampire ())
				gain_condition (ch, COND_BLOODTHIRST, - blood);
			else
				ch->AddMana (-mana);
		}
		start_timer (&time_used);
		retcode =
			(*SkillTable.GetSpellFunction (sn)) (sn, ch->GetLevel (),ch,vo);
		end_timer (&time_used);
		update_userec (&time_used, &SkillTable.GetUseRec (sn));

		if (retcode == rCHAR_DIED || retcode == rERROR)
			return TRUE;

		if (char_died (ch))
			return TRUE;

		if (retcode == rSPELL_FAILED) {
			learn_from_failure (ch, sn);
			retcode = rNONE;
		}
		else
			learn_from_success (ch, sn);

		if (SkillTable.GetTarget (sn) == TAR_CHAR_OFFENSIVE
		  && victim != ch && ! char_died (victim)) {
			CCharacter	*vch;
			CCharacter	*vch_next;

			for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
				vch_next = vch->GetNextInRoom ();
				if (victim == vch && ! victim->GetFightData ()
				  && victim->GetMaster () != ch) {
					retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
					break;
				}
			}
		}
		return TRUE;
	}

	if (mana) {
		if (ch->IsVampire ())
			gain_condition (ch, COND_BLOODTHIRST, - blood);
		else
			ch->AddMana (-mana);
	}

	ch->prev_cmd = ch->last_cmd;	// haus, for automapping
	ch->last_cmd = SkillTable.GetSkillFunction (sn);
	start_timer (&time_used);
	(*SkillTable.GetSkillFunction (sn)) (ch, argument);
	end_timer (&time_used);
	update_userec (&time_used, &SkillTable.GetUseRec (sn));

	return TRUE;
}


// Lookup a skills information
// High god command
void do_slookup (CCharacter *ch, char *argument)
{
	char	buf [MAX_STRING_LENGTH];
	char	arg [MAX_INPUT_LENGTH];
	int		sn;
	int		iClass;
	CSkill	*pSkill = NULL;

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

	if (! str_cmp (arg, "all")) {
		for (sn=0; sn < SkillTable.GetCount (); ++sn) {
			if (pSkill = SkillTable.GetSkill (sn))
				pager_printf (ch,
					"Sn: %4d Slot: %4d Skill/spell: '%-20s' Damtype: %s\n\r",
					sn, pSkill->GetSlot (), pSkill->GetName (),
					spell_damage [pSkill->GetSpellDamage ()]);
		}
	}
	else if (! str_cmp (arg, "herbs")) {
		for (sn=0; sn < HerbTable.GetCount (); ++sn) {
			pSkill = HerbTable.GetSkill (sn);
			pager_printf (ch, "%d) %s\n\r", sn, pSkill->GetName ());
		}
	} else {
		pSkill = NULL;
		CSmaugAffect	*aff;
		int				cnt = 0;

		if (arg [0] == 'h' && is_number (arg+1)) {
			sn = atoi (arg+1);
			if (! HerbTable.IsValid (sn)) {
				ch->SendText ("Invalid herb.\n\r");
				return;
			}
			pSkill = HerbTable.GetSkill (sn);
		}
		else if (is_number (arg)) {
			sn = atoi (arg);
			if ((pSkill = SkillTable.GetValidSkill (sn)) == NULL) {
				ch->SendText ("Invalid sn.\n\r");
				return;
			}
			sn %= 1000;
		}
		else
			if ((sn = SkillTable.Lookup (arg)) >= 0)
				pSkill = SkillTable.GetSkill (sn);
		else
			if ((sn = HerbTable.Lookup (arg)) >= 0)
				pSkill = HerbTable.GetSkill (sn);
		else {
			ch->SendText ("No such skill, spell, proficiency or tongue.\n\r");
			return;
		}

		if (! pSkill) {
			ch->SendText ("Not created yet.\n\r");
			return;
		}

		ch->SendTextf ("Sn: %4d Slot: %4d %s: '%-20s'\n\r",
			sn, pSkill->GetSlot (), skill_tname [pSkill->GetType ()],
			pSkill->GetName ());

		if (pSkill->GetInfo ())
			ch->SendTextf ("Damtype: %s  Acttype: %s   Classtype: %s"
				"   Powertype: %s\n\r",
				spell_damage [pSkill->GetSpellDamage ()],
				spell_action [pSkill->GetSpellAction ()],
				spell_class [pSkill->GetSpellClass ()],
				spell_power [pSkill->GetSpellPower ()]);

		if (pSkill->GetFlags ()) {
			strcpy (buf, "Flags:");
			for (int x = 0; x < 32; x++)
				if (pSkill->HasSpell (1 << x)) {
					strcat (buf, " ");
					strcat (buf, spell_flag [x]);
				}
			strcat (buf, "\n\r");
			ch->SendText (buf);
		}

		ch->SendTextf ("Saves: %s  SaveEffect: %s\n\r",
			pSkill->GetSaveName (), pSkill->GetSaveEffectName ());

		if (pSkill->GetDifficulty () != '\0')
			ch->SendTextf ("Difficulty: %d\n\r", pSkill->GetDifficulty ());

		ch->SendTextf ("Type: %s  Target: %s  Minpos: %d  Mana: %d  "
			"Beats: %d  Range: %d\n\r", skill_tname [pSkill->GetType ()],
			target_type [URANGE (TAR_IGNORE, pSkill->GetTarget (),
				TAR_OBJ_INV)],
			pSkill->GetMinimumPosition (), pSkill->GetMinMana (),
			pSkill->GetBeats (), pSkill->GetRange ());

		ch->SendTextf (
			"Flags: %d  Guild: %d  Value: %d  Info: %d  Code: %s\n\r",
			pSkill->GetFlags (), pSkill->GetGuild (), pSkill->GetValue (),
			pSkill->GetInfo (), pSkill->GetSkillFunction () ?
				skill_name (pSkill->GetSkillFunction ())
				: spell_name (pSkill->GetSpellFunction ()));

		ch->SendTextf ("Dammsg: %s\n\rWearoff: %s\n",
			pSkill->GetDamageMsg (),
			pSkill->GetOffMsg () ? pSkill->GetOffMsg () : "(none set)");

		if (pSkill->GetDiceRoll () && pSkill->GetDiceRoll ()[0] != '\0')
			ch->SendTextf ("Dice: %s\n\r", pSkill->GetDiceRoll ());
		if (pSkill->GetTeachers () && pSkill->GetTeachers ()[0] != '\0')
			ch->SendTextf ("Teachers: %s\n\r", pSkill->GetTeachers ());
		if (pSkill->GetComponents () && pSkill->GetComponents ()[0] != '\0')
			ch->SendTextf ("Components: %s\n\r", pSkill->GetComponents ());
		if (pSkill->GetParticipants ())
			ch->SendTextf ("Participants: %d\n\r", pSkill->GetParticipants ());
		if (pSkill->GetUseRec ().num_uses)
			send_timer (&pSkill->GetUseRec (), ch);

		for (aff = pSkill->GetAffects (); aff; aff = aff->GetNext ()) {
			if (aff == pSkill->GetAffects ())
				ch->SendText ("\n\r");
			sprintf (buf, "Affect %d", ++cnt);
			if (aff->location) {
				strcat (buf, " modifies ");
				strcat (buf, a_types [aff->location % REVERSE_APPLY]);
				strcat (buf, " by '");
				strcat (buf, aff->modifier);
				strcat (buf, (aff->bitvector >= 0) ? "' and" : "'");
			}
			if (aff->bitvector >= 0) {
				strcat (buf, " applies ");
				strcat (buf, CAffectFlags::GetName (aff->bitvector));
			}
			if (aff->duration [0] != '\0' && aff->duration [0] != '0') {
				strcat (buf, " for '");
				strcat (buf, aff->duration);
				strcat (buf, "' rounds");
			}
			if (aff->location >= REVERSE_APPLY)
				strcat (buf, " (affects caster only)");
			strcat (buf, "\n\r");
			ch->SendText (buf);
			if (! aff->GetNext ())
				ch->SendText ("\n\r");
		}

		if (pSkill->ValidHitCharMsg ())
			ch->SendTextf ("Hitchar   : %s\n\r", pSkill->GetHitCharMsg ());
		if (pSkill->ValidHitVictMsg ())
			ch->SendTextf ("Hitvict   : %s\n\r", pSkill->GetHitVictMsg ());
		if (pSkill->ValidHitRoomMsg ())
			ch->SendTextf ("Hitroom   : %s\n\r", pSkill->GetHitRoomMsg ());
		if (pSkill->ValidHitDest ())
			ch->SendTextf ("Hitdest   : %s\n\r", pSkill->GetHitDest ());
		if (pSkill->ValidMissCharMsg ())
			ch->SendTextf ("Misschar  : %s\n\r", pSkill->GetMissCharMsg ());
		if (pSkill->ValidMissVictMsg ())
			ch->SendTextf ("Missvict  : %s\n\r", pSkill->GetMissVictMsg ());
		if (pSkill->ValidMissRoomMsg ())
			ch->SendTextf ("Missroom  : %s\n\r", pSkill->GetMissRoomMsg ());
		if (pSkill->ValidDieCharMsg ())
			ch->SendTextf ("Diechar   : %s\n\r", pSkill->GetDieCharMsg ());
		if (pSkill->ValidDieVictMsg ())
			ch->SendTextf ("Dievict   : %s\n\r", pSkill->GetDieVictMsg ());
		if (pSkill->ValidDieRoomMsg ())
			ch->SendTextf ("Dieroom   : %s\n\r", pSkill->GetDieRoomMsg ());
		if (pSkill->ValidImmuneCharMsg ())
			ch->SendTextf ("Immchar   : %s\n\r", pSkill->GetImmuneCharMsg ());
		if (pSkill->ValidImmuneVictMsg ())
			ch->SendTextf ("Immvict   : %s\n\r", pSkill->GetImmuneVictMsg ());
		if (pSkill->ValidImmuneRoomMsg ())
			ch->SendTextf ("Immroom   : %s\n\r", pSkill->GetImmuneRoomMsg ());

		if (pSkill->GetType () != SKILL_HERB) {
			if (pSkill->GetType () != SKILL_RACIAL) {
				ch->SendText ("--------------------------[CLASS USE]-----"
					"---------------------\n\r");
				for (iClass = 0; iClass < ClassTable.GetCount (); ++iClass) {
					strcpy (buf, ClassTable.GetName (iClass));
					sprintf (buf+3, ") lvl: %3d max: %2d%%",
						pSkill->GetClassLevel (iClass),
						pSkill->GetClassAdept (iClass));
					if (iClass % 3 == 2)
						strcat (buf, "\n\r");
					else
						strcat (buf, "  ");
					ch->SendText (buf);
				}
			} else {
				ch->SendText ("\n\r--------------------------[RACE USE]---"
					"-----------------------\n\r");
				for (int ra=0; ra < RaceTable.GetCount (); ++ra) {
					sprintf (buf, "%8.8s) lvl: %3d max: %2d%%",
						RaceTable.GetName (ra),
						pSkill->GetRaceLevel (ra),
						pSkill->GetRaceAdept (ra));
					if (ra % 3 == 2)
						strcat (buf, "\n\r");
					else
						strcat (buf, "  ");
					ch->SendText (buf);
				}
			}
		}
		ch->SendText ("\n\r");
	}
}


// Set a skill's attributes or what skills a player has.
// High god command, with support for creating skills/spells/herbs/etc
void do_sset (CCharacter *ch, char *argument)
{
	char		arg1 [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];
	CCharacter	*victim;
	int			value;
	int			sn;
	int         i;
	BOOL		fAll;

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

	if (arg1 [0] == '\0' || arg2 [0] == '\0') {
		SkillHelp (ch);
		return;
	}

	if (ch->GetTrustLevel () > LEVEL_SUB_IMPLEM
	  && ! str_cmp (arg1, "save")) {
		if (! str_cmp (arg2, "skills")) {
			ch->SendText ("Saving skill table...\n\r");
			save_skill_table ();
			return;
		}
		if (! str_cmp (arg2, "classes")) {
			ch->SendText ("Saving classes...\n\r");
			ClassTable.Save ();
			return;
		}
		if (! str_cmp (arg2, "races")) {
			ch->SendText ("Saving races...\n\r");
			RaceTable.Save ();
			return;
		}
		if (! str_cmp (arg2, "herbs")) {
			ch->SendText ("Saving herb table...\n\r");
			save_herb_table ();
			return;
		}
	}

	if (argument [0] == '\0') {
		SkillHelp (ch);
		return;
	}

	if (ch->GetTrustLevel () > LEVEL_SUB_IMPLEM
	  && !str_cmp (arg1, "create")
	  && (!str_cmp (arg2, "skill") || !str_cmp (arg2, "herb")
	   || !str_cmp (arg2, "ability"))) {
		CSkill	*skill;
		short	type = SKILL_UNKNOWN;

		if (! str_cmp (arg2, "herb")) {
			type = SKILL_HERB;
			if (HerbTable.GetCount () >= MAX_HERB) {
				ch->SendTextf ("The current top herb is %d, which is the "
				"maximum.  To add more herbs,\n\rMAX_HERB will have to be "
				"raised in smaug.h, and the mud recompiled.\n\r",
				HerbTable.GetCount ());
				return;
			}
		}
		else if (SkillTable.GetCount () >= MAX_SKILL) {
			ch->SendTextf ("The current top sn is %d, which is the maximum.  "
			"To add more skills,\n\rMAX_SKILL will have to be "
			"raised in smaug.h, and the mud recompiled.\n\r",
			SkillTable.GetCount ());
			return;
		}

		skill = new CSkill;
		if (type == SKILL_HERB)
			HerbTable.Add (skill);
		else SkillTable.Add (skill);

		skill->SetName (str_dup (argument));
		skill->SetDamageMsg (str_dup (""));
		skill->SetOffMsg (str_dup (""));
		skill->SetSpellFunction (spell_smaug);
		skill->SetType (type);
		skill->SetGuild (CLASS_NONE);

		if (! str_cmp (arg2, "ability"))
			skill->SetType (SKILL_RACIAL);

		for (i=0; i < ClassTable.GetCount (); ++i) {
			skill->SetClassLevel (i, LEVEL_IMMORTAL);
			skill->SetClassAdept (i, 95);
		}
		for (i=0; i < RaceTable.GetCount (); ++i) {
			skill->SetRaceLevel (i, LEVEL_IMMORTAL);
			skill->SetRaceAdept (i, 95);
		}

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

	if (arg1 [0] == 'h')
		sn = atoi (arg1+1);
	else
		sn = atoi (arg1);

	if (ch->GetTrustLevel () > LEVEL_GREATER
	  && ((arg1[0] == 'h' && is_number (arg1+1) && (sn=atoi (arg1+1))>=0)
	  || (is_number (arg1) && (sn=atoi (arg1)) >= 0))) {
		CSkill	*skill;

		if (arg1 [0] == 'h') {
			if (sn >= HerbTable.GetCount ()) {
				ch->SendText ("Herb number out of range.\n\r");
				return;
			}
			skill = HerbTable.GetSkill (sn);
		} else {
			if ((skill=SkillTable.GetValidSkill (sn)) == NULL) {
				ch->SendText ("Skill number out of range.\n\r");
				return;
			}
			sn %= 1000;
		}

		if (! str_cmp (arg2, "difficulty")) {
			skill->SetDifficulty (atoi (argument));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "participants")) {
			skill->SetParticipants (atoi (argument));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "damtype")) {
			int x = get_sdamage (argument);

			if (x == -1)
				ch->SendText ("Not a spell damage type.\n\r");
			else {
				SET_SDAM (skill, x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "acttype")) {
			int x = get_saction (argument);

			if (x == -1)
				ch->SendText ("Not a spell action type.\n\r");
			else {
				SET_SACT (skill, x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "classtype")) {
			int x = get_sclass (argument);

			if (x == -1)
				ch->SendText ("Not a spell class type.\n\r");
			else {
				SET_SCLA (skill, x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "powertype")) {
			int x = get_spower (argument);

			if (x == -1)
				ch->SendText ("Not a spell power type.\n\r");
			else {
				SET_SPOW (skill, x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "seffect")) {
			int x = SkillTable.GetSaveEffectFromName (argument);

			if (x == -1)
				ch->SendText ("Not a spell save effect type.\n\r");
			else {
				skill->SetSaveEffect (x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "flag")) {
			int x = get_sflag (argument);

			if (x == -1)
				ch->SendText ("Not a spell flag.\n\r");
			else {
				int	flgs = skill->GetFlags ();
				TOGGLE_BIT (flgs, 1 << (x));
				skill->SetFlags (flgs);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "saves")) {
			int x = get_ssave (argument);

			if (x == -1)
				ch->SendText ("Not a saving type.\n\r");
			else {
				skill->SetSaves (x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "code")) {
			SPELL_FUN	*spellfun;
			DO_FUN		*dofun;

			if ((spellfun=spell_function (argument)) != spell_notfound) {
				skill->SetSpellFunction (spellfun);
				skill->SetSkillFunction (NULL);
			}
			else if ((dofun=skill_function (argument)) != skill_notfound) {
				skill->SetSkillFunction (dofun);
				skill->SetSpellFunction (NULL);
			} else {
				ch->SendText ("Not a spell or skill.\n\r");
				return;
			}
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "target")) {
			int x = get_starget (argument);

			if (x == -1)
				ch->SendText ("Not a valid target type.\n\r");
			else {
				skill->SetTarget (x);
				ch->SendText ("Ok.\n\r");
			}
			return;
		}

		if (! str_cmp (arg2, "minpos")) {
			skill->SetMinimumPosition (URANGE (POS_DEAD, atoi (argument), POS_DRAG));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "minlevel")) {
			skill->SetMinLevel (URANGE (1, atoi (argument), MAX_LEVEL));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "slot")) {
			skill->SetSlot (URANGE (0, atoi (argument), 30000));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "mana")) {
			skill->SetMinMana (URANGE (0, atoi (argument), 2000));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "beats")) {
			skill->SetBeats (URANGE (0, atoi (argument), 120));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "range")) {
			skill->SetRange (URANGE (0, atoi (argument), 20));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "guild")) {
			skill->SetGuild (atoi (argument));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "value")) {
			skill->SetValue (atoi (argument));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "type")) {
			skill->SetType (get_skill (argument));
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "rmaffect")) {
			CSmaugAffect *aff = skill->GetAffects ();
			CSmaugAffect *aff_next;
			int num = atoi (argument);
			int cnt = 1;

			if (! aff) {
				ch->SendText ("This spell has no special affects to remove.\n\r");
				return;
			}

			if (num == 1) {
				skill->SetAffects (aff->GetNext ());
				delete aff;
				ch->SendText ("Removed.\n\r");
				return;
			}

			for (; aff; aff = aff->GetNext ()) {
				if (++cnt == num && (aff_next=aff->GetNext ()) != NULL) {
					aff->SetNext (aff_next->GetNext ());
					delete aff_next;
					ch->SendText ("Removed.\n\r");
					return;
				}
			}
			ch->SendText ("Not found.\n\r");
			return;
		}

		// affect <location> <modifier> <duration> <bitvector>
		if (! str_cmp (arg2, "affect")) {
			char	location [MAX_INPUT_LENGTH];
			char	modifier [MAX_INPUT_LENGTH];
			char	duration [MAX_INPUT_LENGTH];
			int		loc, bit, tmpbit;
			CSmaugAffect *aff;

			argument = one_argument (argument, location);
			argument = one_argument (argument, modifier);
			argument = one_argument (argument, duration);

			if (location [0] == '!')
				loc = get_atype (location+1) + REVERSE_APPLY;
			else
				loc = get_atype (location);

			if ((loc % REVERSE_APPLY) < 0
			  || (loc % REVERSE_APPLY) >= MAX_APPLY_TYPE) {
				ch->SendText ("Unknown affect location.  See AFFECTTYPES.\n\r");
				return;
			}

			bit = -1;
			if (argument [0] != 0) {
				if ((tmpbit = get_aflag (argument)) == -1)
					ch->SendTextf ("Unknown bitvector: %s.  See "
						"AFFECTED_BY\n\r", argument);
				else
					bit = tmpbit;
			}

			aff = new CSmaugAffect;
			if (! str_cmp (duration, "0"))
				duration [0] = '\0';
			if (! str_cmp (modifier, "0"))
				modifier [0] = '\0';
			aff->duration = str_dup (duration);
			aff->location = loc;
			aff->modifier = str_dup (modifier);
			aff->bitvector = bit;
			aff->SetNext (skill->GetAffects ());
			skill->SetAffects (aff);
			ch->SendText ("Ok.\n\r");
			return;
		}

		if (! str_cmp (arg2, "level")) {
			char	arg3 [MAX_INPUT_LENGTH];
			int		Class;

			argument = one_argument (argument, arg3);
			Class = ClassTable.GetClass (arg3);
			if (Class == CLASS_NONE)
				ch->SendText ("Not a valid class.\n\r");
			else
				skill->SetClassLevel (Class, URANGE (0, atoi (argument), MAX_LEVEL));
			return;
		}

		if (! str_cmp (arg2, "racelevel")) {
			char	arg3 [MAX_INPUT_LENGTH];
			int		race;

			argument = one_argument (argument, arg3);
			race = RaceTable.GetRace (arg3);
			if (race == RACE_NONE)
				ch->SendText ("Not a valid race.\n\r");
			else
				skill->SetRaceLevel (race,
					URANGE (0, atoi (argument), MAX_LEVEL));
			return;
		}

		if (! str_cmp (arg2, "adept")) {
			char	arg3 [MAX_INPUT_LENGTH];
			int		Class;

			argument = one_argument (argument, arg3);
			Class = ClassTable.GetClass (arg3);
			if (Class == CLASS_NONE)
				ch->SendText ("Not a valid class.\n\r");
			else
				skill->SetClassAdept (Class,
					URANGE (0, atoi (argument), 100));
			return;
		}

		if (! str_cmp (arg2, "raceadept")) {
			char	arg3 [MAX_INPUT_LENGTH];
			int		race;

			argument = one_argument (argument, arg3);
			race = RaceTable.GetRace (arg3);
			if (race == RACE_NONE)
				ch->SendText ("Not a valid race.\n\r");
			else
				skill->SetRaceAdept (race,
					URANGE (0, atoi (argument), 100));
			return;
		}

		if (! str_cmp (arg2, "name")) {
			delete skill->GetName ();
			skill->SetName (str_dup (argument));
			ch->SendText ("Ok.\n\r");
			return;
		}

#define		REP(msg)	skill->msg (stricmp (argument, "clear") ?	\
						str_dup (argument) : NULL);					\
						ch->SendText ("Ok.\n\r")
//			if (! str_cmp (argument, "clear"))
//				skill->SetDamageMsg (NULL);
//			else
//				skill->SetDamageMsg (str_dup (argument));
//			ch->SendText ("Ok.\n\r");

		if (! str_cmp (arg2, "dammsg")) {
			delete skill->GetDamageMsg ();
			REP (SetDamageMsg);
			return;
		}

		if (! str_cmp (arg2, "wearoff")) {
			delete skill->GetOffMsg ();
			REP (SetOffMsg);
			return;
		}

		if (! str_cmp (arg2, "hitchar")) {
			delete skill->GetHitCharMsg ();
			REP (SetHitCharMsg);
			return;
		}

		if (! str_cmp (arg2, "hitvict")) {
			delete skill->GetHitVictMsg ();
			REP (SetHitVictMsg);
			return;
		}

		if (! str_cmp (arg2, "hitroom")) {
			delete skill->GetHitRoomMsg ();
			REP (SetHitRoomMsg);
			return;
		}

		if (! str_cmp (arg2, "hitdest")) {
			delete skill->GetHitDest ();
			REP (SetHitDest);
			return;
		}

		if (! str_cmp (arg2, "misschar")) {
			delete skill->GetMissCharMsg ();
			REP (SetMissCharMsg);
			return;
		}

		if (! str_cmp (arg2, "missvict")) {
			delete skill->GetMissVictMsg ();
			REP (SetMissVictMsg);
			return;
		}

		if (! str_cmp (arg2, "missroom")) {
			delete skill->GetMissRoomMsg ();
			REP (SetMissRoomMsg);
			return;
		}

		if (! str_cmp (arg2, "diechar")) {
			delete skill->GetDieCharMsg ();
			REP (SetDieCharMsg);
			return;
		}

		if (! str_cmp (arg2, "dievict")) {
			delete skill->GetDieVictMsg ();
			REP (SetDieVictMsg);
			return;
		}

		if (! str_cmp (arg2, "dieroom")) {
			delete skill->GetDieRoomMsg ();
			REP (SetDieRoomMsg);
			return;
		}

		if (! str_cmp (arg2, "immchar")) {
			delete skill->GetImmuneCharMsg ();
			REP (SetImmuneCharMsg);
			return;
		}

		if (! str_cmp (arg2, "immvict")) {
			delete skill->GetImmuneVictMsg ();
			REP (SetImmuneVictMsg);
			return;
		}

		if (! str_cmp (arg2, "immroom")) {
			delete skill->GetImmuneRoomMsg ();
			REP (SetImmuneRoomMsg);
			return;
		}

		if (! str_cmp (arg2, "dice")) {
			delete skill->GetDiceRoll ();
			REP (SetDiceRoll);
			return;
		}

		if (! str_cmp (arg2, "components")) {
			delete skill->GetComponents ();
			REP (SetComponents);
			return;
		}

		if (! str_cmp (arg2, "teachers")) {
			delete skill->GetTeachers ();
			REP (SetTeachers);
			return;
		}

		do_sset (ch, "");
		return;
	}

	if ((victim = get_char_world (ch, arg1)) == NULL) {
		if ((sn = SkillTable.Lookup (arg1)) >= 0) {
			sprintf (arg1, "%d %s %s", sn, arg2, argument);
			do_sset (ch, arg1);
		}
		else
			ch->SendText ("They aren't here.\n\r");
		return;
	}

	if (victim->IsNpc ()) {
		ch->SendText ("Not on NPC's.\n\r");
		return;
	}

	fAll = ! str_cmp (arg2, "all");
	sn   = 0;
	if (! fAll && (sn = SkillTable.Lookup (arg2)) < 0) {
		ch->SendText ("No such skill or spell.\n\r");
		return;
	}

	// Snarf the value.
	if (! is_number (argument)) {
		ch->SendText ("Value must be numeric.\n\r");
		return;
	}

	value = atoi (argument);
	if (value < 0 || value > 100) {
		ch->SendText ("Value range is 0 to 100.\n\r");
		return;
	}

	if (fAll) {
		for (sn = 0; sn < SkillTable.GetCount (); sn++) {
			// Fix by Narn to prevent ssetting skills
			// the player shouldn't have.
			if (SkillTable.GetName (sn) 
				&& (victim->GetLevel () >=
				SkillTable.GetClassLevel (sn,
					victim->GetClass ()) || value == 0))
						victim->GetPcData ()->learned [sn] = value;
		}
	}
	else victim->GetPcData ()->learned [sn] = value;
}


void SkillHelp (CCharacter* ch)
{
	ch->SendText ("Syntax: sset <victim> <skill> <value>\n\r");
	ch->SendText ("or:     sset <victim> all     <value>\n\r");
	if (ch->GetTrustLevel () > LEVEL_SUB_IMPLEM) {
		ch->SendText ("or:     sset save skills\n\r");
		ch->SendText ("or:     sset save herbs\n\r");
		ch->SendText ("or:     sset save classes\n\r");
		ch->SendText ("or:     sset save races\n\r");
		ch->SendText ("or:     sset create skill 'new skill'\n\r");
		ch->SendText ("or:     sset create herb 'new herb'\n\r");
		ch->SendText ("or:     sset create ability 'new ability'\n\r");
	}

	if (ch->GetTrustLevel () > LEVEL_GREATER) {
		ch->SendText ("or:     sset <sn>     <field> <value>\n\r");
		ch->SendText ("\n\rField being one of:\n\r");
		ch->SendText ("  name code target minpos slot mana beats dammsg wearoff guild minlevel\n\r");
		ch->SendText ("  type damtype acttype classtype powertype seffect flag dice value difficulty affect\n\r");
		ch->SendText ("  affect rmaffect level adept hit miss die imm (char/vict/room)\n\r");
		ch->SendText ("  components teachers racelevel raceadept\n\r");
		ch->SendText ("Affect having the fields: <location> <modfifier> [duration] [bitvector]\n\r");
		ch->SendText ("(See AFFECTTYPES for location, and AFFECTED_BY for bitvector)\n\r");
	}
	ch->SendText ("Skill being any skill or spell.\n\r");
}


void learn_from_success (CCharacter *ch, int sn)
{
	int		adept, gain, sklvl, learn, percent, chance;

	if (ch->IsNpc () || ch->GetPcData ()->learned [sn] == 0)
		return;

	short	&learned = ch->GetPcData ()->learned [sn];

	adept = SkillTable.GetClassAdept (sn, ch->GetClass ());
	if (learned >= adept)
		return;							// already knows it all

	sklvl = SkillTable.GetClassLevel (sn, ch->GetClass ());
	if (sklvl == 0)
		sklvl = ch->GetLevel ();

	chance = learned + (5 * SkillTable.GetDifficulty (sn));
	percent = number_percent ();

	if (percent >= chance)
		learn = 2;
	else
		if (chance - percent > 25)
			return;
		else
			learn = 1;

	learned = UMIN (adept, learned + learn);

	int	Mage = ClassTable.GetClass ("Mage");

	if (learned == adept) {							// now fully learned!
		gain = 1000 * sklvl;
		if (ch->GetClass () == Mage)
			gain *= 5;								// h, mage upgrade
		set_char_color (AT_WHITE, ch);
		ch->SendTextf ("You are now an adept of %s!  You gain %d bonus"
			" experience!\n\r", SkillTable.GetName (sn), gain);
	} else {
		gain = 20 * sklvl;
		if (ch->GetClass () == Mage)
			gain *= 6;								// h, mage upgrade
		if (! ch->GetFightData () && sn != gsn_hide && sn != gsn_sneak) {
			set_char_color (AT_WHITE, ch);
			ch->SendTextf (
				"You gain %d experience points from your success!\n\r", gain);
		}
	}
	gain_exp (ch, gain);
}


void learn_from_failure (CCharacter *ch, int sn)
{
	int		adept, chance;
	short	&learned = ch->GetPcData ()->learned [sn];

	if (ch->IsNpc () || learned == 0)
		return;

	chance = learned + (5 * SkillTable.GetDifficulty (sn));
	if (chance - number_percent () > 25)
		return;

	adept = SkillTable.GetClassAdept (sn, ch->GetClass ());

	if (learned < (adept-1))
		++learned;
}


void do_gouge (CCharacter *ch, char *argument)
{
	CCharacter	*victim;
	CAffectData	af;
	short		dam;
	int			chance;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! can_use_skill (ch, 0, gsn_gouge)) {
		ch->SendText ("You do not yet know of this skill.\n\r");
		return;
	}

	if (ch->mount) {
		ch->SendText ("You can't get close enough while mounted.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	chance = ((victim->GetDexterity () - ch->GetDexterity ()) * 10) + 10;
// 1.4add
//	if (! ch->IsNpc () && ! victim->IsNpc ())
//		chance += sysdata.gouge_plr_vs_plr;
//
//	if (victim->fighting && victim->fighting->who != ch)
//		chance += sysdata.gouge_nontank;

	if (can_use_skill (ch, (number_percent () + chance), gsn_gouge)) {
		dam = number_range (5, ch->GetLevel ());
		global_retcode = damage (ch, victim, dam, gsn_gouge);
		if (global_retcode == rNONE) {
			if (! victim->IsBlind ()) {
				af.type      = gsn_blindness;
				af.location  = APPLY_HITROLL;
				af.modifier  = -6;
				if (! victim->IsNpc () && ! ch->IsNpc ())
					af.duration = (ch->GetLevel () + 10) /
						victim->GetConstitution ();
				else af.duration  = 3 + (ch->GetLevel () / 15);
				af.bitvector = AFF_BLIND;
				affect_to_char (victim, &af);
				act (AT_SKILL, "You can't see a thing!",
					victim, NULL, NULL, TO_CHAR);
			}
			WAIT_STATE (ch, PULSE_VIOLENCE);
			if (! ch->IsNpc () && ! victim->IsNpc ()) {
				if (number_bits (1) == 0) {
					ch->SendTextf ("%s looks momentarily dazed.\n\r",
						victim->GetName ());
					victim->SendText ("You are momentarily dazed ...\n\r");
					WAIT_STATE (victim, PULSE_VIOLENCE);
				}
			}
			else WAIT_STATE (victim, PULSE_VIOLENCE);
			// Taken out by request - put back in by Thoric
			// This is how it was designed.  You'd be a tad stunned
			// if someone gouged you in the eye.
			// Mildly modified by Blodkai, Feb 1998 at request of
			// of pkill Conclave (peaceful use remains the same)
		}
		else if (global_retcode == rVICT_DIED) {
			act (AT_BLOOD, "Your fingers plunge into your victim's brain, "
				"causing immediate death!", ch, NULL, NULL, TO_CHAR);
		}
		if (global_retcode != rCHAR_DIED && global_retcode != rBOTH_DIED)
			learn_from_success (ch, gsn_gouge);
	} else {
			WAIT_STATE (victim, PULSE_VIOLENCE);
		WAIT_STATE (ch, SkillTable.GetBeats (gsn_gouge));
		global_retcode = damage (ch, victim, 0, gsn_gouge);
		learn_from_failure (ch, gsn_gouge);
	}
}


void do_detrap (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CObjData	*trap;
	CObjData	*obj;
	int			percent;
	BOOL		bFound = FALSE;
	POSITION	Cpos;

	switch (ch->GetSubstate ()) {
	  default:
		if (ch->IsNpc () && ch->IsCharmed ()) {
			ch->SendText ("You can't concentrate enough for that.\n\r");
			return;
		}

		argument = one_argument (argument, arg);
		if (! can_use_skill (ch, 0, gsn_detrap)) {
			ch->SendText ("You do not yet know of this skill.\n\r");
			return;
		}

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

		if (ms_find_obj (ch))
			return;

		if (ch->mount) {
			ch->SendText ("You can't do that while mounted.\n\r");
			return;
		}

		if (ch->GetInRoom ()->IsEmpty ()) {
			ch->SendText ("You can't find that here.\n\r");
			return;
		}

		Cpos = ch->GetInRoom ()->GetHeadContentPos ();
		while (obj = ch->GetInRoom ()->GetNextContent (Cpos)) {
			if (can_see_obj (ch, *obj)
			  && nifty_is_name (arg, obj->GetName ())) {
				bFound = TRUE;
				break;
			}
		}
		if (! bFound) {
			ch->SendText ("You can't find that here.\n\r");
			return;
		}

		act (AT_ACTION,
			"You carefully begin your attempt to remove a trap from $p...",
			ch, obj, NULL, TO_CHAR);
		act (AT_ACTION, "$n carefully attempts to remove a trap from $p...",
			ch, obj, NULL, TO_ROOM);
		ch->alloc_ptr = str_dup (obj->GetName ());
		add_timer (ch, TIMER_DO_FUN, 3, do_detrap, 1);
		/*	    WAIT_STATE (ch, skill_table[gsn_detrap]->beats); */
		return;

	  case 1:
		if (! ch->alloc_ptr) {
			ch->SendText ("Your detrapping was interrupted!\n\r");
			bug ("do_detrap: ch->dest_buf NULL!");
			return;
		}
		strcpy (arg, (char*) ch->alloc_ptr);
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		ch->SetSubstate (SUB_NONE);
		break;

	  case SUB_TIMER_DO_ABORT:
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		ch->SetSubstate (SUB_NONE);
		ch->SendText ("You carefully stop what you were doing.\n\r");
		return;
	}

	if (ch->GetInRoom ()->IsEmpty ()) {
		ch->SendText ("You can't find that here.\n\r");
		return;
	}

	Cpos = ch->GetInRoom ()->GetHeadContentPos ();
	while (obj = ch->GetInRoom ()->GetNextContent (Cpos)) {
		if (can_see_obj (ch, *obj) && nifty_is_name (arg, obj->GetName ())) {
			bFound = TRUE;
			break;
		}
	}
	if (! bFound) {
		ch->SendText ("You can't find that here.\n\r");
		return;
	}

	if ((trap = get_trap (obj)) == NULL) {
		ch->SendText ("You find no trap on that.\n\r");
		return;
	}

	percent = number_percent () - (ch->GetLevel () / 15) 
		- (get_curr_lck (ch) - 16);

	separate_obj (obj);
	if (can_use_skill (ch, percent, gsn_detrap)) {
		ch->SendText ("Ooops!\n\r");
		spring_trap (ch, trap);
		learn_from_failure (ch, gsn_detrap);
		return;
	}

	extract_obj (trap);

	ch->SendText ("You successfully remove a trap.\n\r");
	learn_from_success (ch, gsn_detrap);
}


void do_dig (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CObjData	*obj;
	BOOL		bFound;
	BOOL		bShovel;
	CExitData	*pexit;

	switch (ch->GetSubstate ()) {
	  default:
		if (ch->IsNpc () && ch->IsCharmed ()) {
			ch->SendText ("You can't concentrate enough for that.\n\r");
			return;
		}

		if (ch->mount) {
			ch->SendText ("You can't do that while mounted.\n\r");
			return;
		}

		one_argument (argument, arg);
		if (arg [0] != '\0') {
			if ((pexit = find_door (ch, arg, TRUE)) == NULL
			  && get_dir (arg) == -1) {
				ch->SendText ("What direction is that?\n\r");
				return;
			}
			if (pexit) {
				if (! pexit->IsDig () && ! pexit->IsClosed ()) {
					ch->SendText ("There is no need to dig out that exit.\n\r");
					return;
				}
			}
		} else {
			switch (ch->GetInRoom ()->sector_type) {
			  case SECT_CITY:
			  case SECT_INSIDE:
				ch->SendText ("The floor is too hard to dig through.\n\r");
				return;
			  case SECT_WATER_SWIM:
			  case SECT_WATER_NOSWIM:
			  case SECT_UNDERWATER:
				ch->SendText ("You cannot dig here.\n\r");
				return;
			  case SECT_AIR:
				ch->SendText ("What?  In the air?!\n\r");
				return;
			}
		}

		add_timer (ch,
			TIMER_DO_FUN, UMIN (SkillTable.GetBeats (gsn_dig) / 10, 3), 	
			do_dig, 1);
		ch->alloc_ptr = str_dup (arg);
		ch->SendText ("You begin digging...\n\r");
		act (AT_PLAIN, "$n begins digging...", ch, NULL, NULL, TO_ROOM);
		return;

	  case 1:
		if (! ch->alloc_ptr) {
			ch->SendText ("Your digging was interrupted!\n\r");
			act (AT_PLAIN, "$n's digging was interrupted!",
				ch, NULL, NULL, TO_ROOM);
			bug ("do_dig: dest_buf NULL");
			return;
		}
		strcpy (arg, (char*) ch->alloc_ptr);  
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		break;

	  case SUB_TIMER_DO_ABORT:
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		ch->SetSubstate (SUB_NONE);
		ch->SendText ("You stop digging...\n\r");
		act (AT_PLAIN, "$n stops digging...", ch, NULL, NULL, TO_ROOM);
		return;
	}

	ch->SetSubstate (SUB_NONE);

	// not having a shovel makes it harder to succeed
	bShovel = FALSE;
	POSITION	pos = ch->GetHeadCarryPos ();
	while (pos)
		if (ch->GetNextCarrying (pos)->item_type == ITEM_SHOVEL) {
			bShovel = TRUE;
			break;
		}

	// dig out an EX_DIG exit...
	if (arg [0] != '\0') {
		if ((pexit = find_door (ch, arg, TRUE)) != NULL
		  && pexit->IsDig () && pexit->IsClosed ()) {
			// 4 times harder to dig open a passage without a shovel
			if (can_use_skill (ch, (number_percent () * (bShovel ? 1 : 4)),
			  gsn_dig)) {
				pexit->ClrClosed ();
				ch->SendText ("You dig open a passageway!\n\r");
				act (AT_PLAIN, "$n digs open a passageway!",
					ch, NULL, NULL, TO_ROOM);
				learn_from_success (ch, gsn_dig);
				return;
			}
		}
		learn_from_failure (ch, gsn_dig);
		ch->SendText ("Your dig did not discover any exit...\n\r");
		act (AT_PLAIN, "$n's dig did not discover any exit...",
			ch, NULL, NULL, TO_ROOM);
		return;
	}

	bFound = FALSE;
	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (obj = ch->GetInRoom ()->GetNextContent (pos)) {
		// twice as hard to find something without a shovel
		if (obj->IsBuried ()
		  && (can_use_skill (ch, (number_percent () * (bShovel ? 1 : 2)),
		  gsn_dig))) {
			bFound = TRUE;
			break;
		}
	}

	if (! bFound) {
		ch->SendText ("Your dig uncovered nothing.\n\r");
		act (AT_PLAIN, "$n's dig uncovered nothing.", ch, NULL, NULL, TO_ROOM);
		learn_from_failure (ch, gsn_dig);
		return;
	}

	separate_obj (obj);
	obj->ClrBuried ();
	act (AT_SKILL, "Your dig uncovered $p!", ch, obj, NULL, TO_CHAR);
	act (AT_SKILL, "$n's dig uncovered $p!", ch, obj, NULL, TO_ROOM);
	learn_from_success (ch, gsn_dig);
	if (obj->item_type == ITEM_CORPSE_PC
		|| obj->item_type == ITEM_CORPSE_NPC)
			adjust_favor (ch, 14, 1);
} 

    
void do_search (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CObjData	*obj;
	CObjData	*container;
	int			percent;
	int			door = -1;

	switch (ch->GetSubstate ()) {
	  default:
		if (ch->IsNpc () && ch->IsCharmed ()) {
			ch->SendText ("You can't concentrate enough for that.\n\r");
			return;
		}

		if (ch->mount) {
			ch->SendText ("You can't do that while mounted.\n\r");
			return;
		}

		argument = one_argument (argument, arg);
		if (arg [0] != '\0' && (door = get_door (arg)) == -1) {
			container = get_obj_here (ch, arg);
			if (! container) {
				ch->SendText ("You can't find that here.\n\r");
				return;
			}
			if (container->item_type != ITEM_CONTAINER) {
				ch->SendText ("You can't search in that!\n\r");
				return;
			}
			if (IS_SET (container->value [1], CONT_CLOSED)) {
				ch->SendText ("It is closed.\n\r");
				return;
			}
		}

		add_timer (ch, TIMER_DO_FUN,
			UMIN (SkillTable.GetBeats (gsn_search) / 10, 3), do_search, 1);
		ch->SendText ("You begin your search...\n\r");
		ch->alloc_ptr = str_dup (arg);
		return;

	  case 1:
		if (! ch->alloc_ptr) {
			ch->SendText ("Your search was interrupted!\n\r");
			bug ("do_search: dest_buf NULL");
			return;
		}
		strcpy (arg, (char*) ch->alloc_ptr);
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		break;
	  case SUB_TIMER_DO_ABORT:
		delete ch->alloc_ptr;
		ch->alloc_ptr = NULL;
		ch->SetSubstate (SUB_NONE);
		ch->SendText ("You stop your search...\n\r");
		return;
	}

	ch->SetSubstate (SUB_NONE);
	CObjectList	*pList = NULL;
	if (arg [0] == '\0')
		pList = &ch->GetInRoom ()->GetContentList ();
	else {
		if ((door = get_door (arg)) == -1) {
			container = get_obj_here (ch, arg);
			if (! container) {
				ch->SendText ("You can't find that here.\n\r");
				return;
			}
			pList = &container->GetContentList ();
		}
	}

	if ((! pList && door == -1) || ch->IsNpc ()) {
		ch->SendText ("You find nothing.\n\r");
		learn_from_failure (ch, gsn_search);
		return;
	}

	percent = number_percent () + number_percent () - (ch->GetLevel () / 10);

	if (door != -1) {
		CExitData *pexit;

		if ((pexit = get_exit (ch->GetInRoom (), door)) != NULL
		  && pexit->IsSecret () && pexit->CanSearch ()
		  && can_use_skill (ch, percent, gsn_search)) {
			act (AT_SKILL, "Your search reveals the $d!",
				ch, NULL, pexit->keyword, TO_CHAR);
			act (AT_SKILL, "$n finds the $d!",
				ch, NULL, pexit->keyword, TO_ROOM);
			pexit->ClrSecret ();
			learn_from_success (ch, gsn_search);
			return;
		}
	} else {
		POSITION	pos = pList->GetHeadPosition ();
		while (obj = pList->GetNext (pos)) {
			if (obj->IsHidden ()
			  && can_use_skill (ch, percent, gsn_search)) {
				separate_obj (obj);
				obj->ClrHidden ();
				act (AT_SKILL, "Your search reveals $p!", ch, obj,NULL,TO_CHAR);
				act (AT_SKILL, "$n finds $p!", ch, obj, NULL, TO_ROOM );
				learn_from_success (ch, gsn_search);
				return;
			}
		}
	}
	ch->SendText ("You find nothing.\n\r");
	learn_from_failure (ch, gsn_search);
}


void do_steal (CCharacter *ch, char *argument)
{
    char buf  [MAX_STRING_LENGTH];
    char arg1 [MAX_INPUT_LENGTH];
    char arg2 [MAX_INPUT_LENGTH];
    CCharacter *victim, *mst;
    CObjData *obj;
    int percent;

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

    if (ch->mount)
    {
	ch->SendText ("You can't do that while mounted.\n\r");
	return;
    }

    if (arg1[0] == '\0' || arg2[0] == '\0')
    {
	ch->SendText ("Steal what from whom?\n\r");
	return;
    }

    if (ms_find_obj (ch))
	return;

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

    if (victim == ch)
    {
	ch->SendText ("That's pointless.\n\r");
	return;
    }

    if (ch->GetInRoom ()->IsSafe ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("A magical force interrupts you.\n\r");
		return;
    }

/* Disabled stealing among players because of complaints naked avatars were 
   running around stealing eq from equipped pkillers. -- Narn
*/
/*    if (check_illegal_psteal (ch, victim))
    {
	ch->SendText ("You can't steal from that player.\n\r");
	return;
    }
*/
    if (!ch->IsNpc () && !victim->IsNpc ())
    {
	set_char_color (AT_IMMORT, ch);
	ch->SendText ("The gods forbid theft between players.\n\r");
	return;
    }

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_steal));
    percent  = number_percent () + (victim->IsAwake () ? 10 : -50)
	       - (get_curr_lck (ch) - 15) + (get_curr_lck (victim) - 13);

    /* Changed the level check, made it 10 levels instead of five and made the 
       victim not attack in the case of a too high level difference.  This is 
       to allow mobprogs where the mob steals eq without having to put level 
       checks into the progs.  Also gave the mobs a 10% chance of failure.
    */
    if (ch->GetLevel () + 10 < victim->GetLevel ())
    {
	ch->SendText ("You really don't want to try that!\n\r");
        return;
    } 

	if (victim->GetPosition () == POS_FIGHTING
	  || ! can_use_skill (ch, percent, gsn_steal))
	{
	/*
	 * Failure.
	 */
	ch->SendText ("Oops...\n\r");
	act (AT_ACTION, "$n tried to steal from you!\n\r", ch, NULL, victim, TO_VICT   );
	act (AT_ACTION, "$n tried to steal from $N.\n\r",  ch, NULL, victim, TO_NOTVICT);

	sprintf (buf, "%s is a bloody thief!", ch->GetName ());
	do_yell (victim, buf);

	learn_from_failure (ch, gsn_steal);
	if (!ch->IsNpc ())
	{
	    if (legal_loot (ch, victim))
	    {
		global_retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
	    }
	    else
	    {
		/* gpDoc->LogString (buf); */
		if (ch->IsNpc ())
		{
		  if ((mst = ch->GetMaster ()) == NULL)
		    return;
		}
		else
		  mst = ch;
		if (mst->IsNpc ())
		  return;
		if (! mst->IsThief ())
		{
		    mst->SetThief ();
		    set_char_color (AT_WHITE, ch);
		    ch->SendText ("A strange feeling grows deep inside you, and a tingle goes up your spine...\n\r");
		    set_char_color (AT_IMMORT, ch);
		    ch->SendText ("A deep voice booms inside your head, 'Thou shall now be known as a lowly thief!'\n\r");
		    set_char_color (AT_WHITE, ch);
		    ch->SendText ("You feel as if your soul has been revealed for all to see.\n\r");
		    save_char_obj (mst);
		}
	    }
	}

	return;
    }

    if (!str_cmp (arg1, "coin" )
    ||   !str_cmp (arg1, "coins")
    ||   !str_cmp (arg1, "gold" ))
    {
	int amount;

	amount = (int) (victim->GetGold () * number_range (1, 10) / 100);
	if (amount <= 0)
	{
	    ch->SendText ("You couldn't get any gold.\n\r");
	    learn_from_failure (ch, gsn_steal);
	    return;
	}

	ch->AddGold (amount);
	victim->AddGold (-amount);
	ch->SendTextf ("Aha!  You got %d gold coins.\n\r", amount);
	learn_from_success (ch, gsn_steal);
	return;
    }

    if ((obj = get_obj_carry (victim, arg1)) == NULL)
    {
	ch->SendText ("You can't seem to find it.\n\r");
	learn_from_failure (ch, gsn_steal);
	return;
    }

    if (!can_drop_obj (ch, obj)
    || obj->IsInventory ()
    || obj->IsPrototype ()
    || obj->level > ch->GetLevel ())
    {
	ch->SendText ("You can't manage to pry it away.\n\r");
	learn_from_failure (ch, gsn_steal);
	return;
    }

    if (ch->carry_number + (get_obj_number (obj)/obj->count) > ch->GetMaxItems ())
    {
	ch->SendText ("You have your hands full.\n\r");
	learn_from_failure (ch, gsn_steal);
	return;
    }

    if (ch->GetCarryWeight () + (get_obj_weight (obj)/obj->count) > can_carry_w (ch))
    {
	ch->SendText ("You can't carry that much weight.\n\r");
	learn_from_failure (ch, gsn_steal);
	return;
    }

    separate_obj (obj);
    obj_from_char (obj);
    obj_to_char (obj, ch);
    ch->SendText ("Ok.\n\r");
    learn_from_success (ch, gsn_steal);
    adjust_favor (ch, 9, 1);
    return;
}


void do_backstab (CCharacter *ch, char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CCharacter *victim;
    CObjData *obj;
    int percent;

    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't do that right now.\n\r");
	return;
    }

    one_argument (argument, arg);

    if (ch->mount)
    {
	ch->SendText ("You can't get close enough while mounted.\n\r");
	return;
    }

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

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

    if (victim == ch)
    {
	ch->SendText ("How can you sneak up on yourself?\n\r");
	return;
    }

    if (is_safe (ch, victim))
      return;

    /* Added stabbing weapon. -Narn */
    if ((obj = get_eq_char (ch, WEAR_WIELD)) == NULL
    ||   (obj->value[3] != 11 && obj->value[3] != 2))
    {
	ch->SendText ("You need to wield a piercing or stabbing weapon.\n\r");
	return;
    }

    if (victim->GetFightData ())
    {
	ch->SendText ("You can't backstab someone who is in combat.\n\r");
	return;
    }

    /* Can backstab a char even if it's hurt as long as it's sleeping. -Narn */
    if (victim->GetHp () < victim->GetMaxHp () && victim->IsAwake ())
    {
    act (AT_PLAIN, "$N is hurt and suspicious ... you can't sneak up.",
	    ch, NULL, victim, TO_CHAR);
	return;
    }

    percent = number_percent () - (get_curr_lck (ch) - 14) 
	      + (get_curr_lck (victim) - 13);

    check_attacker (ch, victim);
    WAIT_STATE (ch, SkillTable.GetBeats (gsn_backstab));
    if (!victim->IsAwake ()
    ||   ch->IsNpc ()
    || can_use_skill( ch, percent, gsn_backstab))
    {
	learn_from_success (ch, gsn_backstab);
	global_retcode = multi_hit (ch, victim, gsn_backstab);
	adjust_favor (ch, 10, 1);
        check_illegal_pk (ch, victim);

    }
    else
    {
	learn_from_failure (ch, gsn_backstab);
	global_retcode = damage (ch, victim, 0, gsn_backstab);
    }
    return;
}


void do_rescue (CCharacter *ch, char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CCharacter *victim;
    CCharacter *fch;
    int percent;

    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't concentrate enough for that.\n\r");
	return;
    }

    one_argument (argument, arg);
    if (arg[0] == '\0')
    {
	ch->SendText ("Rescue whom?\n\r");
	return;
    }

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

    if (victim == ch)
    {
	ch->SendText ("How about fleeing instead?\n\r");
	return;
    }

    if (ch->mount)
    {
	ch->SendText ("You can't do that while mounted.\n\r");
	return;
    }

    if (!ch->IsNpc () && victim->IsNpc ())
    {
	ch->SendText ("Doesn't need your help!\n\r");
	return;
    }

    if (!ch->GetFightData ())
    {
	ch->SendText ("Too late...\n\r");
	return;
    }

    if ((fch = victim->GetFightWho ()) == NULL)
    {
	ch->SendText ("They are not fighting right now.\n\r");
	return;
    }

    percent = number_percent () - (get_curr_lck (ch) - 14) 
	      - (get_curr_lck (victim) - 16);

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_rescue));
    if (! can_use_skill( ch, percent, gsn_rescue))
    {
	ch->SendText ("You fail the rescue.\n\r");
	act (AT_SKILL, "$n tries to rescue you!", ch, NULL, victim, TO_VICT  );
	act (AT_SKILL, "$n tries to rescue $N!", ch, NULL, victim, TO_NOTVICT);
	learn_from_failure (ch, gsn_rescue);
	return;
    }

    act (AT_SKILL, "You rescue $N!",  ch, NULL, victim, TO_CHAR   );
    act (AT_SKILL, "$n rescues you!", ch, NULL, victim, TO_VICT   );
    act (AT_SKILL, "$n moves in front of $N!",  ch, NULL, victim, TO_NOTVICT);

    learn_from_success (ch, gsn_rescue);
    adjust_favor (ch, 8, 1);  
    stop_fighting (fch, FALSE);
    stop_fighting (victim, FALSE);
    if (ch->GetFightData ())
      stop_fighting (ch, FALSE);

    /* check_killer (ch, fch); */
    set_fighting (ch, fch);
    set_fighting (fch, ch);
    return;
}



void do_kick (CCharacter *ch, char *argument)
{
    CCharacter *victim;

    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't concentrate enough for that.\n\r");
	return;
    }

    if (!ch->IsNpc ()
    &&   ch->GetLevel () < SkillTable.GetClassLevel (gsn_kick, ch->GetClass ()))
    {
	ch->SendText (
	    "You better leave the martial arts to fighters.\n\r");
	return;
    }

    if ((victim = ch->GetFightWho ()) == NULL)
    {
	ch->SendText ("You aren't fighting anyone.\n\r");
	return;
    }

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_kick));
    if (ch->IsNpc () || number_percent () < ch->GetPcData ()->learned[gsn_kick])
    {
	learn_from_success (ch, gsn_kick);
	global_retcode = damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_kick);
    }
    else
    {
	learn_from_failure (ch, gsn_kick);
	global_retcode = damage (ch, victim, 0, gsn_kick);
    }
    return;
}


void do_punch (CCharacter *ch, char *argument)
{
    CCharacter *victim;

    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't concentrate enough for that.\n\r");
	return;
    }

    if (!ch->IsNpc ()
    &&   ch->GetLevel () < SkillTable.GetClassLevel (gsn_punch, ch->GetClass ()))
    {
	ch->SendText (
	    "You better leave the martial arts to fighters.\n\r");
	return;
    }

    if ((victim = ch->GetFightWho ()) == NULL)
    {
	ch->SendText ("You aren't fighting anyone.\n\r");
	return;
    }

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_punch));
    if (can_use_skill (ch, number_percent (), gsn_kick))
    {
	learn_from_success (ch, gsn_punch);
	global_retcode = damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_punch);
    }
    else
    {
	learn_from_failure (ch, gsn_punch);
	global_retcode = damage (ch, victim, 0, gsn_punch);
    }
    return;
}


void do_bite (CCharacter *ch, char *argument)
{
	CCharacter	*victim;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! ch->IsNpc () && ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_bite, ch->GetClass ())) {
		ch->SendText ("That isn't quite one of your natural skills.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_bite));
	if (can_use_skill (ch, number_percent (), gsn_bite)) {
		learn_from_success (ch, gsn_bite);
		global_retcode =
			damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_bite);
	} else {
		learn_from_failure (ch, gsn_bite);
		global_retcode = damage (ch, victim, 0, gsn_bite);
	}
}


void do_claw (CCharacter *ch, char *argument)
{
	CCharacter *victim;

	if (! ch->IsNpc () && ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_claw, ch->GetClass ())) {
		ch->SendText ("That isn't quite one of your natural skills.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_claw));
	if (can_use_skill (ch, number_percent (), gsn_claw)) {
		learn_from_success (ch, gsn_claw);
		global_retcode =
			damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_claw);
	} else {
		learn_from_failure (ch, gsn_claw);
		global_retcode = damage (ch, victim, 0, gsn_claw);
	}
}


void do_sting (CCharacter *ch, char *argument)
{
	CCharacter	*victim;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! ch->IsNpc () && ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_sting, ch->GetClass ())) {
		ch->SendText ("That isn't quite one of your natural skills.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_sting));
	if (can_use_skill (ch, number_percent (), gsn_sting)) {
		learn_from_success (ch, gsn_sting);
		global_retcode =
			damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_sting);
	} else {
		learn_from_failure (ch, gsn_sting);
		global_retcode = damage (ch, victim, 0, gsn_sting);
	}
}


void do_tail (CCharacter *ch, char *argument)
{
	CCharacter	*victim;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! ch->IsNpc () && ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_tail, ch->GetClass ())) {
		ch->SendText ("That isn't quite one of your natural skills.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_tail));
	if (can_use_skill (ch, number_percent (), gsn_tail)) {
		learn_from_success (ch, gsn_tail);
		global_retcode =
			damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_tail);
	} else {
		learn_from_failure (ch, gsn_tail);
		global_retcode = damage (ch, victim, 0, gsn_tail);
	}
}


void do_bash (CCharacter *ch, char *argument)
{
	CCharacter	*victim;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! ch->IsNpc () && ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_bash, ch->GetClass ())) {
		ch->SendText ("You better leave the martial arts to fighters.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	int	chance = (((victim->GetDexterity () + victim->GetCurrentStrength ())
		- (ch->GetDexterity () + ch->GetCurrentStrength ())) * 10) + 10;

	if (! ch->IsNpc () && !victim->IsNpc ())
		chance += 25;
	if (victim->GetFightData () && victim->GetFightData ()->who != ch)
		chance += 19;

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_bash));
	if (can_use_skill (ch, (number_percent () + chance), gsn_bash)) {
		learn_from_success (ch, gsn_bash);
		// do not change anything here!  -Thoric
		WAIT_STATE (ch,     2 * PULSE_VIOLENCE);
		WAIT_STATE (victim, 2 * PULSE_VIOLENCE);
		victim->SetPosition (POS_SITTING);
		global_retcode =
			damage (ch, victim, number_range (1, ch->GetLevel ()), gsn_bash);
	} else {
		WAIT_STATE (ch,     2 * PULSE_VIOLENCE);
		learn_from_failure (ch, gsn_bash);
		global_retcode = damage (ch, victim, 0, gsn_bash);
	}
}


void do_stun (CCharacter *ch, char *argument)
{
	CCharacter	*victim;
	CAffectData	af;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! ch->IsNpc () && ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_stun, ch->GetClass ())) {
		ch->SendText ("You better leave the martial arts to fighters.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	if (ch->GetMove () < 16) {
		set_char_color (AT_SKILL, ch);
		ch->SendText ("You are far too tired to do that.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_stun));
	BOOL	fail = FALSE;
	int		chance = ris_save (victim, ch->GetLevel (), RIS_PARALYSIS);

	if (chance == 1000)
		fail = TRUE;
	else
		fail = saves_para_petri (chance, victim);

	chance = (((victim->GetDexterity () + victim->GetCurrentStrength ())
		- (ch->GetDexterity () + ch->GetCurrentStrength ())) * 10) + 10;

	// harder for player to stun another player
	if (!ch->IsNpc () && !victim->IsNpc ())
		chance += SysData.StunPlayerLev;
	else
		chance += SysData.StunRegularLev;

	if (!fail && can_use_skill (ch, (number_percent () + chance), gsn_stun)) {
		learn_from_success (ch, gsn_stun);
		//    DO *NOT* CHANGE!    -Thoric
		ch->AddMove (-15);
		WAIT_STATE (ch, 2 * PULSE_VIOLENCE);
		WAIT_STATE (victim, PULSE_VIOLENCE);
		act (AT_SKILL, "$N smashes into you, leaving you stunned!",
			victim, NULL, ch, TO_CHAR);
		act (AT_SKILL, "You smash into $N, leaving $M stunned!",
			ch, NULL, victim, TO_CHAR);
		act (AT_SKILL, "$n smashes into $N, leaving $M stunned!",
			ch, NULL, victim, TO_NOTVICT);
		if (! victim->IsParalysed ()) {
			af.type      = gsn_stun;
			af.location  = APPLY_AC;
			af.modifier  = 20;
			af.duration  = 3;
			af.bitvector = AFF_PARALYSIS;
			affect_to_char (victim, &af);
			update_pos (victim);
		}
	} else {
		WAIT_STATE (ch, 2 * PULSE_VIOLENCE);
		ch->AddMove (-5);
		learn_from_failure (ch, gsn_stun);
		act (AT_SKILL, "$N charges at you screaming, but you dodge out "
			"of the way.", victim, NULL, ch, TO_CHAR);
		act (AT_SKILL, "You try to stun $N, but $E dodges out of the way.",
			ch, NULL, victim, TO_CHAR);
		act (AT_SKILL, "$n charges screaming at $N, but keeps going right "
			"on past.", ch, NULL, victim, TO_NOTVICT);
	}
}


void do_bloodlet (CCharacter *ch, char *argument)
{
	CObjData  *obj;

	if (ch->IsNpc () || ! ch->IsVampire ())
		return;

	if (ch->GetFightWho ()) {
		ch->SendText ("You're too busy fighting...\n\r");
		return;
	}
	if (ch->GetPcData ()->condition [COND_BLOODTHIRST] < 10) {
		ch->SendText ("You are too drained to offer any blood...\n\r");
		return;
	}

	WAIT_STATE (ch, PULSE_VIOLENCE);
	if (can_use_skill (ch, number_percent (), gsn_bloodlet)) {
		gain_condition (ch, COND_BLOODTHIRST, -7);
		act (AT_BLOOD, "Tracing a sharp nail over your skin, you let your "
			"blood spill.", ch, NULL, NULL, TO_CHAR);
		act (AT_BLOOD, "$n traces a sharp nail over $s skin, spilling a "
			"quantity of blood to the ground.", ch, NULL, NULL, TO_ROOM);
		learn_from_success (ch, gsn_bloodlet);

		obj = create_object (OIdxTable.GetObj (OBJ_VNUM_BLOODLET), 0);
		obj->timer = 1;
		obj->value [1] = 6;
		obj_to_room (obj, ch->GetInRoom ());
	} else {
		act (AT_BLOOD, "You cannot manage to draw much blood...",
			ch, NULL, NULL, TO_CHAR);
		act (AT_BLOOD, "$n slices open $s skin, but no blood is spilled...",
			ch, NULL, NULL, TO_ROOM);
		learn_from_failure (ch, gsn_bloodlet);
	}
}


void do_feed (CCharacter *ch, char *argument)
{
	CCharacter *victim;
	short dam;

	if (ch->IsNpc () && ch->IsCharmed ())
	{
	  ch->SendText ("You can't concentrate enough for that.\n\r");
	  return;
	}

	if (!ch->IsNpc ()
	&&   !ch->IsVampire ())
	{
	  ch->SendText ("It is not of your nature to feed on living creatures.\n\r");
	  return;
	}
	if (! can_use_skill (ch, 0, gsn_feed))
	{
	  ch->SendText ("You have not yet practiced your new teeth.\n\r");
	  return;
	}

	if ((victim = ch->GetFightWho ()) == NULL)
	{
	  ch->SendText ("You aren't fighting anyone.\n\r");
	  return;
	}

	if (ch->mount)
	{
	  ch->SendText ("You can't do that while mounted.\n\r");
	  return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_feed));
	if (can_use_skill (ch, number_percent (), gsn_feed))
	{
	  dam = number_range (1, ch->GetLevel ());
	  global_retcode = damage (ch, victim, dam, gsn_feed);
	  if (global_retcode == rNONE && !ch->IsNpc () && dam
	  &&  ch->GetFightData ()
	  &&  ch->GetPcData ()->condition[COND_BLOODTHIRST] < (10 + ch->GetLevel ()))
	  {
	    gain_condition (ch, COND_BLOODTHIRST,
	     UMIN (number_range (1, (ch->GetLevel ()+victim->GetLevel () / 20) + 3),
	     (10 + ch->GetLevel ()) - ch->GetPcData ()->condition[COND_BLOODTHIRST]));
	    gain_condition (ch, COND_FULL, 2);
	    gain_condition (ch, COND_THIRST, 2);
	    act (AT_BLOOD, "You manage to suck a little life out of $N.", ch, NULL,
		 victim, TO_CHAR);
	    act (AT_BLOOD, "$n sucks some of your blood!", ch, NULL, victim, TO_VICT);
	    learn_from_success (ch, gsn_feed);
	  }
	}
	else
	{
	  global_retcode = damage (ch, victim, 0, gsn_feed);
	  if (global_retcode == rNONE && !ch->IsNpc ()
	  &&  ch->GetFightData ()
	  &&  ch->GetPcData ()->condition[COND_BLOODTHIRST] < (10 + ch->GetLevel ()))
	  {
	    act (AT_BLOOD, "The smell of $N's blood is driving you insane!",
		ch, NULL, victim, TO_CHAR);
	    act (AT_BLOOD, "$n is lusting after your blood!", ch, NULL, victim, TO_VICT);
	    learn_from_failure (ch, gsn_feed);
	  }
	}
	return;
}


// Disarm a creature.
// Caller must check for successful attack.
// Check for loyalty flag (weapon disarms to inventory) for pkillers -Blodkai
void disarm (CCharacter *ch, CCharacter *victim)
{
	CObjData	*obj, *tmpobj;

	if ((obj = get_eq_char (victim, WEAR_WIELD)) == NULL)
		return;

	if ((tmpobj = get_eq_char (victim, WEAR_DUAL_WIELD))
		&& number_bits (1) == 0)
			obj = tmpobj;

	if (get_eq_char (ch, WEAR_WIELD) == NULL && number_bits (1) == 0) {
		learn_from_failure (ch, gsn_disarm);
		return;
	}

	if (ch->IsNpc () && !can_see_obj (ch, *obj) && number_bits (1) == 0) {
		learn_from_failure (ch, gsn_disarm);
		return;
	}

	if (check_grip (ch, victim)) {
		learn_from_failure (ch, gsn_disarm);
		return;
	}

	act (AT_SKILL, "$n DISARMS you!", ch, NULL, victim, TO_VICT);
	act (AT_SKILL, "You disarm $N!",  ch, NULL, victim, TO_CHAR);
	act (AT_SKILL, "$n disarms $N!",  ch, NULL, victim, TO_NOTVICT);
	learn_from_success (ch, gsn_disarm);

	if (obj == get_eq_char (victim, WEAR_WIELD)
		&& (tmpobj = get_eq_char (victim, WEAR_DUAL_WIELD)) != NULL)
			tmpobj->wear_loc = WEAR_WIELD;

	obj_from_char (obj);
	if (victim->IsNpc () || (obj->IsLoyal () && victim->IsPkiller ()))
		obj_to_char (obj, victim);
	else
		obj_to_room (obj, victim->GetInRoom ());
}


void do_disarm (CCharacter *ch, char *argument)
{
	CCharacter	*victim;
	CObjData	*obj;
	int			percent;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	if (! ch->IsNpc () && ch->GetLevel ()
	  < SkillTable.GetClassLevel (gsn_disarm, ch->GetClass ())) {
		ch->SendText ("You don't know how to disarm opponents.\n\r");
		return;
	}

	if (get_eq_char (ch, WEAR_WIELD) == NULL) {
		ch->SendText ("You must wield a weapon to disarm.\n\r");
		return;
	}

	if ((victim = ch->GetFightWho ()) == NULL) {
		ch->SendText ("You aren't fighting anyone.\n\r");
		return;
	}

	if ((obj = get_eq_char (victim, WEAR_WIELD)) == NULL) {
		ch->SendText ("Your opponent is not wielding a weapon.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_disarm));
	percent = number_percent () + victim->GetLevel () - ch->GetLevel ()
		- (get_curr_lck (ch) - 15) + (get_curr_lck (victim) - 15);

	if (! can_see_obj (ch, *obj))
		percent += 10;
	if (can_use_skill (ch, (percent*3/2), gsn_disarm))
		disarm (ch, victim);
	else {
		ch->SendText ("You failed.\n\r");
		learn_from_failure (ch, gsn_disarm);
	}
}


// Trip a creature.
// Caller must check for successful attack.
void trip (CCharacter *ch, CCharacter *victim)
{
    if (victim->IsFlying ()
    ||   victim->IsFloating ())
      return;
    if (victim->mount)
    {
	if (victim->mount->IsFlying ()
	||   victim->mount->IsFloating ())
	  return;
	act (AT_SKILL, "$n trips your mount and you fall off!", ch, NULL, victim, TO_VICT   );
	act (AT_SKILL, "You trip $N's mount and $N falls off!", ch, NULL, victim, TO_CHAR   );
	act (AT_SKILL, "$n trips $N's mount and $N falls off!", ch, NULL, victim, TO_NOTVICT);
	victim->mount->ClrActBit (ACT_MOUNTED);
	victim->mount = NULL;
	WAIT_STATE (ch,     2 * PULSE_VIOLENCE);
	WAIT_STATE (victim, 2 * PULSE_VIOLENCE);
	victim->SetPosition (POS_RESTING);
	return;		
    }
    if (victim->GetWait () == 0)
    {
	act (AT_SKILL, "$n trips you and you go down!", ch, NULL, victim, TO_VICT   );
	act (AT_SKILL, "You trip $N and $N goes down!", ch, NULL, victim, TO_CHAR   );
	act (AT_SKILL, "$n trips $N and $N goes down!", ch, NULL, victim, TO_NOTVICT);

	WAIT_STATE (ch,     2 * PULSE_VIOLENCE);
	WAIT_STATE (victim, 2 * PULSE_VIOLENCE);
	victim->SetPosition (POS_RESTING);
    }

    return;
}


// Converted to function well as a skill for vampires -- Blodkai
void do_mistwalk (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CCharacter	*victim;
	bool		allowday;

	set_char_color (AT_DGREEN, ch);
	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't do that right now.\n\r");
		return;
	}
	if (ch->mount) {
		ch->SendText ("And scare your mount to death?\n\r");
		return;
	}
	one_argument (argument, arg);
	if (arg [0] == '\0') {
		ch->SendText ("Who will be your victim?\n\r");
		return;
	}

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_mistwalk));
	if ((victim = get_char_world (ch, arg)) == NULL || victim == ch) {
		ch->SendText ("You are unable to sense your victim.\n\r");
		return;
	}

	allowday =
		(ch->IsPkiller () &&
			ch->GetPcData ()->condition [COND_BLOODTHIRST] > 22);

	CRoomIndexData	*pRoom = victim->GetInRoom ();
	if ((time_info.hour < 21 && time_info.hour > 5 && !allowday)
	  ||   ! pRoom
	  ||   pRoom->IsPrivate ()
	  ||   pRoom->IsSolitary ()
	  ||   pRoom->IsNoAstral ()
	  ||   pRoom->IsDeathRoom ()
	  ||   pRoom->IsPrototype ()
	  ||   pRoom->IsNoRecall ()
	  ||   victim->GetLevel () >= ch->GetLevel () + 15
	  ||  (victim->IsNpc () && victim->IsPrototype ())
	  ||  (victim->IsNpc () && saves_spell_staff (ch->GetLevel (), victim))
	  ||  (victim->CanPkill () && ! ch->IsNpc () && ! ch->CanPkill ())
	  ||  ! in_hard_range (ch, pRoom->GetArea ())
	  ||  (pRoom->GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
		ch->SendText ("You are unable to sense your victim.\n\r");
		learn_from_failure (ch, gsn_mistwalk);
		return;
	}

	// Subtract 22 extra bp for mist walk from 0500 to 2100 SB
	if (time_info.hour < 21 && time_info.hour > 5 && ! ch->IsNpc ())
		gain_condition (ch, COND_BLOODTHIRST, - 22);

	act (AT_DGREEN, "Your surroundings blur as you assume a form of "
		"churning mist!", ch, NULL, NULL, TO_CHAR);
	act (AT_DGREEN, "$n dissolves into a cloud of glowing mist, then "
		"vanishes!", ch, NULL, NULL, TO_ROOM);
	learn_from_success (ch, gsn_mistwalk);
	ch->RemoveFromRoom ();
	ch->SendToRoom (pRoom);
	act (AT_DGREEN, "A cloud of glowing mist engulfs you, then withdraws "
		"to unveil $n!", ch, NULL, NULL, TO_ROOM);
	do_look (ch, "auto");
}


void do_broach (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CExitData	*pexit;

	set_char_color (AT_DGREEN, ch);

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}
	one_argument (argument, arg);
	if (arg [0] == '\0') {
		ch->SendText ("Attempt this in which direction?\n\r");
		return;
	}
	if (ch->mount) {
		ch->SendText ("You should really dismount first.\n\r");
		return;
	}

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_broach));
	if (pexit = find_door (ch, arg, TRUE)) {
		CExitData	*pexit_rev;
		if (! pexit->IsClosed () || ! pexit->IsLocked ()
		  || pexit->IsPickProof ()
		  || can_use_skill (ch, number_percent (), gsn_broach)) {
			ch->SendText ("Your attempt fails.\n\r");
			learn_from_failure (ch, gsn_broach);
			check_room_for_traps (ch, TRAP_PICK | trap_door[pexit->vdir]);
			return;
		}
		pexit->ClrLocked ();
		ch->SendText ("You successfully broach the exit...\n\r");
		learn_from_success (ch, gsn_broach);
		adjust_favor (ch, 9, 1);

		if ((pexit_rev = pexit->rexit) != NULL
			&& pexit_rev->GetToRoom () == ch->GetInRoom ())
				pexit_rev->ClrLocked ();

		check_room_for_traps (ch, TRAP_PICK | trap_door [pexit->vdir]);
		return;
	}
	ch->SendText ("Your attempt fails.\n\r");
}


void do_pick (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CCharacter	*gch;
	CObjData	*obj;
	CExitData	*pexit;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	one_argument (argument, arg);

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

	if (ms_find_obj (ch))
		return;

	if (ch->mount) {
		ch->SendText ("You can't do that while mounted.\n\r");
		return;
	}

	WAIT_STATE (ch, SkillTable.GetBeats (gsn_pick_lock));

	// look for guards
	gch = ch->GetInRoom ()->first_person;
	for ( ; gch; gch = gch->GetNextInRoom ()) {
		if (gch->IsNpc ()
		  && gch->IsAwake () && ch->GetLevel () + 5 < gch->GetLevel ()) {
			act (AT_PLAIN, "$N is standing too close to the lock.",
				ch, NULL, gch, TO_CHAR);
			return;
		}
	}

	if (! can_use_skill (ch, number_percent (), gsn_pick_lock)) {
		ch->SendText ("You failed.\n\r");
		learn_from_failure (ch, gsn_pick_lock);
		return;
	}

	if ((pexit = find_door (ch, arg, TRUE)) != NULL) {
		// 'pick door'
		CExitData	*pexit_rev;

		if (! pexit->IsClosed ()) {
			ch->SendText ("It's not closed.\n\r");
			return;
		}
		if (pexit->key < 0) {
			ch->SendText ("It can't be picked.\n\r");
			return;
		}
		if (! pexit->IsLocked ()) {
			ch->SendText ("It's already unlocked.\n\r");
			return;
		}
		if (pexit->IsPickProof ()) {
			ch->SendText ("You failed.\n\r");
			learn_from_failure (ch, gsn_pick_lock);
			check_room_for_traps (ch, TRAP_PICK | trap_door[pexit->vdir]);
			return;
		}

		pexit->ClrLocked ();
		ch->SendText ("*Click*\n\r");
		act (AT_ACTION, "$n picks the $d.", ch, NULL, pexit->keyword, TO_ROOM);
		learn_from_success (ch, gsn_pick_lock);
		adjust_favor (ch, 9, 1);

		// pick the other side
		if ((pexit_rev = pexit->rexit) != NULL
		  && pexit_rev->GetToRoom () == ch->GetInRoom ()) {
			pexit_rev->ClrLocked ();
		}
		check_room_for_traps (ch, TRAP_PICK | trap_door[pexit->vdir]);
		return;
	}

	if ((obj = get_obj_here (ch, arg)) != NULL) {
		// 'pick object'
		if (obj->item_type != ITEM_CONTAINER) {
			ch->SendText ("That's not a container.\n\r");
			return;
		}
		if (! IS_SET (obj->value[1], CONT_CLOSED)) {
			ch->SendText ("It's not closed.\n\r");
			return;
		}
		if (obj->value [2] < 0) {
			ch->SendText ("It can't be unlocked.\n\r");
			return;
		}
		if (! IS_SET (obj->value [1], CONT_LOCKED)) {
			ch->SendText ("It's already unlocked.\n\r");
			return;
		}
		if (IS_SET (obj->value [1], CONT_PICKPROOF)) {
			ch->SendText ("You failed.\n\r");
			learn_from_failure (ch, gsn_pick_lock);
			check_for_trap (ch, obj, TRAP_PICK);
			return;
		}

		separate_obj (obj);
		REMOVE_BIT (obj->value [1], CONT_LOCKED);
		ch->SendText ("*Click*\n\r");
		act (AT_ACTION, "$n picks $p.", ch, obj, NULL, TO_ROOM);
		learn_from_success (ch, gsn_pick_lock);
		adjust_favor (ch, 9, 1);
		check_for_trap (ch, obj, TRAP_PICK);
		return;
	}

	ch->SendTextf ("You see no %s here.\n\r", arg);
}



void do_sneak (CCharacter *ch, char *argument)
{
    CAffectData af;

    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't concentrate enough for that.\n\r");
	return;
    }

    if (ch->mount)
    {
	ch->SendText ("You can't do that while mounted.\n\r");
	return;
    }

    ch->SendText ("You attempt to move silently.\n\r");
    affect_strip (ch, gsn_sneak);

    if (can_use_skill (ch, number_percent (), gsn_sneak))
    {
	af.type      = gsn_sneak;
	af.duration  = (short) (ch->GetLevel () * DUR_CONV);
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bitvector = AFF_SNEAK;
	affect_to_char (ch, &af);
	learn_from_success (ch, gsn_sneak);
    }
    else
	learn_from_failure (ch, gsn_sneak);

    return;
}



void do_hide (CCharacter *ch, char *argument)
{
    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't concentrate enough for that.\n\r");
	return;
    }

    if (ch->mount)
    {
	ch->SendText ("You can't do that while mounted.\n\r");
	return;
    }

    ch->SendText ("You attempt to hide.\n\r");

    if (ch->IsHidden ())
	ch->ClrHide ();

    if (can_use_skill (ch, number_percent (), gsn_hide))
    {
	ch->SetHide ();
	learn_from_success (ch, gsn_hide);
    }
    else
	learn_from_failure (ch, gsn_hide);
    return;
}



// Contributed by Alander.
void do_visible (CCharacter *ch, char *argument)
{
	affect_strip (ch, gsn_invis);
	affect_strip (ch, gsn_mass_invis);
	affect_strip (ch, gsn_sneak);
	ch->ClrHide ();
	ch->ClrInvis ();
	if (! RaceTable.IsAffected (ch->GetRace (), AFF_SNEAK))
		ch->ClrSneak ();
	ch->SendText ("Ok.\n\r");
}


void do_recall (CCharacter *ch, char *argument)
{
	CRoomIndexData	*location;
	CCharacter		*opponent;

	location = NULL;

	if (!ch->IsNpc () && ch->GetPcData ()->GetClan ())
		location = RoomTable.GetRoom (ch->GetPcData ()->GetClan ()->recall);

	if (! location)		// 1998-01-02, h
		location =
			RoomTable.GetRoom (RaceTable.GetRaceRecall (ch->GetRace ()));

	if (! location)
		location = RoomTable.GetRoom (SysData.m_RoomTemple); 

	if (! location) {
		ch->SendText ("You are completely lost.\n\r");
		return;
	}

	if (ch->GetInRoom () == location)
		return;

	if (ch->GetInRoom ()->IsNoRecall ()) {
		ch->SendText ("For some strange reason... nothing happens.\n\r");
		return;
	}

	if (ch->IsCursed ()) {
		ch->SendText ("You are cursed and cannot recall!\n\r");
		return;
	}

	if (opponent = ch->GetFightWho ()) {
		int		lose;

		if (number_bits (1) == 0
		  || (!opponent->IsNpc () && number_bits (3) > 1)) {
			WAIT_STATE (ch, 4);
			lose = (int) ((exp_level (ch, ch->GetLevel ()+1) -
				exp_level (ch, ch->GetLevel ())) * 0.1);
			if (ch->GetDesc ())
				lose /= 2;
			gain_exp (ch, 0 - lose);
			ch->SendTextf ("You failed!  You lose %d exps.\n\r", lose);
			return;
		}

		lose = (int) ((exp_level (ch, ch->GetLevel ()+1) -
			exp_level (ch, ch->GetLevel ())) * 0.2);
		if (ch->GetDesc ())
			lose /= 2;
		gain_exp (ch, 0 - lose);
		ch->SendTextf ("You recall from combat!  You lose %d exps.\n\r",lose);
		stop_fighting (ch, TRUE);
	}

	act (AT_ACTION, "$n disappears in a swirl of smoke.", ch, NULL, NULL,
		TO_ROOM);
	ch->RemoveFromRoom ();
	ch->SendToRoom (location);

	if (ch->mount) {
		ch->mount->RemoveFromRoom ();
		ch->mount->SendToRoom (location);
	}
	act (AT_ACTION, "$n appears in the room.", ch, NULL, NULL, TO_ROOM);
	do_look (ch, "auto");
}


void do_aid (CCharacter *ch, char *argument)
{
    char arg[MAX_INPUT_LENGTH];
    CCharacter *victim;
    int percent;

    if (ch->IsNpc () && ch->IsCharmed ())
    {
	ch->SendText ("You can't concentrate enough for that.\n\r");
	return;
    }

    one_argument (argument, arg);
    if (arg[0] == '\0')
    {
	ch->SendText ("Aid whom?\n\r");
	return;
    }

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

    if (ch->mount)
    {
	ch->SendText ("You can't do that while mounted.\n\r");
	return;
    }

    if (victim == ch)
    {
	ch->SendText ("Aid yourself?\n\r");
	return;
    }

    if (victim->GetPosition () > POS_STUNNED)
    {
	act (AT_PLAIN, "$N doesn't need your help.", ch, NULL, victim,
	     TO_CHAR);
	return;
    }

    if (victim->GetHp () <= -6)
    {
	act (AT_PLAIN, "$N's condition is beyond your aiding ability.", ch,
	     NULL, victim, TO_CHAR);
	return;
    }

    percent = number_percent () - (get_curr_lck (ch) - 13);
    WAIT_STATE (ch, SkillTable.GetBeats (gsn_aid));
    if (! can_use_skill (ch, percent, gsn_aid))
    {
	ch->SendText ("You fail.\n\r");
	learn_from_failure (ch, gsn_aid);
	return;
    }

    act (AT_SKILL, "You aid $N!",  ch, NULL, victim, TO_CHAR   );
    act (AT_SKILL, "$n aids $N!",  ch, NULL, victim, TO_NOTVICT);
    learn_from_success (ch, gsn_aid);
    adjust_favor (ch, 8, 1);
    if (victim->GetHp () < 1)
      victim->SetHp (1);

    update_pos (victim);
    act (AT_SKILL, "$n aids you!", ch, NULL, victim, TO_VICT   );
    return;
}


void do_mount (CCharacter *ch, char *argument)
{
    CCharacter *victim;

    if (!ch->IsNpc ()
    &&   ch->GetLevel () < SkillTable.GetClassLevel (gsn_mount, ch->GetClass ()))
    {
	ch->SendText (
	    "I don't think that would be a good idea...\n\r");
	return;
    }

    if (ch->mount)
    {
	ch->SendText ("You're already mounted!\n\r");
	return;
    }

    if ((victim = get_char_room (ch, argument)) == NULL)
    {
	ch->SendText ("You can't find that here.\n\r");
	return;
    }

    if (!victim->IsNpc () || ! victim->IsAction (ACT_MOUNTABLE))
    {
	ch->SendText ("You can't mount that!\n\r");
	return;
    }

    if (victim->IsAction (ACT_MOUNTED))
    {
	ch->SendText ("That mount already has a rider.\n\r");
	return;
    }

    if (victim->GetPosition () < POS_STANDING)
    {
	ch->SendText ("Your mount must be standing.\n\r");
	return;
    }

    if (victim->GetPosition () == POS_FIGHTING || victim->GetFightData ())
    {
	ch->SendText ("Your mount is moving around too much.\n\r");
	return;
    }

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_mount));
    if (can_use_skill (ch, number_percent (), gsn_mount))
    {
	victim->SetActBit (ACT_MOUNTED);
	ch->mount = victim;
	act (AT_SKILL, "You mount $N.", ch, NULL, victim, TO_CHAR);
	act (AT_SKILL, "$n skillfully mounts $N.", ch, NULL, victim, TO_NOTVICT);
	act (AT_SKILL, "$n mounts you.", ch, NULL, victim, TO_VICT);
	learn_from_success (ch, gsn_mount);
	ch->SetPosition (POS_MOUNTED);
    }
    else
    {
	act (AT_SKILL, "You unsuccessfully try to mount $N.", ch, NULL, victim, TO_CHAR);
	act (AT_SKILL, "$n unsuccessfully attempts to mount $N.", ch, NULL, victim, TO_NOTVICT);
	act (AT_SKILL, "$n tries to mount you.", ch, NULL, victim, TO_VICT);
	learn_from_failure (ch, gsn_mount);
    }
    return;
}


void do_dismount (CCharacter *ch, char *argument)
{
    CCharacter *victim;

    if ((victim = ch->mount) == NULL)
    {
	ch->SendText ("You're not mounted.\n\r");
	return;	
    }

    WAIT_STATE (ch, SkillTable.GetBeats (gsn_mount));
    if (can_use_skill (ch, number_percent (), gsn_mount))
    {
	act (AT_SKILL, "You dismount $N.", ch, NULL, victim, TO_CHAR);
	act (AT_SKILL, "$n skillfully dismounts $N.", ch, NULL, victim, TO_NOTVICT);
	act (AT_SKILL, "$n dismounts you.  Whew!", ch, NULL, victim, TO_VICT);
	victim->ClrActBit (ACT_MOUNTED);
	ch->mount = NULL;
	ch->SetPosition (POS_STANDING);
	learn_from_success (ch, gsn_mount);
    }
    else
    {
	act (AT_SKILL, "You fall off while dismounting $N.  Ouch!", ch, NULL, victim, TO_CHAR);
	act (AT_SKILL, "$n falls off of $N while dismounting.", ch, NULL, victim, TO_NOTVICT);
	act (AT_SKILL, "$n falls off your back.", ch, NULL, victim, TO_VICT);
	learn_from_failure (ch, gsn_mount);
	victim->ClrActBit (ACT_MOUNTED);
	ch->mount = NULL;
	ch->SetPosition (POS_SITTING);
	global_retcode = damage (ch, ch, 1, TYPE_UNDEFINED);
    }
    return;
}


/**************************************************************************/


// Check for parry.
BOOL check_parry (CCharacter *ch, CCharacter *victim)
{
	int		chances;

	if (! victim->IsAwake ())
		return FALSE;

	if (victim->IsNpc () && ! victim->CanParry ())
		return FALSE;

	if (victim->IsNpc ()) {
		// Tuan was here.  :)
		chances	= UMIN (60, 2 * victim->GetLevel ());
	} else {
		if (get_eq_char (victim, WEAR_WIELD) == NULL)
			return FALSE;
		chances	= (int) (victim->GetPcData ()->learned [gsn_parry] / 2);
	}

	// Put in the call to chance () to allow penalties for misaligned
	// clannies.
	if (! chance (victim, chances + victim->GetLevel () - ch->GetLevel ())) {
		learn_from_failure (victim, gsn_parry);
		return FALSE;
	}

	if (! victim->IsNpc () && ! victim->IsGagged ())		// SB
		act (AT_SKILL, "You parry $n's attack.",  ch, NULL, victim, TO_VICT);

	if (! ch->IsNpc () && ! ch->IsGagged ())				// SB
		act (AT_SKILL, "$N parries your attack.", ch, NULL, victim, TO_CHAR);

	learn_from_success (victim, gsn_parry);
	return TRUE;
}



// Check for dodge.
BOOL check_dodge (CCharacter *ch, CCharacter *victim)
{
	int		chances;

	if (! victim->IsAwake ())
		return FALSE;

	if (victim->IsNpc () && ! victim->CanDodge ())
		return FALSE;

	if (victim->IsNpc ())
		chances  = UMIN (60, 2 * victim->GetLevel ());
	else
		chances = (int) (victim->GetPcData ()->learned [gsn_dodge] / 2);

	// Consider luck as a factor
	if (! chance (victim, chances + victim->GetLevel () - ch->GetLevel ())) {
		learn_from_failure (victim, gsn_dodge);
		return FALSE;
	}

	if (! victim->IsNpc () && ! victim->IsGagged ())    
		act (AT_SKILL, "You dodge $n's attack.", ch, NULL, victim, TO_VICT);

	if (! ch->IsNpc () && ! ch->IsGagged ()) 
		act (AT_SKILL, "$N dodges your attack.", ch, NULL, victim, TO_CHAR);

	learn_from_success (victim, gsn_dodge);
	return TRUE;
}


void do_poison_weapon (CCharacter *ch, char *argument)
{
  CObjData *obj;
  CObjData *pobj;
  CObjData *wobj;
  char      arg [ MAX_INPUT_LENGTH ];
  int       percent;

  if (!ch->IsNpc ()
  && ch->GetLevel () < SkillTable.GetClassLevel (gsn_poison_weapon, ch->GetClass ()))
    {
    ch->SendText ("What do you think you are, a thief?\n\r");
    return;
    }

  one_argument (argument, arg);

    if (arg[0] == '\0')
    {
	ch->SendText ("What are you trying to poison?\n\r");
	return;
    }
    if (ch->GetFightData ())
    {
	ch->SendText ("While you're fighting?  Nice try.\n\r");
	return;
    }
    if (ms_find_obj (ch))
	return;

    if (! (obj = get_obj_carry (ch, arg)))
    {
	ch->SendText ("You do not have that weapon.\n\r");
	return;
    }
    if (obj->item_type != ITEM_WEAPON)
    {
	ch->SendText ("That item is not a weapon.\n\r");
	return;
    }
    if (obj->IsPoisoned ())
    {
	ch->SendText ("That weapon is already poisoned.\n\r");
	return;
    }

	// Now we have a valid weapon...check to see if we have the powder.
	POSITION	Cpos = ch->GetHeadCarryPos ();
	while (pobj = ch->GetNextCarrying (Cpos)) {
		if (pobj->pIndexData->vnum == OBJ_VNUM_BLACK_POWDER)
			break;
	}

	if (! pobj) {
		ch->SendText ("You do not have the black poison powder.\n\r");
		return;
    }

	// Okay, we have the powder...do we have water?
	Cpos = ch->GetHeadCarryPos ();
	while (wobj = ch->GetNextCarrying (Cpos)) {
		if (wobj->item_type == ITEM_DRINK_CON
			&& wobj->value [1] > 0 && wobj->value [2] == 0)
				break;
	}

	if (! wobj) {
		ch->SendText ("You have no water to mix with the powder.\n\r");
		return;
    }

	/* Great, we have the ingredients...but is the thief smart enough? */
    if (!ch->IsNpc () && ch->GetWisdom () < 16)
    {
	ch->SendText ("You can't quite remember what to do...\n\r");
	return;
    }
    /* And does the thief have steady enough hands? */
    if (!ch->IsNpc ()
    && ((ch->GetDexterity () < 17) || ch->GetPcData ()->condition[COND_DRUNK] > 0))
    {
	ch->SendText ("Your hands aren't steady enough to properly mix the poison.\n\r");
	return;
    }
    WAIT_STATE (ch, SkillTable.GetBeats (gsn_poison_weapon));
  
    percent = (number_percent () - get_curr_lck (ch) - 14);

    /* Check the skill percentage */
    separate_obj (pobj);
    separate_obj (wobj);
    if (! can_use_skill (ch, percent, gsn_poison_weapon))
    {
	set_char_color (AT_RED, ch);
	ch->SendText ("You failed and spill some on yourself.  Ouch!\n\r");
	set_char_color (AT_GREY, ch);
	damage (ch, ch, ch->GetLevel (), gsn_poison_weapon);
	act (AT_RED, "$n spills the poison all over!", ch, NULL, NULL, TO_ROOM);
	extract_obj (pobj);
	extract_obj (wobj);
	learn_from_failure (ch, gsn_poison_weapon);
	return;
    }
    separate_obj (obj);
    /* Well, I'm tired of waiting.  Are you? */
    act (AT_RED, "You mix $p in $P, creating a deadly poison!", ch, pobj, wobj, TO_CHAR);
    act (AT_RED, "$n mixes $p in $P, creating a deadly poison!",ch, pobj, wobj, TO_ROOM);
    act (AT_GREEN, "You pour the poison over $p, which glistens wickedly!",ch, obj, NULL, TO_CHAR );
    act (AT_GREEN, "$n pours the poison over $p, which glistens wickedly!",ch, obj, NULL, TO_ROOM );
    obj->SetPoisoned ();
    obj->cost *= ch->GetLevel ();
    /* Set an object timer.  Don't want proliferation of poisoned weapons */
    obj->timer = 10 + ch->GetLevel ();

    if (obj->IsBlessed ())
    obj->timer *= 2;

    if (obj->IsMagic ())
    obj->timer *= 2;

    /* WHAT?  All of that, just for that one bit?  How lame. ;) */
   act (AT_BLUE, "The remainder of the poison eats through $p.", ch, wobj, NULL, TO_CHAR);
   act (AT_BLUE, "The remainder of the poison eats through $p.", ch, wobj, NULL, TO_ROOM);
   extract_obj (pobj);
   extract_obj (wobj);
   learn_from_success (ch, gsn_poison_weapon);
   return;
}


void do_scribe (CCharacter *ch, char *argument)
{
    CObjData *scroll;
    int sn;
    char buf1[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char buf3[MAX_STRING_LENGTH];
    int mana;

    if (ch->IsNpc ())
        return;

    if (!ch->IsNpc ()
    &&   ch->GetLevel () < SkillTable.GetClassLevel (gsn_scribe, ch->GetClass ()))
    {
	ch->SendText ("A skill such as this requires more magical ability than that of your class.\n\r");
	return;
    }

    if (argument[0] == '\0' || !str_cmp (argument, ""))
    {
	ch->SendText ("Scribe what?\n\r");
	return;
    }

    if (ms_find_obj (ch))
	return;

    if ((sn = find_spell (ch, argument, TRUE)) < 0)
    {
         ch->SendText ("You have not learned that spell.\n\r");
         return;
    }

    if (SkillTable.GetSpellFunction (sn) == spell_null)
    {
        ch->SendText ("That's not a spell!\n\r");
        return;
    }

    if (SkillTable.GetSkill (sn)->IsNoScribe ()) {
        ch->SendText ("You cannot scribe that spell.\n\r");
        return;
    }

    mana = ch->IsNpc () ? 0 : UMAX (SkillTable.GetMinMana (sn),
     100 / (2 + ch->GetLevel () - SkillTable.GetClassLevel (sn, ch->GetClass ())));

    mana *=5;

    if (!ch->IsNpc () && ch->GetMana () < mana)
    {
        ch->SendText ("You don't have enough mana.\n\r");
        return;
    }

     if ((scroll = get_eq_char (ch, WEAR_HOLD)) == NULL)
     {
	ch->SendText ("You must be holding a blank scroll to scribe it.\n\r");
	return;
     }

     if (scroll->pIndexData->vnum != OBJ_VNUM_SCROLL_SCRIBING)
     {
	ch->SendText ("You must be holding a blank scroll to scribe it.\n\r");
	return;
     }
    
     if ((scroll->value[1] != -1)
     && (scroll->pIndexData->vnum == OBJ_VNUM_SCROLL_SCRIBING))
     {
	ch->SendText ("That scroll has already been inscribed.\n\r");
	return;
     }

     if (!process_spell_components (ch, sn))
     {
	learn_from_failure (ch, gsn_scribe);
	ch->AddMana (-mana / 2);
	return;
     }

     if (! can_use_skill (ch, number_percent (), gsn_scribe))
     {
       set_char_color (AT_MAGIC, ch);
       ch->SendText ("You failed.\n\r");
       learn_from_failure (ch, gsn_scribe);
       ch->AddMana (-mana / 2);
       return;
     }
     
     scroll->value[1] = sn;
     scroll->value[0] = ch->GetLevel ();
     sprintf (buf1, "%s scroll", SkillTable.GetName (sn));
     scroll->SetShortDescr (aoran (buf1));

     sprintf (buf2, "A glowing scroll inscribed '%s' lies in the dust.", 
                                              SkillTable.GetName (sn));

     scroll->SetDescription (buf2);

     sprintf (buf3, "scroll scribing %s", SkillTable.GetName (sn));
     scroll->SetName (buf3);

     act (AT_MAGIC, "$n magically scribes $p.",   ch,scroll, NULL, TO_ROOM);
     act (AT_MAGIC, "You magically scribe $p.",   ch,scroll, NULL, TO_CHAR);
    
     learn_from_success (ch, gsn_scribe);
    
     ch->AddMana (-mana);
     
}


void do_brew (CCharacter *ch, char *argument)
{
    CObjData *potion;
    CObjData *fire;
    int sn;
    char buf1[MAX_STRING_LENGTH];
    char buf2[MAX_STRING_LENGTH];
    char buf3[MAX_STRING_LENGTH];
    int mana;
    BOOL found;

    if (ch->IsNpc ())
        return;

    if (!ch->IsNpc ()
    &&   ch->GetLevel () < SkillTable.GetClassLevel (gsn_brew, ch->GetClass ()))
    {
        ch->SendText ("A skill such as this requires more magical ability than that of your class.\n\r");
	return;
    }

    if (argument[0] == '\0' || !str_cmp (argument, ""))
    {
	ch->SendText ("Brew what?\n\r");
	return;
    }

    if (ms_find_obj (ch))
	return;

    if ((sn = find_spell (ch, argument, TRUE)) < 0)
    {
         ch->SendText ("You have not learned that spell.\n\r");
         return;
    }

    if (SkillTable.GetSpellFunction (sn) == spell_null)
    {
        ch->SendText ("That's not a spell!\n\r");
        return;
    }

    if (SkillTable.GetSkill (sn)->IsNoBrew ()) {
        ch->SendText ("You cannot brew that spell.\n\r");
        return;
    }

    mana = ch->IsNpc () ? 0 : UMAX (SkillTable.GetMinMana (sn),
     100 / (2 + ch->GetLevel () - SkillTable.GetClassLevel (sn, ch->GetClass ())));

    mana *=4;

    if (!ch->IsNpc () && ch->GetMana () < mana)
    {
        ch->SendText ("You don't have enough mana.\n\r");
        return;
    }
  
    found = FALSE;

	POSITION	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (fire = ch->GetInRoom ()->GetNextContent (pos)) {
		if (fire->item_type == ITEM_FIRE) {
			found = TRUE;
			break;
		}
	}

	if (! found) {
        ch->SendText (
        "There must be a fire in the room to brew a potion.\n\r");
        return;
     }

     if ((potion = get_eq_char (ch, WEAR_HOLD)) == NULL)
     {
        ch->SendText (
        "You must be holding an empty flask to brew a potion.\n\r");
        return;
     }

     if (potion->pIndexData->vnum != OBJ_VNUM_FLASK_BREWING)
     {
	ch->SendText ("You must be holding an empty flask to brew a potion.\n\r");
	return;
     }

     if ((potion->value[1] != -1)
     && (potion->pIndexData->vnum == OBJ_VNUM_FLASK_BREWING))
     {
	ch->SendText ("That's not an empty flask.\n\r");
	return;
     }

     if (!process_spell_components (ch, sn))
     {
	learn_from_failure (ch, gsn_brew);
	ch->AddMana (-mana / 2);
	return;
     }

     if (! can_use_skill (ch, number_percent (), gsn_brew))
     {
       set_char_color (AT_MAGIC, ch);
       ch->SendText ("You failed.\n\r");
       learn_from_failure (ch, gsn_brew);
       ch->AddMana (-mana / 2);
       return;
     }

     potion->value[1] = sn;
     potion->value[0] = ch->GetLevel ();
     sprintf (buf1, "%s potion", SkillTable.GetName (sn));
     potion->SetShortDescr (aoran (buf1));

     sprintf (buf2, "A strange potion labelled '%s' sizzles in a glass flask.",
                                              SkillTable.GetName (sn));

     potion->SetDescription (buf2);

     sprintf (buf3, "flask potion %s", SkillTable.GetName (sn));
     potion->SetName (buf3);

     act (AT_MAGIC, "$n brews up $p.",   ch,potion, NULL, TO_ROOM);
     act (AT_MAGIC, "You brew up $p.",   ch,potion, NULL, TO_CHAR);

     learn_from_success (ch, gsn_brew);
    
     ch->AddMana (-mana);
     
}


BOOL check_grip (CCharacter *ch, CCharacter *victim)
{
	int		chance;

	if (! victim->IsAwake ())
		return FALSE;

	if (victim->IsNpc () && ! victim->CanGrip ())
		return FALSE;

	if (victim->IsNpc ())
		chance = UMIN (60, 2 * victim->GetLevel ());
	else
		chance = victim->GetLearnedPercent (gsn_grip) / 2;

	// Consider luck as a factor
	chance += (2 * (get_curr_lck (victim) - 13));

	if (number_percent () >=
	  chance + victim->GetLevel () - ch->GetLevel ()) {
		learn_from_failure (victim, gsn_grip);
		return FALSE;
	}

	act (AT_SKILL, "You evade $n's attempt to disarm you.",
		ch, NULL, victim, TO_VICT   );
	act (AT_SKILL, "$N holds $S weapon strongly, and is not disarmed.", 
		ch, NULL, victim, TO_CHAR);
	learn_from_success (victim, gsn_grip);
	return TRUE;
}


void do_circle (CCharacter *ch, char *argument)
{
	char		arg [MAX_INPUT_LENGTH];
	CCharacter	*victim;
	CObjData	*obj;
	int			percent;

	if (ch->IsNpc () && ch->IsCharmed ()) {
		ch->SendText ("You can't concentrate enough for that.\n\r");
		return;
	}

	one_argument (argument, arg);

	if (ch->mount) {
		ch->SendText ("You can't circle while mounted.\n\r");
		return;
	}

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

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

	if (victim == ch) {
		ch->SendText ("How can you sneak up on yourself?\n\r");
		return;
	}

	if (is_safe (ch, victim))
		return;

	if ((obj = get_eq_char (ch, WEAR_WIELD)) == NULL
	  || (obj->value [3] != 11 && obj->value [3] != 2)) {
		ch->SendText ("You need to wield a piercing or stabbing weapon.\n\r");
		return;
	}

	if (! ch->GetFightData ()) { 
		ch->SendText ("You can't circle when you aren't fighting.\n\r");
		return;
	}

	if (! victim->GetFightData ()) {
		ch->SendText ("You can't circle around a person who is not fighting.\n\r");
		return;
	}

	if (victim->num_fighting < 2) {
		act (AT_PLAIN, "You can't circle around them without a distraction.",
			ch, NULL, victim, TO_CHAR);
		return;
	}

	percent = number_percent () - (get_curr_lck (ch) - 16) 
		+ (get_curr_lck (victim) - 13);

	check_attacker (ch, victim);
	WAIT_STATE (ch, SkillTable.GetBeats (gsn_circle));

	if (can_use_skill (ch, percent, gsn_circle)) {
		learn_from_success (ch, gsn_circle);
		WAIT_STATE (ch, 2 * PULSE_VIOLENCE);
		global_retcode = multi_hit (ch, victim, gsn_circle);
		adjust_favor (ch, 10, 1);	
		check_illegal_pk (ch, victim);
	} else {
		learn_from_failure (ch, gsn_circle);
		WAIT_STATE (ch, 2 * PULSE_VIOLENCE);
		global_retcode = damage (ch, victim, 0, gsn_circle);
	}
}


// Berserk and HitAll. -- Altrag
void do_berserk (CCharacter *ch, char *argument)
{
  short percent;
  CAffectData af;
  
  if (!ch->GetFightData ())
  {
    ch->SendText ("But you aren't fighting!\n\r");
    return;
  }
  
  if (ch->IsBeserk ())
  {
    ch->SendText ("Your rage is already at its peak!\n\r");
    return;
  }
  
  percent = ch->IsNpc () ? 80 : ch->GetPcData ()->learned[gsn_berserk];
  WAIT_STATE (ch, SkillTable.GetBeats (gsn_berserk));
  if (!chance (ch, percent))
  {
    ch->SendText ("You couldn't build up enough rage.\n\r");
    learn_from_failure (ch, gsn_berserk);
    return;
  }
  af.type = gsn_berserk;
  /* Hmmm.. 10-20 combat rounds at level 50.. good enough for most mobs,
     and if not they can always go berserk again.. shrug.. maybe even
     too high. -- Altrag */
  af.duration = number_range (ch->GetLevel ()/5, ch->GetLevel ()*2/5);
  /* Hmm.. you get stronger when yer really enraged.. mind over matter
     type thing.. */
  af.location = APPLY_STR;
  af.modifier = 1;
  af.bitvector = AFF_BERSERK;
  affect_to_char (ch, &af);
  ch->SendText ("You start to lose control..\n\r");
  learn_from_success (ch, gsn_berserk);
  return;
}


// External from fight.c
ch_ret one_hit (CCharacter *ch, CCharacter *victim, int dt);
void do_hitall (CCharacter *ch, char *argument)
{
	CCharacter	*vch;
	CCharacter	*vch_next;
	short		nvict = 0;
	short		nhit = 0;
	short		percent;

	if (ch->GetInRoom ()->IsSafe ()) {
		ch->SendText ("You cannot do that here.\n\r");
		return;
	}

	if (!ch->GetInRoom ()->first_person) {
		ch->SendText ("There's no one here!\n\r");
		return;
	}

	percent = ch->IsNpc () ? 80 : ch->GetPcData ()->learned[gsn_hitall];
	for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
		vch_next = vch->GetNextInRoom ();
		if (is_same_group (ch, vch) || !is_legal_kill (ch, vch)
			|| !can_see (ch, vch) || is_safe (ch, vch))
				continue;
		if (++nvict > ch->GetLevel () / 5)
			break;
		check_illegal_pk (ch, vch);
		if (chance (ch, percent)) {
			nhit++;
			global_retcode = one_hit (ch, vch, TYPE_UNDEFINED);
		}
		else
			global_retcode = damage (ch, vch, 0, TYPE_UNDEFINED);
		// Fireshield, etc. could kill ch too.. :>.. -- Altrag
		if (global_retcode == rCHAR_DIED || global_retcode == rBOTH_DIED
			|| char_died (ch))
				return;
	}

	if (!nvict) {
		ch->SendText ("There's no one here!\n\r");
		return;
	}
	ch->SetMove (UMAX (0, ch->GetMove ()-nvict*3+nhit));
	if (nhit)
		learn_from_success (ch, gsn_hitall);
	else
		learn_from_failure (ch, gsn_hitall);
}
    
  

BOOL check_illegal_psteal (CCharacter *ch, CCharacter *victim)
{
	if (!victim->IsNpc () && ! ch->IsNpc ()) {
		if ((! victim->IsPkiller ()
		  || ch->GetLevel () - victim->GetLevel () > 10
		  || ! ch->IsPkiller ())
		  && (ch->GetInRoom ()->vnum < 29 || ch->GetInRoom ()->vnum > 43)
		  && ch != victim) {
			//sprintf (log_buf, "%s illegally stealing from %s at %d",
			//	(ch->IsNpc () ? ch->short_descr : ch->GetName ()),
			//	victim->GetName (), victim->GetInRoom ()->vnum);
			//gpDoc->LogString (log_buf);
			//to_channel (log_buf, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL);
			return TRUE;
		}
	}
	return FALSE;
}


void do_scan (CCharacter *ch, char *argument)
{
	CRoomIndexData	*was_in_room;
	CExitData		*pExit;
	short		dir = -1;
	short		dist;
	short		max_dist = 5;

	if (argument [0] == '\0') {
		ch->SendText ("Scan in a direction...\n\r");
		return;
	}

	if ((dir = get_door (argument)) == -1) {
		ch->SendText ("Scan in WHAT direction?\n\r");
		return;
	}

	was_in_room = ch->GetInRoom ();
	act (AT_GREY, "Scanning $t...", ch, dir_name [dir], NULL, TO_CHAR);
	act (AT_GREY, "$n scans $t.", ch, dir_name [dir], NULL, TO_ROOM);

	if (! can_use_skill (ch, number_percent (), gsn_scan)) {
		act (AT_GREY, "You stop scanning $t as your vision blurs.", ch,
			dir_name [dir], NULL, TO_CHAR);
		learn_from_failure (ch, gsn_scan);
		return;
	}

	if (ch->IsVampire ()) {
		if (time_info.hour < 21 && time_info.hour > 5) {
			ch->SendText ("You have trouble seeing clearly through all the "
			"light.\n\r");
			max_dist = 1;
		}
	}

	if ((pExit = get_exit (ch->GetInRoom (), dir)) == NULL) {
		act (AT_GREY, "You can't see $t.", ch, dir_name[dir], NULL, TO_CHAR);
		return;
	}

	if (ch->GetLevel () < 50)
		--max_dist;
	if (ch->GetLevel () < 40)
		--max_dist;

	for (dist = 1; dist <= max_dist; ) {
		if (pExit->IsClosed ()) {
			if (pExit->IsSecret ())
				act (AT_GREY, "Your view $t is blocked by a wall.", ch,
					dir_name [dir], NULL, TO_CHAR);
			else
				act (AT_GREY, "Your view $t is blocked by a door.", ch, 
					dir_name [dir], NULL, TO_CHAR);
			break;
		}
		if (room_is_private (pExit->GetToRoom ())
		  && ch->GetLevel () < LEVEL_GREATER) {
			act (AT_GREY, "Your view $t is blocked by a private room.", ch, 
				dir_name [dir], NULL, TO_CHAR);
			break;
		}
		ch->RemoveFromRoom ();
		ch->SendToRoom (pExit->GetToRoom ());    
		set_char_color (AT_RMNAME, ch);
		ch->SendText (ch->GetInRoom ()->GetName ());
		ch->SendText ("\n\r");
		ShowListToChar (ch->GetInRoom ()->GetContentList (), ch,
			FALSE, FALSE);
		show_char_to_char (ch->GetInRoom ()->first_person, ch);

		switch (ch->GetInRoom ()->sector_type) {
		  default:
			dist++;
			break;
		  case SECT_AIR:
			if (number_percent () < 80) dist++;
			break;
		  case SECT_INSIDE:
		  case SECT_FIELD:
		  case SECT_UNDERGROUND:
			dist++;
			break;
		  case SECT_FOREST:
		  case SECT_CITY:
		  case SECT_DESERT:
		  case SECT_HILLS:
			dist += 2;
			break;
		  case SECT_WATER_SWIM:
		  case SECT_WATER_NOSWIM:
			dist += 3;
			break;
		  case SECT_MOUNTAIN:
		  case SECT_UNDERWATER:
		  case SECT_OCEANFLOOR:
			dist += 4;
			break;
		}

		if (dist >= max_dist) {
			act (AT_GREY, "Your vision blurs with distance and you see no "
				"farther $t.", ch, dir_name [dir], NULL, TO_CHAR);
			break;
		}
		if ((pExit = get_exit (ch->GetInRoom (), dir)) == NULL) {
			act (AT_GREY, "Your view $t is blocked by a wall.", ch, 
				dir_name [dir], NULL, TO_CHAR);
			break;
		}
	}

	ch->RemoveFromRoom ();
	ch->SendToRoom (was_in_room);
	learn_from_success (ch, gsn_scan);
}


// Basically the same guts as do_scan() from above (please keep them in
// sync) used to find the victim we're firing at.	-Thoric
CCharacter *scan_for_victim (CCharacter *ch, CExitData *pexit, char *name)
{
	CCharacter	*victim;
	short		dist, dir;
	short		max_dist = 8;

	if (ch->IsBlind () || ! pexit)
		return NULL;

	CRoomIndexData	*pWasInRoom = ch->GetInRoom ();
	if (ch->IsVampire () && time_info.hour < 21 && time_info.hour > 5)
		max_dist = 1;

	if (ch->GetLevel () < 50) --max_dist;
	if (ch->GetLevel () < 40) --max_dist;
	if (ch->GetLevel () < 30) --max_dist;

	for (dist = 1; dist <= max_dist; ) {
		if (pexit->IsClosed ())
			break;

		if (room_is_private (pexit->GetToRoom ())
			&& ch->GetLevel () < LEVEL_GREATER)
				break;

		ch->RemoveFromRoom ();
		ch->SendToRoom (pexit->GetToRoom ());    

		if ((victim = get_char_room (ch, name)) != NULL) {
			ch->RemoveFromRoom ();
			ch->SendToRoom (pWasInRoom);
			return victim;
		}

		switch (ch->GetInRoom ()->sector_type) {
		  default:
			++dist;
			break;
		  case SECT_AIR:
			if (number_percent () < 80)
				++dist;
				break;
		  case SECT_INSIDE:
		  case SECT_FIELD:
		  case SECT_UNDERGROUND:
			++dist;
			break;
		  case SECT_FOREST:
		  case SECT_CITY:
		  case SECT_DESERT:
		  case SECT_HILLS:
			dist += 2;
			break;
		  case SECT_WATER_SWIM:
		  case SECT_WATER_NOSWIM:
			dist += 3;
			break;
		  case SECT_MOUNTAIN:
		  case SECT_UNDERWATER:
		  case SECT_OCEANFLOOR:
			dist += 4;
			break;
		}

		if (dist >= max_dist)
			break;

		dir = pexit->vdir;
		if ((pexit = get_exit (ch->GetInRoom (), dir)) == NULL)
			break;
	}

	ch->RemoveFromRoom ();
	ch->SendToRoom (pWasInRoom);

	return NULL;
}


// Perform the actual attack on a victim			-Thoric
ch_ret ranged_got_target (CCharacter *ch, CCharacter *victim,
						  CObjData *weapon, CObjData *projectile,
						  short dist, short dt, char *stxt, short color)
{
	if (ch->GetInRoom ()->IsSafe ()) {
		// safe room, bubye projectile
		if (projectile) {
			ch->SendTextf ("Your %s is blasted from existance by a godly "
				"presense.", myobj (projectile));
			act (color, "A godly presence smites $p!", ch,
				projectile, NULL, TO_ROOM);
			extract_obj (projectile);
		} else {
			ch->SendTextf ("Your %s is blasted from existance by a godly "
				"presense.", stxt);
			act (color, "A godly presence smites $t!", ch, aoran (stxt),
				NULL, TO_ROOM);
		}
		return rNONE;
	}

	if (victim->IsNpc () && victim->IsAction (ACT_SENTINEL)
	  && ch->GetInRoom () != victim->GetInRoom ()) {
		// letsee, if they're high enough.. attack back with fireballs
		// long distance or maybe some minions... go herne! heh..
		//
		// For now, just always miss if not in same room  -Thoric
		if (projectile) {
			learn_from_failure (ch, gsn_missile_weapons);

			// 50% chance of projectile getting lost
			if (number_percent () < 50)
				extract_obj (projectile);
			else {
				if (projectile->in_obj)
					obj_from_obj (projectile);
				if (projectile->carried_by)
					obj_from_char (projectile);
				obj_to_room (projectile, victim->GetInRoom ());
			}
		}
		return damage (ch, victim, 0, dt);
	}

	if (number_percent () > 50 || (projectile && weapon
	  && can_use_skill (ch, number_percent (), gsn_missile_weapons))) {
		if (projectile)
			global_retcode =
				projectile_hit (ch, victim, weapon, projectile, dist);
		else
			global_retcode = spell_attack (dt, ch->GetLevel (), ch, victim);
	} else {
		learn_from_failure (ch, gsn_missile_weapons);
		global_retcode = damage (ch, victim, 0, dt);

		if (projectile) {
			// 50% chance of getting lost
			if (number_percent() < 50)
				extract_obj (projectile);
			else {
				if (projectile->in_obj)
					obj_from_obj (projectile);
				if (projectile->carried_by)
					obj_from_char (projectile);
				obj_to_room (projectile, victim->GetInRoom ());
			}
		}
	}
	return global_retcode;
}


// Generic use ranged attack function			-Thoric & Tricops
ch_ret ranged_attack (CCharacter *ch, char *argument, CObjData *weapon,
				      CObjData *projectile, short dt, short range)
{
	CCharacter		*victim, *vch;
	CExitData		*pexit;

	char		arg [MAX_INPUT_LENGTH];
	char		arg1 [MAX_INPUT_LENGTH];
	char		temp [MAX_INPUT_LENGTH];
	char		buf [MAX_STRING_LENGTH];
	short		dir = -1, dist = 0, color = AT_GREY;
	char		*dtxt = "somewhere";
	char		*stxt = "burst of energy";
	int			count;

	if (argument && argument [0] != '\0' && argument [0] == '\'') {
		one_argument (argument, temp);
		argument = temp;
	}

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

	if (arg [0] == '\0') {
		ch->SendText ("Where?  At who?\n\r");
		return rNONE;
	}

	victim = NULL;

	// get an exit or a victim
	if ((pexit = find_door (ch, arg, TRUE)) == NULL) {
		if ((victim = get_char_room (ch, arg)) == NULL) {
			ch->SendText ("Aim in what direction?\n\r");
			return rNONE;
		} else {
			if (ch->GetFightWho () == victim) {
				ch->SendText ("They are too close to release that type of "
					"attack!\n\r");
				return rNONE;
			}
			if (! ch->IsNpc () && ! victim->IsNpc ()) {
				ch->SendText ("Pkill like a real pkiller.\n\r");
				return rNONE;
			}
		}
	}
	else dir = pexit->vdir;

	// check for ranged attacks from private rooms, etc
	CRoomIndexData &ChRoom = *ch->GetInRoom ();
	if (! victim) {
		if (ChRoom.IsPrivate () || ChRoom.IsSolitary ()) {
			ch->SendText ("You cannot perform a ranged attack from a "
				"private room.\n\r");
			return rNONE;
		}
		if (ChRoom.tunnel > 0) {
			count = 0;
			for (vch = ChRoom.first_person; vch; vch = vch->GetNextInRoom ())
				++count;
			if (count >= ChRoom.tunnel) {
				ch->SendText ("This room is too cramped to perform such "
					"an attack.\n\r");
				return rNONE;
			}
		}
	}

	CSkill	*pSkill = SkillTable.GetSkill (dt);

	if (pexit && ! pexit->GetToRoom ()) {
		ch->SendText ("Are you expecting to fire through a wall!?\n\r");
		return rNONE;
	}

	// Check for obstruction
	if (pexit && pexit->IsClosed ()) {
		if (pexit->IsSecret () || pexit->IsDig ())
			ch->SendText ("Are you expecting to fire through a wall!?\n\r");
		else
			ch->SendText ("Are you expecting to fire through a door!?\n\r");
		return rNONE;
	}

	vch = NULL;
	if (pexit && arg1 [0] != '\0') {
		if ((vch = scan_for_victim (ch, pexit, arg1)) == NULL) {
			ch->SendText ("You cannot see your target.\n\r");
			return rNONE;
		}

		// don't allow attacks on mobs stuck in another area?
		// if (vch->IsNpc () && vch->IsAction (ACT_STAY_AREA)
		//   && ch->GetInRoom ()->area != vch->GetInRoom ()->area) {}

		// don't allow attacks on mobs that are in a no-missile room
		if (vch->GetInRoom ()->IsNoMissile ()) {
			ch->SendText ("You can't get a clean shot off.\n\r");
			return rNONE;
		}
		if (! ch->IsNpc () && ! vch->IsNpc ()) {
			ch->SendText ("Pkill like a real pkiller.\n\r");
			return rNONE;
		}

		// can't properly target someone heavily in battle
		if (vch->num_fighting > max_fight (vch)) {
			ch->SendText ("There is too much activity there for you to "
				"get a clear shot.\n\r");
			return rNONE;
		}
	}
	if (vch) {
		if (! vch->IsNpc () && ! ch->IsNpc () && ch->IsNice ()) {
			ch->SendText ("Your too nice to do that!\n\r");
			return rNONE;
		}
		if (vch && is_safe (ch, vch))
			return rNONE;
	}
	CRoomIndexData	*pWasInRoom = ch->GetInRoom ();

	if (projectile) {
		separate_obj (projectile);
		if (pexit) {
			if (weapon) {
				act (AT_GREY, "You fire $p $T.", ch, projectile,
					dir_name [dir], TO_CHAR);
				act (AT_GREY, "$n fires $p $T.", ch, projectile,
					dir_name [dir], TO_ROOM);
			} else {
				act (AT_GREY, "You throw $p $T.", ch, projectile,
					dir_name [dir], TO_CHAR);
				act (AT_GREY, "$n throw $p $T.", ch, projectile,
					dir_name [dir], TO_ROOM);
			}
		} else {
			if (weapon) {
				act (AT_GREY, "You fire $p at $N.", ch, projectile,
					victim, TO_CHAR);
				act (AT_GREY, "$n fires $p at $N.", ch, projectile,
					victim, TO_NOTVICT);
				act (AT_GREY, "$n fires $p at you!", ch, projectile,
					victim, TO_VICT);
			} else {
				act (AT_GREY, "You throw $p at $N.", ch, projectile,
					victim, TO_CHAR);
				act (AT_GREY, "$n throws $p at $N.", ch, projectile,
					victim, TO_NOTVICT);
				act (AT_GREY, "$n throws $p at you!", ch, projectile,
					victim, TO_VICT);
			}
		}
	}
	else if (pSkill) {
		if (pSkill->ValidDamageMsg ())
			stxt = pSkill->GetDamageMsg ();
		else
			stxt = pSkill->GetName ();

		// a plain "spell" flying around seems boring
		if (! str_cmp (stxt, "spell"))
			stxt = "magical burst of energy";

		if (pSkill->GetType () == SKILL_SPELL) {
			color = AT_MAGIC;
			if (pexit) {
				act (AT_MAGIC, "You release $t $T.", ch, aoran (stxt),
					dir_name [dir], TO_CHAR);
				act (AT_MAGIC, "$n releases $s $t $T.", ch, stxt,
					dir_name [dir], TO_ROOM);
			} else {
				act (AT_MAGIC, "You release $t at $N.", ch, aoran (stxt),
					victim, TO_CHAR);
				act (AT_MAGIC, "$n releases $s $t at $N.", ch, stxt,
					victim, TO_NOTVICT);
				act (AT_MAGIC, "$n releases $s $t at you!", ch, stxt,
					victim, TO_VICT);
			}
		}
	} else {
		bug ("Ranged_attack: no projectile, no skill dt %d", dt);
		return rNONE;
	}

	// victim in same room
	if (victim) {
		check_illegal_pk (ch, victim);
		check_attacker (ch, victim);
		return ranged_got_target (ch, victim, weapon, projectile,
			0, dt, stxt, color);
	}

	// assign scanned victim
	victim = vch;

	// reverse direction text from move_char
	dtxt = ReverseExit (pexit->vdir);

	while (dist <= range) {
		ch->RemoveFromRoom ();
		ch->SendToRoom (pexit->GetToRoom ());

		if (pexit->IsClosed ()) {
			// whadoyahknow, the door's closed
			if (projectile)
				sprintf (buf,
					"You see your %s pierce a door in the distance to the %s.", 
					myobj (projectile), dir_name [dir]);
			else
				sprintf (buf,
					"You see your %s hit a door in the distance to the %s.",
					stxt, dir_name [dir]);
			act (color, buf, ch, NULL, NULL, TO_CHAR);

			if (projectile) {
				sprintf (buf, "$p flies in from %s and implants itself "
					"solidly in the %sern door.", dtxt, dir_name [dir]);
				act (color, buf, ch, projectile, NULL, TO_ROOM);
			} else {
				sprintf (buf, "%s flies in from %s and implants itself "
					"solidly in the %sern door.", aoran (stxt), dtxt,
					dir_name [dir]);
				buf [0] = UPPER (buf [0]);
				act (color, buf, ch, NULL, NULL, TO_ROOM);
			}
			break; 
		}


		// no victim? pick a random one
		if (! victim) {
			vch = ch->GetInRoom ()->first_person;
			for ( ; vch; vch = vch->GetNextInRoom ()) {
				if (((ch->IsNpc () && ! vch->IsNpc ())
				  || (! ch->IsNpc () && vch->IsNpc ()))
				  && number_bits (1) == 0) {
					victim = vch;
					break;
				}
			}
			if (victim && is_safe (ch, victim)) {
				ch->RemoveFromRoom ();
				ch->SendToRoom (pWasInRoom);
				return rNONE;
			}
		}

		// In the same room as our victim?
		if (victim && ch->GetInRoom () == victim->GetInRoom ()) {
			if (projectile)
				act (color, "$p flies in from $T.", ch, projectile,
					dtxt, TO_ROOM);
			else
				act (color, "$t flies in from $T.", ch, aoran (stxt),
					dtxt, TO_ROOM);

			// get back before the action starts
			ch->RemoveFromRoom ();
			ch->SendToRoom (pWasInRoom);

			check_illegal_pk (ch, victim);
			check_attacker (ch, victim);
			return ranged_got_target (ch, victim, weapon, projectile,
				dist, dt, stxt, color);
		}

		if (dist == range) {
			if (projectile) {
				act (color,
					"Your $t falls harmlessly to the ground to the $T.", 
					ch, myobj (projectile), dir_name [dir], TO_CHAR);
				act (color, "$p flies in from $T and falls harmlessly to "
					"the ground here.", ch, projectile, dtxt, TO_ROOM);
				if (projectile->in_obj)
					obj_from_obj (projectile);
				if (projectile->carried_by)
					obj_from_char (projectile);
				obj_to_room (projectile, ch->GetInRoom ());
			} else {
				act (color, "Your $t fizzles out harmlessly to the $T.",
					ch, stxt, dir_name [dir], TO_CHAR);
				act (color, "$t flies in from $T and fizzles out harmlessly.",
					ch, aoran (stxt), dtxt, TO_ROOM);
			}
			break;
		}

		if ((pexit = get_exit (ch->GetInRoom (), dir)) == NULL) {
			if (projectile) {
				act (color, "Your $t hits a wall and bounces harmlessly "
					"to the ground to the $T.", 
					ch, myobj (projectile), dir_name [dir], TO_CHAR);
				act (color, "$p strikes the $Tsern wall and falls harmlessly"
					" to the ground.", ch, projectile, dir_name [dir], TO_ROOM);
				if (projectile->in_obj)
					obj_from_obj (projectile);
				if (projectile->carried_by)
					obj_from_char (projectile);
				obj_to_room (projectile, ch->GetInRoom ());
			} else {
				act (color, "Your $t harmlessly hits a wall to the $T.", 
					ch, stxt, dir_name [dir], TO_CHAR);
				act (color, "$t strikes the $Tsern wall and falls harmlessly"
					" to the ground.", ch, aoran (stxt), dir_name [dir], TO_ROOM);
			}
			break;
		}
		if (projectile)
			act (color, "$p flies in from $T.", ch, projectile, dtxt, TO_ROOM);
		else
			act (color, "$t flies in from $T.", ch, aoran (stxt), dtxt, TO_ROOM);
		++dist;
	}

	ch->RemoveFromRoom ();
	ch->SendToRoom (pWasInRoom);

	return rNONE;
}


// Fire <direction> <target>
//
// Fire a projectile from a missile weapon (bow, crossbow, etc)
//
// Design by Thoric, coding by Thoric and Tricops.
//
// Support code (see projectile_hit(), quiver support, other changes to
// fight.c, etc by Thoric.
void do_fire (CCharacter* ch, char* argument)
{
	CObjData	*arrow;
	CObjData	*bow;
	short		max_dist;

	if (argument [0] == '\0' || !str_cmp (argument, " ")) {
		ch->SendText ("Fire where at who?\n\r");
		return;
	}

	if (ch->GetInRoom ()->IsSafe ()) {
		set_char_color (AT_MAGIC, ch);
		ch->SendText ("A magical force prevents you from attacking.\n\r");
		return;
	}

	// Find the projectile weapon
	if (bow = get_eq_char (ch, WEAR_MISSILE_WIELD))
		if (bow->item_type != ITEM_MISSILE_WEAPON)
			bow = NULL;

	if (! bow) {
		ch->SendText ("You are not wielding a missile weapon.\n\r");
		return;
	}

	// modify maximum distance based on bow-type and ch's class/str/etc
	max_dist = URANGE (1, bow->value [4], 10);

	if ((arrow = find_projectile (ch, bow->value [3])) == NULL) {
		char	*msg = "You have nothing to fire...\n\r";

		switch (bow->value [3]) {
		  case DAM_BOLT:	msg = "You have no bolts...\n\r";	break;
		  case DAM_ARROW:	msg = "You have no arrows...\n\r";	break;
		  case DAM_DART:	msg = "You have no darts...\n\r";	break;
		  case DAM_STONE:	msg = "You have no slingstones...\n\r";	break;
		  case DAM_PEA:		msg = "You have no peas...\n\r";	break;
		}
		ch->SendText (msg);
		return;
	}

	// handle the ranged attack
	ranged_attack (ch, argument, bow, arrow, TYPE_HIT +
		arrow->value [3], max_dist);
}


// Attempt to fire at a victim.
// Returns FALSE if no attempt was made
BOOL mob_fire (CCharacter* ch, char* name)
{
	CObjData	*arrow;
	CObjData	*bow;
	short		max_dist;

	if (ch->GetInRoom ()->IsSafe ())
		return FALSE;

	if (bow = get_eq_char (ch, WEAR_MISSILE_WIELD))
		if (bow->item_type != ITEM_MISSILE_WEAPON)
			bow = NULL;

	if (! bow)
		return FALSE;

	// modify maximum distance based on bow-type and ch's class/str/etc
	max_dist = URANGE (1, bow->value [4], 10);

	if ((arrow = find_projectile (ch, bow->value [3])) == NULL)
		return FALSE;

	ranged_attack (ch, name, bow, arrow, TYPE_HIT +
		arrow->value [3], max_dist);

	return TRUE;
}


/*
 *
 *
void do_release (CCharacter *ch, char *argument)
{
  CCharacter *victim;
  OBJ_INDEX_DATA *arrow;
  CObjData *quiver;
  CObjData *bow;
  CExitData *pexit;
  CRoomIndexData *was_in_room;
  char arg[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  short dir;
  short dist;
  short max_dist = 3;

  for (bow = ch->last_carrying; bow; bow = bow->prev_content)
  {
    if (can_see_obj (ch, bow)
	&& (bow->wear_loc == WEAR_WIELDED
	|| bow->wear_loc == WEAR_DUAL_WIELDED)
	&& ((bow->item_type == ITEM_SHORT_BOW) || (bow->item_type == 
           ITEM_LONG_BOW) || (bow->item_type == ITEM_CROSS_BOW)))
      break;
  }

  if (!bow)
  {
    ch->SendText ("You are not wielding a bow.\n\r");
    return;
  }

  switch (bow->item_type)
  {
    case ITEM_SHORT_BOW: max_dist = 4; break;
    case ITEM_LONG_BOW:  max_dist = 5; break;
    case ITEM_CROSS_BOW: max_dist = 6; break;
  }

  for (quiver = ch->last_carrying; quiver; quiver = quiver->GetPrev ())
  {
    if (can_see_obj (ch, quiver)
      && (quiver->item_type == ITEM_QUIVER))
    break;
  }

  for (arrow = ch->last_carrying; arrow; arrow = arrow->GetPrev ())
  {
    if (can_see_obj (ch, arrow)
      && (arrow->item_type == ITEM_PROJECTILE))
    break;
  }

  if ((dir = get_door (arg)) == -1)
  {
    ch->SendText ("Aim in what direction?\n\r");
    return;
  }

  if ((pexit = get_exit (ch->GetInRoom (), dir)) == NULL)
  {
    ch->SendText ("Are you expecting to fire through a wall!?\n\r");
    return;
  }

  if (IS_SET (pexit->exit_info, EX_CLOSED))
  {
    ch->SendText ("Are you expecting to fire through a door!?\n\r");
    return;
  }

  was_in_room = ch->GetInRoom ();

  act (AT_GREY, "You release an arrow $t.", ch, dir_name[dir], NULL, 
	TO_CHAR);
  act (AT_GREY, "$n releases an arrow $t.", ch, dir_name[dir], NULL,
	TO_ROOM);
  
  for (dist = 0; dist <= max_dist;)   
  {
    if (IS_SET (pexit->exit_info, EX_CLOSED))
    {
      act (AT_GREY, "You see your arrow pierce a door in the distance.",
	ch, NULL, NULL, TO_CHAR);
      break; 
    }
    
    ch->RemoveFromRoom ();
    ch->SendToRoom (pexit->to_room);

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

    if (dist == max_dist) break;

    if ((pexit = get_exit (ch->GetInRoom (), dir)) == NULL)
    {
    }
    
  }

  ch->RemoveFromRoom ();
  ch->SendToRoom (was_in_room);

  return;
}
*/

/* -- working on -- 
 * Syntaxes: throw object  (assumed already fighting)
 *	     throw object direction target  (all needed args for distance 
 *	          throwing)
 *	     throw object  (assumed same room throw)

void do_throw (CCharacter *ch, char *argument)
{
  CRoomIndexData *was_in_room;
  CCharacter *victim;
  CObjData *throw_obj;
  CExitData *pexit;
  short dir;
  short dist;
  short max_dist = 3;
  char arg[MAX_INPUT_LENGTH];
  char arg1[MAX_INPUT_LENGTH];
  char arg2[MAX_INPUT_LENGTH];

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

  for (throw_obj = ch->last_carrying; throw_obj;
	throw_obj = throw_obj=>prev_content)
  {
    if (can_see_obj (ch, throw_obj)
	&& (throw_obj->wear_loc == WEAR_HELD || throw_obj->wear_loc == 
	WEAR_WIELDED || throw_obj->wear_loc == WEAR_DUAL_WIELDED)
	&& nifty_is_name (arg, throw_obj->name))
      break;
  }

  if (!throw_obj)
  {
    ch->SendText ("You aren't holding or wielding anything like that.\n\r", 
	ch);
    return;
  }

  if ((throw_obj->item_type != ITEM_WEAPON)
  {
    ch->SendText ("You can only throw weapons.\n\r");
    return;
  }

  if (get_obj_weight (throw_obj) - (3 * (ch->GetCurrentStrength () - 15)) > 0)
  {
    ch->SendText ("That is too heavy for you to throw.\n\r");
    learn_from_failure (ch, gsn_throw);
    return;
  }

  if (ch->GetFightData ())
    victim = ch->GetFightData ();
   else
    {
      if (( (victim = get_char_room (ch, arg1)) == NULL)
  	&& (arg2[0] == '\0'))
      {
        act (AT_GREY, "Throw $t at whom?", ch, obj->short_descr, NULL,  
	  TO_CHAR);
        return;
      }
    }
}*/


// Search inventory for an appropriate projectile to fire.
// Also search open quivers.					-Thoric
CObjData *find_projectile (CCharacter* ch, int type)
{
	CObjData	*obj, *obj2;

	POSITION	pos = ch->GetHeadCarryPos ();
	while (obj = ch->GetNextCarrying (pos)) {
		if (can_see_obj (ch, *obj)) {
			if (obj->item_type == ITEM_QUIVER
			  && !IS_SET (obj->value [1], CONT_CLOSED)) {
				POSITION	pos = obj->GetTailContentPos ();
				while (obj2 = obj->GetPreviousContent (pos)) {
					if (obj2->item_type == ITEM_PROJECTILE
						&& obj2->value [3] == type)
							return obj2;
				}
			}
			if (obj->item_type == ITEM_PROJECTILE && obj->value [3] == type)
				return obj;
		}
	}

	return NULL;
}


void do_slice (CCharacter *ch, char *argument)
{
  CObjData *corpse;
  CObjData *obj;
  CObjData *slice;
  BOOL found;
  CMobIndexData *pMobIndex;
  char buf[MAX_STRING_LENGTH];
  char buf1[MAX_STRING_LENGTH];
  found = FALSE;


  if (!ch->IsNpc () && ch->IsMortal ()
  &&   ch->GetLevel () < SkillTable.GetClassLevel (gsn_slice, ch->GetClass ()))
  {
    ch->SendText ("You are not learned in this skill.\n\r");
    return;
  }

  if (argument[0] == '\0')
  { 
    ch->SendText ("From what do you wish to slice meat?\n\r");
    return;
  }


  if ((obj = get_eq_char (ch, WEAR_WIELD)) == NULL
  ||   (obj->value[3] != 1 && obj->value[3] != 2 && obj->value[3] != 3
      && obj->value[3] != 11))
  {
    ch->SendText ("You need to wield a sharp weapon.\n\r");
    return;
  }

  if ((corpse = get_obj_here (ch, argument)) == NULL)
  {  
    ch->SendText ("You can't find that here.\n\r");
    return;
  }

  if (corpse->item_type != ITEM_CORPSE_NPC || corpse->value[3] < 75)
  {
    ch->SendText ("That is not a suitable source of meat.\n\r");
    return;
  }

  if ((pMobIndex = MobTable.GetMob ((short) - (corpse->value[2]))) == NULL)   
  {
        bug ("Can not find mob for value[2] of corpse, do_slice", 0);
        return;
  }

  if (OIdxTable.GetObj (OBJ_VNUM_SLICE) == NULL)
  {
    bug ("Vnum 24 not found for do_slice!", 0);
    return;
  }

  if (! can_use_skill (ch, number_percent (), gsn_slice) && ch->IsMortal ())
  { 
    ch->SendText ("You fail to slice the meat properly.\n\r");
    learn_from_failure (ch, gsn_slice); /* Just in case they die :> */
    if (number_percent () + (ch->GetDexterity () - 13) < 10)   
    {
      act (AT_BLOOD, "You cut yourself!", ch, NULL, NULL, TO_CHAR);
      damage (ch, ch, ch->GetLevel (), gsn_slice);
    }
    return;
  }

  slice = create_object (OIdxTable.GetObj (OBJ_VNUM_SLICE), 0);

  sprintf (buf, "meat fresh slice %s", pMobIndex->GetPlayerName ());
  slice->SetName (buf);

  sprintf (buf, "a slice of raw meat from %s", pMobIndex->GetShortDescr ());
  slice->SetShortDescr (buf);

  sprintf (buf1, "A slice of raw meat from %s lies on the ground.",
	pMobIndex->GetShortDescr ());
  slice->SetDescription (buf1);
    
  act (AT_BLOOD, "$n cuts a slice of meat from $p.", ch, corpse, NULL, TO_ROOM);
  act (AT_BLOOD, "You cut a slice of meat from $p.", ch, corpse, NULL, TO_CHAR);

  obj_to_char (slice, ch);
  corpse->value[3] -= 25;
  learn_from_success (ch, gsn_slice);
  return;
}


//  Fighting Styles - haus
void do_style (CCharacter* ch, char* argument)
{
	char	arg [MAX_INPUT_LENGTH];

	if (ch->IsNpc ())
		return;
	int		style = ch->GetStyle ();

	one_argument (argument, arg);
	if (arg [0] == '\0') {
		ch->SendColorf ("&wAdopt which fighting style?  (current:  %s&w)\n\r",
			style == STYLE_BERSERK    ? "&Rberserk"    :
			style == STYLE_AGGRESSIVE ? "&Raggressive" :
			style == STYLE_DEFENSIVE  ? "&Ydefensive"  :
			style == STYLE_EVASIVE    ? "&Yevasive"    : "standard");
		return;
	}

	if (! str_prefix (arg, "evasive")) {
		if (ch->GetLevel () <
		  SkillTable.GetClassLevel (gsn_style_evasive, ch->GetClass ())) {
			ch->SendText ("You have not yet learned enough to fight evasively.\n\r");
			return;
		}
		WAIT_STATE (ch, SkillTable.GetBeats (gsn_style_evasive));
		if (number_percent () < ch->GetLearnedPercent (gsn_style_evasive)) {
			// success
			if (ch->GetFightData ()) {
				ch->SetPosition (POS_EVASIVE);
				learn_from_success (ch, gsn_style_evasive);
				if (ch->IsPkiller ())
					act (AT_ACTION, "$n falls back into an evasive stance.",
						ch, NULL, NULL, TO_ROOM);
			}
			ch->SetStyle (STYLE_EVASIVE);
			ch->SendText ("You adopt an evasive fighting style.\n\r");
			return;
		} else {
			// failure
			ch->SendText ("You nearly trip in a lame attempt to adopt "
				"an evasive fighting style.\n\r");
			return;
		}
	}
	else if (! str_prefix (arg, "defensive")) {
		if (ch->GetLevel () <
		  SkillTable.GetClassLevel (gsn_style_defensive, ch->GetClass())) {
			ch->SendText ("You have not yet learned enough to fight "
				"defensively.\n\r");
			return;
		}
		WAIT_STATE (ch, SkillTable.GetBeats (gsn_style_defensive));
		if (number_percent () < ch->GetLearnedPercent (gsn_style_defensive)){
			// success
			if (ch->GetFightData ()) {
				ch->SetPosition (POS_DEFENSIVE);
				learn_from_success (ch, gsn_style_defensive);
				if (ch->IsPkiller ())
					act (AT_ACTION, "$n moves into a defensive posture.",
						ch, NULL, NULL, TO_ROOM );
			}
			ch->SetStyle (STYLE_DEFENSIVE);
			ch->SendText ("You adopt a defensive fighting style.\n\r");
			return;
		} else {
			// failure
			ch->SendText ("You nearly trip in a lame attempt to adopt "
				"a defensive fighting style.\n\r");
			return;
		}
	}
	else if (! str_prefix (arg, "standard")) {
		if (ch->GetLevel () <
		  SkillTable.GetClassLevel (gsn_style_standard, ch->GetClass ())) {
			ch->SendText ("You have not yet learned enough to fight in "
				"the standard style.\n\r");
			return;
		}
		WAIT_STATE (ch, SkillTable.GetBeats (gsn_style_standard));
		if (number_percent () < ch->GetLearnedPercent (gsn_style_standard)) {
			// success
			if (ch->GetFightData ()) {
				ch->SetPosition (POS_FIGHTING);
				learn_from_success (ch, gsn_style_standard);
				if (ch->IsPkiller ())
					act (AT_ACTION, "$n switches to a standard fighting "
						"style.", ch, NULL, NULL, TO_ROOM);
			}
			ch->SetStyle (STYLE_FIGHTING);
			ch->SendText ("You adopt a standard fighting style.\n\r");
			return;
		} else {
			// failure
			ch->SendText ("You nearly trip in a lame attempt to adopt a "
				"standard fighting style.\n\r");
			return;
		}
	}
	else if (! str_prefix (arg, "aggressive")) {
		if (ch->GetLevel () <
		  SkillTable.GetClassLevel (gsn_style_aggressive, ch->GetClass ())) {
			ch->SendText ("You have not yet learned enough to fight "
				"aggressively.\n\r");
			return;
		}
		WAIT_STATE (ch, SkillTable.GetBeats (gsn_style_aggressive));
		if (number_percent () < ch->GetLearnedPercent (gsn_style_aggressive)){
			// success
			if (ch->GetFightData ()) {
				ch->SetPosition (POS_AGGRESSIVE);
				learn_from_success (ch, gsn_style_aggressive);
				if (ch->IsPkiller ())
					act (AT_ACTION, "$n assumes an aggressive stance.",
						ch, NULL, NULL, TO_ROOM);
			}
			ch->SetStyle (STYLE_AGGRESSIVE);
			ch->SendText ("You adopt an aggressive fighting style.\n\r");
			return;
		} else {
			// failure
			ch->SendText ("You nearly trip in a lame attempt to adopt an "
				"aggressive fighting style.\n\r");
			return;
		}
	}
	else if (! str_prefix (arg, "berserk")) {
		if (ch->GetLevel () <
		  SkillTable.GetClassLevel (gsn_style_berserk, ch->GetClass ())) {
			ch->SendText ("You have not yet learned enough to fight as a "
				"berserker.\n\r");
			return;
		}
		WAIT_STATE (ch, SkillTable.GetBeats (gsn_style_berserk));
		if (number_percent () < ch->GetLearnedPercent (gsn_style_berserk)) {
			// success
			if (ch->GetFightData ()) {
				ch->SetPosition (POS_BERSERK);
				learn_from_success (ch, gsn_style_berserk);
				if (ch->IsPkiller ())
					act (AT_ACTION, "$n enters a wildly aggressive style.",
						ch, NULL, NULL, TO_ROOM);
			}
			ch->SetStyle (STYLE_BERSERK);
			ch->SendText ("You adopt a berserk fighting style.\n\r");
			return;
		} else {
			// failure
			ch->SendText ("You nearly trip in a lame attempt to adopt a "
				"berserk fighting style.\n\r");
			return;
		}
	}

	ch->SendText ("Adopt which fighting style?\n\r");
}


// Function used by qsort to sort skills
int skill_comp (CSkill **sk1, CSkill **sk2)
{
    CSkill *skill1 =  *sk1;
    CSkill *skill2 =  *sk2;

    if (!skill1 && skill2)
	return 1;
    if (skill1 && !skill2)
	return -1;
    if (!skill1 && !skill2)
	return 0;
    if (skill1->GetType () < skill2->GetType ())
	return -1;
    if (skill1->GetType () > skill2->GetType ())
	return 1;
    return strcmp (skill1->GetName (), skill2->GetName ());
}


BOOL CSkillTable::Read (FILE* fp)
{
	BOOL	bHerb = this == &HerbTable;
	int	max = bHerb ? MAX_HERB : MAX_SKILL;

	if (m_Top >= max) {
		bug ("CSkillTable::Read: Too many Skills for table. Max=%d.", max);
		fclose (fp);
		return FALSE;
	}

	m_sk [m_Top] = new CSkill;
	m_sk [m_Top++]->Read (fp);

	if (bHerb) {
		if (m_sk [m_Top-1]->m_Slot == 0) then
			m_sk [m_Top-1]->m_Slot = m_Top-1;
	}
	return TRUE;
}


void CSkillTable::Add (CSkill* skill)
{
	BOOL	bHerb = this == &HerbTable;
	int	max = bHerb ? MAX_HERB : MAX_SKILL;
	ASSERT (m_Top < max);

	m_sk [m_Top++] = skill;

	if (bHerb) {
		int		max, x;

		for (max = x = 0; x < m_Top-1; ++x)
			if (m_sk [x] && m_sk [x]->m_Slot > max)
				max = m_sk [x]->m_Slot;
		skill->m_Slot = max+1;
	}
}


// Sort the skill table with qsort
void CSkillTable::Sort ()
{
	gpDoc->LogString ("Sorting skill table...", LOG_BOOT);
	qsort (&m_sk [1], m_Top-1, sizeof (CSkill*),
		 (int (*) (const void *, const void *)) skill_comp);
}


//Lookup a skill by name.
int CSkillTable::Lookup (const char *name)
{
    int		sn;
	BOOL	bHerb = this == &HerbTable;

	if (! bHerb) {			// do skills
		if ((sn = BsearchExact (name, gsn_first_spell, gsn_first_skill-1)) == -1)
		if ((sn = BsearchExact (name, gsn_first_skill, gsn_first_weapon-1)) == -1)
		if ((sn = BsearchExact (name, gsn_first_weapon, gsn_first_tongue-1)) == -1)
		if ((sn = BsearchExact (name, gsn_first_tongue, gsn_top_sn-1)) == -1)
		if ((sn = Bsearch (name, gsn_first_spell, gsn_first_skill-1)) == -1)
		if ((sn = Bsearch (name, gsn_first_skill, gsn_first_weapon-1)) == -1)
		if ((sn = Bsearch (name, gsn_first_weapon, gsn_first_tongue-1)) == -1)
		if ((sn = Bsearch (name, gsn_first_tongue, gsn_top_sn-1)) == -1
		  && gsn_top_sn < m_Top) {
			for (sn = gsn_top_sn; sn < m_Top; ++sn) {
				if (GetSkill (sn) == NULL || GetName (sn) == NULL)
					return -1;
				if (LOWER (name[0]) == LOWER (GetName (sn)[0])
				  && !str_prefix (name, GetName (sn)))
					return sn;
			}
			return -1;
		}
		return sn;
	} else {				// else do herbs
		for (sn = 0; sn < m_Top; sn++) {
			if (! GetSkill (sn) || ! GetName (sn))
				return -1;
			if (LOWER (name[0]) == LOWER (GetName (sn)[0])
			  && !str_prefix (name, GetName (sn)))
				return sn;
		}
		return -1;
	}
	return sn;		// should never get here
}



// Return a skilltype pointer from either table based on sn			-Thoric
// Returns NULL if bad, unused or personal sn.
CSkill *CSkillTable::GetValidSkill (int sn)
{
	if (sn >= TYPE_PERSONAL)
		return NULL;

	if (sn >= TYPE_HERB)
		return HerbTable.IsValid (sn - TYPE_HERB) ?
			HerbTable.GetSkill (sn - TYPE_HERB) : NULL;

	if (sn >= TYPE_HIT)
		return NULL;

	return SkillTable.IsValid (sn) ? SkillTable.GetSkill (sn) : NULL;
}


// Perform a binary search on a section of the skill table	-Thoric
// Each different section of the skill table is sorted alphabetically
int CSkillTable::Bsearch (const char *name, int first, int top)
{
	int sn;

	for (;;) {
		sn = (first + top) >> 1;

		if (LOWER (name[0]) == LOWER (GetName (sn)[0])
		  && !str_prefix (name, GetName (sn)))
			return sn;
		if (first >= top)
			return -1;
		if (strcmp (name, GetName (sn)) < 1)
			top = sn - 1;
		else first = sn + 1;
	}
	return -1;
}


// Perform a binary search on a section of the skill table	-Thoric
// Each different section of the skill table is sorted alphabetically
// Check for exact matches only
int CSkillTable::BsearchExact (const char *name, int first, int top)
{
	int sn;

	for (;;) {
		sn = (first + top) >> 1;
		if (!str_prefix (name, GetName (sn)))
			return sn;
		if (first >= top)
			return -1;
		if (strcmp (name, GetName (sn)) < 1)
			top = sn - 1;
		else first = sn + 1;
	}
	return -1;
}


// Perform a binary search on a section of the skill table
// Each different section of the skill table is sorted alphabetically
// Only match skills player knows				-Thoric
int CSkillTable::ChBsearch (CCharacter *ch, const char *name, int first, int top)
{
	int sn;

	for (;;) {
		sn = (first + top) >> 1;

		if (LOWER (name[0]) == LOWER (GetName (sn)[0])
		  && !str_prefix (name, GetName (sn))
		  && ch->GetPcData ()->learned [sn] > 0
		  && ch->GetLevel () >= GetClassLevel (sn, ch->GetClass ()))
			return sn;
		if (first >= top)
			return -1;
		if (strcmp (name, GetName (sn)) < 1)
			top = sn - 1;
		else first = sn + 1;
	}
	return -1;
}





#define KEY2(literal,field,value)					\
				if (!str_cmp (word, literal)) {	\
				    field (value);					\
				    fMatch = TRUE;					\
				    break;							\
				}


void CSkill::Read (FILE *fp)
{
    char	*word, *pLine;
    BOOL	fMatch;

	SetGuild (-1);

    for (;;) {
		fMatch = FALSE;
		if (! feof (fp)) {
			pLine = fread_line (fp);
			word = ParseWord (pLine);
		}
		else word = "End";

		switch  (UPPER (word [0])) {
		  case '*':
			fMatch = TRUE;
			break;

		  case 'A':
			if (! str_cmp (word, "Affect")) {
				CSmaugAffect	*aff = new CSmaugAffect;
				aff->duration = str_dup (ParseWord (pLine));
				aff->location = ParseNumber (pLine);
				aff->modifier = str_dup (ParseWord (pLine));
				aff->bitvector = ParseNumber (pLine);
				aff->SetNext (GetAffects ());
				SetAffects (aff);
				fMatch = TRUE;
				break;
			}
			break;

		  case 'C':
			if (! str_cmp (word, "Class")) {
//				Class not used in skill.dat
				fMatch = TRUE;
				break;
			}
			if (! str_cmp (word, "Code")) {
				SPELL_FUN *spellfun = NULL;
				DO_FUN	  *dofun = NULL;
				char	  *w = ParseWord (pLine);
				
				fMatch = TRUE;
				spellfun = spell_function (w);
				if (spellfun != spell_notfound) {
					SetSpellFunction (spellfun);
					break;
				}

				dofun = skill_function (w);
				if (dofun != skill_notfound) {
					SetSkillFunction (dofun);
					break;
				}

				bug ("CSkill::Read: unknown skill/spell %s (%s)", w,
					GetName ());
				SetSpellFunction (spell_null);
				break;
			}
			KEY2("Code", SetSpellFunction, spell_function (ParseWord (pLine)));
			KEY2("Components",	SetComponents,	ParseStringNohash (pLine, fp));
			break;

		  case 'D':
			KEY2("Dammsg",	SetDamageMsg,	ParseStringNohash (pLine, fp));
			KEY2("Dice",	SetDiceRoll,	ParseStringNohash (pLine, fp));
			KEY2("Diechar",	SetDieCharMsg,	ParseStringNohash (pLine, fp));
			KEY2("Dieroom",	SetDieRoomMsg,	ParseStringNohash (pLine, fp));
			KEY2("Dievict",	SetDieVictMsg,	ParseStringNohash (pLine, fp));
			KEY2("Difficulty",	SetDifficulty,	ParseNumber (pLine));
			break;

		  case 'E':
			if (! str_cmp (word, "End"))
				return;
			break;
			
		  case 'F':
			KEY2("Flags",	SetFlags,		ParseNumber (pLine));
			break;

		  case 'G':
			KEY2("Guild",	SetGuild,		ParseNumber (pLine));
			break;

		  case 'H':
			KEY2("Hitchar",	SetHitCharMsg,	ParseStringNohash (pLine, fp));
			KEY2("Hitdest",	SetHitDest,		ParseStringNohash (pLine, fp));
			KEY2("Hitroom",	SetHitRoomMsg,	ParseStringNohash (pLine, fp));
			KEY2("Hitvict",	SetHitVictMsg,	ParseStringNohash (pLine, fp));
			break;

		  case 'I':
			KEY2("Immchar",	SetImmuneCharMsg, ParseStringNohash (pLine, fp));
			KEY2("Immroom",	SetImmuneRoomMsg, ParseStringNohash (pLine, fp));
			KEY2("Immvict",	SetImmuneVictMsg, ParseStringNohash (pLine, fp));
			KEY2("Info",	SetInfo,		  ParseNumber (pLine));
			break;

		  case 'M':
			KEY2("Mana",	SetMinMana,	ParseNumber (pLine));
			if (! str_cmp (word, "Minlevel")) {
				fMatch = TRUE;
				break;
			}
			KEY2("Minpos",	SetMinimumPosition,	ParseNumber (pLine));
			KEY2("Misschar", SetMissCharMsg,	ParseStringNohash (pLine, fp));
			KEY2("Missroom", SetMissRoomMsg,	ParseStringNohash (pLine, fp));
			KEY2("Missvict", SetMissVictMsg,	ParseStringNohash (pLine, fp));
			break;

		  case 'N':
			KEY2("Name",	SetName,		ParseStringNohash (pLine, fp));
			break;

		  case 'P':
			KEY2("Participants", SetParticipants,	ParseNumber (pLine));
			break;

		  case 'R':
			KEY2("Range",	SetRange,		ParseNumber (pLine));
			KEY2("Rounds",	SetBeats,		ParseNumber (pLine));
			break;

		  case 'S':
			KEY2("Slot",	SetSlot,		ParseNumber (pLine));
			KEY2("Saves",	SetSaves,		ParseNumber (pLine));
			break;

		  case 'T':
			KEY2("Target",		SetTarget,		ParseNumber (pLine));
			KEY2("Teachers",	SetTeachers,	ParseStringNohash (pLine, fp));
			KEY2("Type",		SetType,		get_skill (ParseWord (pLine)));
			break;

		  case 'V':
			KEY2("Value",		SetValue,		ParseNumber (pLine));
			break;

		  case 'W':
			KEY2("Wearoff",		SetOffMsg,		ParseStringNohash (pLine, fp));
			break;
		}

		if (!fMatch)
			bug ("CSkill::Read: no match: %s", word);
    }
}


// Write skill data to a file
void CSkill::Write (FILE *fp)
{
	CSmaugAffect *aff;

	fprintf (fp, "Name         %s~\n", GetName ());
	fprintf (fp, "Type         %s\n", skill_tname [GetType ()]);
	if (GetInfo ())
		fprintf (fp, "Info         %d\n", GetInfo ());
	fprintf (fp, "Flags        %d\n", GetFlags ());
	if (GetTarget ())
		fprintf (fp, "Target       %d\n", GetTarget ());
	if (GetMinimumPosition ())
		fprintf (fp, "Minpos       %d\n", GetMinimumPosition ());
	if (GetSaves ())
		fprintf (fp, "Saves        %d\n", GetSaves ());
	if (GetSlot ())
		fprintf (fp, "Slot         %d\n",	GetSlot ());
	if (GetMinMana ())
		fprintf (fp, "Mana         %d\n",	GetMinMana ());
	if (GetBeats ())
		fprintf (fp, "Rounds       %d\n", GetBeats ());
	if (GetGuild () != -1)
		fprintf (fp, "Guild        %d\n", GetGuild ());

	if (GetSkillFunction ())
		fprintf (fp, "Code         %s\n",	skill_name (GetSkillFunction ()));
	else if (GetSpellFunction ())
		fprintf (fp, "Code         %s\n", spell_name (GetSpellFunction ()));

	if (ValidDamageMsg ())
		fprintf (fp, "Dammsg       %s~\n",	GetDamageMsg ());
	if (ValidOffMsg ())
		fprintf (fp, "Wearoff      %s~\n", GetOffMsg ());
	if (ValidHitCharMsg ())
		fprintf (fp, "Hitchar      %s~\n", GetHitCharMsg ());
	if (ValidHitVictMsg ())
		fprintf (fp, "Hitvict      %s~\n", GetHitVictMsg ());
	if (ValidHitRoomMsg ())
		fprintf (fp, "Hitroom      %s~\n", GetHitRoomMsg ());
	if (ValidHitDest ())
		fprintf (fp, "Hitdest      %s~\n", GetHitDest ());

	if (ValidMissCharMsg ())
		fprintf (fp, "Misschar     %s~\n", GetMissCharMsg ());
	if (ValidMissVictMsg ())
		fprintf (fp, "Missvict     %s~\n", GetMissVictMsg ());
	if (ValidMissRoomMsg ())
		fprintf (fp, "Missroom     %s~\n", GetMissRoomMsg ());

	if (ValidDieCharMsg ())
		fprintf (fp, "Diechar      %s~\n", GetDieCharMsg ());
	if (ValidDieVictMsg ())
		fprintf (fp, "Dievict      %s~\n", GetDieVictMsg ());
	if (ValidDieRoomMsg ())
		fprintf (fp, "Dieroom      %s~\n", GetDieRoomMsg ());

	if (ValidImmuneCharMsg ())
		fprintf (fp, "Immchar      %s~\n", GetImmuneCharMsg ());
	if (ValidImmuneVictMsg ())
		fprintf (fp, "Immvict      %s~\n", GetImmuneVictMsg ());
	if (ValidImmuneRoomMsg ())
		fprintf (fp, "Immroom      %s~\n", GetImmuneRoomMsg ());
	if (HasTeachers ())
		fprintf (fp, "Teachers     %s~\n", GetTeachers ());

	if (GetDiceRoll () && GetDiceRoll ()[0] != '\0')
		fprintf (fp, "Dice         %s~\n", GetDiceRoll ());
	if (GetValue ())
		fprintf (fp, "Value        %d\n", GetValue ());
	if (GetDifficulty ())
		fprintf (fp, "Difficulty   %d\n", GetDifficulty ());
	if (GetParticipants ())
		fprintf (fp, "Participants %d\n", GetParticipants ());
	if (GetComponents () && GetComponents ()[0] != '\0')
		fprintf (fp, "Components   %s~\n", GetComponents ());

	for (aff = GetAffects (); aff; aff = aff->GetNext  ()) {
		fprintf (fp, "Affect       '%s' %d ", aff->duration, aff->location);
		int iMod = atoi (aff->modifier);
		if ((aff->location == APPLY_WEAPONSPELL
			|| aff->location == APPLY_WEARSPELL
			|| aff->location == APPLY_REMOVESPELL
			|| aff->location == APPLY_STRIPSN
			|| aff->location == APPLY_RECURRINGSPELL)
			&& SkillTable.IsValid (iMod))
				fprintf (fp, "'%d' ", SkillTable.GetSlot (iMod));
		else
			fprintf (fp, "'%s' ", aff->modifier);
		fprintf (fp, "%d\n", aff->bitvector);
	}
	
	if (GetType () != SKILL_HERB) {
		int y;
		int min = 1000;

		for  (y = 0; y < ClassTable.GetCount (); y++)
		if (GetClassLevel (y) < min)
			min = GetClassLevel (y);

		fprintf (fp, "Minlevel     %d\n", min);
	}
	fprintf (fp, "End\n\n");
}


void CSkillTable::RemoveAll ()
{
	for (int skill=0; skill < m_Top; ++skill) {
		delete m_sk [skill];
		m_sk [skill] = NULL;
	}
	m_Top = 0;
}


CSkill::~CSkill ()
{
    delete m_pName;
    delete m_pDamMsg;
    delete m_OffMsg;
	delete m_HitChar;
	delete m_HitVict;
	delete m_HitRoom;
	delete m_MissChar;
	delete m_MissVict;
	delete m_MissRoom;
	delete m_DieChar;
	delete m_DieVict;
	delete m_DieRoom;
	delete m_pDice;
	delete m_ImmChar;
	delete m_ImmVict;
	delete m_ImmRoom;
	delete m_pComponents;
	delete m_pTeachers;

	CSmaugAffect	*pNext, *pAff = m_pAffects;
	while (pAff) {
		pNext = pAff->GetNext ();
		delete pAff;
		pAff = pNext;
	}

	m_ClassLevel.RemoveAll ();
	m_ClassAdept.RemoveAll ();
	m_RaceLevel.RemoveAll ();
	m_RaceAdept.RemoveAll ();
}


DO_FUN *skill_function (char *name)
{
    switch (name[3])
    {
    case 'a':
	if (!str_cmp (name, "do_aassign"))		return do_aassign;
	if (!str_cmp (name, "do_advance"))		return do_advance;
	if (!str_cmp (name, "do_affected"))	return do_affected;
	if (!str_cmp (name, "do_afk"))			return do_afk;
	if (!str_cmp (name, "do_aid"))			return do_aid;
	if (!str_cmp (name, "do_allow"))		return do_allow;
	if (!str_cmp (name, "do_ansi"))		return do_ansi;
	if (!str_cmp (name, "do_answer"))		return do_answer;
	if (!str_cmp (name, "do_apply"))		return do_apply;
	if (!str_cmp (name, "do_appraise"))	return do_appraise;
	if (!str_cmp (name, "do_areas"))		return do_areas;
	if (!str_cmp (name, "do_aset"))		return do_aset;
	if (!str_cmp (name, "do_ask"))			return do_ask;
	if (!str_cmp (name, "do_astat"))		return do_astat;
	if (!str_cmp (name, "do_at"))			return do_at;
	if (!str_cmp (name, "do_atmob"))		return do_atmob;
	if (!str_cmp (name, "do_atobj"))		return do_atobj;
	if (!str_cmp (name, "do_auction"))		return do_auction;
	if (!str_cmp (name, "do_authorize"))	return do_authorize;
	if (!str_cmp (name, "do_avtalk"))		return do_avtalk;
	break;
    case 'b':
	if (!str_cmp (name, "do_backstab"))	return do_backstab;
	if (!str_cmp (name, "do_balzhur"))		return do_balzhur;
	if (!str_cmp (name, "do_bamfin"))		return do_bamfin;
	if (!str_cmp (name, "do_bamfout"))		return do_bamfout;
	if (!str_cmp (name, "do_ban"))			return do_ban;
	if (!str_cmp (name, "do_bash"))			return do_bash;
	if (!str_cmp (name, "do_bashdoor"))		return do_bashdoor;
	if (!str_cmp (name, "do_berserk"))		return do_berserk;
	if (!str_cmp (name, "do_bestow"))		return do_bestow;
	if (!str_cmp (name, "do_bestowarea"))	return do_bestowarea;
	if (!str_cmp (name, "do_bio"))			return do_bio;
	if (!str_cmp( name, "do_bite" ))		return do_bite;
	if (!str_cmp( name, "do_bloodlet"))		return do_bloodlet;
	if (!str_cmp (name, "do_boards"))		return do_boards;
	if (!str_cmp (name, "do_bodybag"))		return do_bodybag;
	if (!str_cmp (name, "do_brandish"))		return do_brandish;
	if (!str_cmp (name, "do_brew"))			return do_brew;
	if (!str_cmp (name, "do_broach"))		return do_broach;
	if (!str_cmp (name, "do_bset"))			return do_bset;
	if (!str_cmp (name, "do_bstat"))		return do_bstat;
	if (!str_cmp (name, "do_bug"))			return do_bug;
	if (!str_cmp (name, "do_bury"))			return do_bury;
	if (!str_cmp (name, "do_buy"))			return do_buy;
	break;
    case 'c':
	if (!str_cmp (name, "do_cast"))			return do_cast;
	if (!str_cmp (name, "do_cedit"))		return do_cedit;
	if (!str_cmp (name, "do_channels"))	return do_channels;
	if (!str_cmp (name, "do_chat"))		return do_chat;
	if (!str_cmp (name, "do_check_vnums"))	return do_check_vnums;
	if (!str_cmp (name, "do_circle"))		return do_circle;
	if (!str_cmp (name, "do_clans"))		return do_clans;
	if (!str_cmp (name, "do_clantalk"))	return do_clantalk;
	if (!str_cmp (name, "do_climb"))		return do_climb;
	if (!str_cmp (name, "do_close"))		return do_close;
	if (!str_cmp (name, "do_cmdtable"))	return do_cmdtable;
	if (!str_cmp (name, "do_cmenu"))		return do_cmenu;
	if (!str_cmp (name, "do_commands"))	return do_commands;
	if (!str_cmp (name, "do_comment"))		return do_comment;
	if (!str_cmp (name, "do_compare"))		return do_compare;
	if (!str_cmp (name, "do_config"))		return do_config;
	if (!str_cmp (name, "do_consider"))		return do_consider;
	if (!str_cmp( name, "do_cook"))			return do_cook;
	if (!str_cmp (name, "do_council_induct"))	return do_council_induct;
	if (!str_cmp (name, "do_council_outcast"))	return do_council_outcast;
	if (!str_cmp (name, "do_councils"))		return do_councils;
	if (!str_cmp (name, "do_counciltalk"))	return do_counciltalk;
	if (!str_cmp (name, "do_credits"))		return do_credits;
	if (!str_cmp (name, "do_cset"))		return do_cset;
	break;
    case 'd':
	if (!str_cmp (name, "do_deities"))		return do_deities;
	if (!str_cmp (name, "do_deny"))		return do_deny;
	if (!str_cmp (name, "do_description"))	return do_description;
	if (!str_cmp (name, "do_destro"))		return do_destro;
	if (!str_cmp (name, "do_destroy"))		return do_destroy;
	if (!str_cmp (name, "do_detrap"))		return do_detrap;
	if (!str_cmp (name, "do_devote"))		return do_devote;
	if (!str_cmp (name, "do_dig"))			return do_dig;
	if (!str_cmp (name, "do_disarm"))		return do_disarm;
	if (!str_cmp (name, "do_disconnect"))	return do_disconnect;
	if (!str_cmp (name, "do_dismiss"))		return do_dismiss;
	if (!str_cmp (name, "do_dismount"))	return do_dismount;
	if (!str_cmp (name, "do_dmesg"))		return do_dmesg;
	if (!str_cmp (name, "do_down"))		return do_down;
	if (!str_cmp (name, "do_drag"))		return do_drag;
	if (!str_cmp (name, "do_drink"))		return do_drink;
	if (!str_cmp (name, "do_drop"))			return do_drop;
	if (!str_cmp (name, "do_diagnose"))		return do_diagnose;
	break;
    case 'e':
	if (!str_cmp (name, "do_east"))			return do_east;
	if (!str_cmp (name, "do_eat"))			return do_eat;
	if (!str_cmp (name, "do_echo"))			return do_echo;
	if (!str_cmp (name, "do_elevate"))		return do_elevate;
	if (!str_cmp (name, "do_emote"))		return do_emote;
	if (!str_cmp (name, "do_empty"))		return do_empty;
	if (!str_cmp (name, "do_enter"))		return do_enter;
	if (!str_cmp (name, "do_equipment"))	return do_equipment;
	if (!str_cmp (name, "do_examine"))		return do_examine;
	if (!str_cmp (name, "do_exits"))		return do_exits;
	break;
    case 'f':
	if (!str_cmp (name, "do_feed"))			return do_feed;
	if (!str_cmp (name, "do_fill"))			return do_fill;
	if (!str_cmp (name, "do_fire"))			return do_fire;
	if (!str_cmp (name, "do_fixchar"))		return do_fixchar;
	if (!str_cmp (name, "do_flee"))			return do_flee;
	if (!str_cmp (name, "do_foldarea"))		return do_foldarea;
	if (!str_cmp (name, "do_follow"))		return do_follow;
	if (!str_cmp (name, "do_for"))			return do_for;
	if (!str_cmp (name, "do_force"))		return do_force;
	if (!str_cmp (name, "do_forceclose"))		return do_forceclose;
	if (!str_cmp (name, "do_form_password"))	return do_form_password;
	if (!str_cmp (name, "do_fprompt"))		return do_fprompt;
	if (!str_cmp (name, "do_fquit"))		return do_fquit;
	if (!str_cmp (name, "do_freeze"))		return do_freeze;
	break;
    case 'g':
	if (!str_cmp (name, "do_get"))			return do_get;
	if (!str_cmp (name, "do_gfighting"))	return do_gfighting;
	if (!str_cmp (name, "do_give"))			return do_give;
	if (!str_cmp (name, "do_glance"))		return do_glance;
        if (!str_cmp (name, "do_gold"))		return do_gold;
	if (!str_cmp (name, "do_goto"))			return do_goto;
	if (!str_cmp (name, "do_gouge"))		return do_gouge;
	if (!str_cmp (name, "do_group"))		return do_group;
	if (!str_cmp (name, "do_grub"))			return do_grub;
	if (!str_cmp (name, "do_gtell"))		return do_gtell;
	if (!str_cmp (name, "do_guilds"))		return do_guilds;
	if (!str_cmp (name, "do_guildtalk"))	return do_guildtalk;
	if (!str_cmp (name, "do_gwhere"))		return do_gwhere;
	break;
    case 'h':
	if (!str_cmp (name, "do_hedit"))		return do_hedit;
	if (!str_cmp (name, "do_hell"))		return do_hell;
	if (!str_cmp (name, "do_help"))		return do_help;
	if (!str_cmp (name, "do_hide"))		return do_hide;
	if (!str_cmp (name, "do_hitall"))		return do_hitall;
	if (!str_cmp (name, "do_hlist"))		return do_hlist;
	if (!str_cmp (name, "do_holylight"))	return do_holylight;
	if (!str_cmp (name, "do_homepage"))	return do_homepage;
	if (!str_cmp (name, "do_hset"))		return do_hset;
	break;
    case 'i':
	if (!str_cmp (name, "do_ide"))			return do_ide;
	if (!str_cmp (name, "do_idea"))			return do_idea;
	if (!str_cmp (name, "do_ignore"))		return do_ignore;
	if (!str_cmp (name, "do_immortalize"))	return do_immortalize;
	if (!str_cmp (name, "do_immtalk"))		return do_immtalk;
	if (!str_cmp (name, "do_induct"))		return do_induct;
	if (!str_cmp (name, "do_installarea"))	return do_installarea;
	if (!str_cmp (name, "do_instaroom"))	return do_instaroom;
	if (!str_cmp (name, "do_instazone"))	return do_instazone;
	if (!str_cmp (name, "do_inventory"))	return do_inventory;
	if (!str_cmp (name, "do_invis"))		return do_invis;
	break;
    case 'k':
	if (!str_cmp (name, "do_kick"))		return do_kick;
	if (!str_cmp (name, "do_kill"))		return do_kill;
	break;
    case 'l':
	if (!str_cmp (name, "do_languages"))	return do_languages;
	if (!str_cmp (name, "do_last"))		return do_last;
	if (!str_cmp (name, "do_leave"))		return do_leave;
	if (!str_cmp (name, "do_level"))		return do_level;
	if (!str_cmp (name, "do_light"))		return do_light;
	if (!str_cmp (name, "do_list"))		return do_list;
	if (!str_cmp (name, "do_litterbug"))	return do_litterbug;
	if (!str_cmp (name, "do_loadarea"))	return do_loadarea;
	if (!str_cmp (name, "do_loadup"))		return do_loadup;
	if (!str_cmp (name, "do_lock"))		return do_lock;
	if (!str_cmp (name, "do_log"))			return do_log;
	if (!str_cmp (name, "do_look"))		return do_look;
	if (!str_cmp (name, "do_low_purge"))	return do_low_purge;
	break;
    case 'm':
	if (!str_cmp (name, "do_mailroom"))	return do_mailroom;
	if (!str_cmp (name, "do_make"))		return do_make;
	if (!str_cmp (name, "do_makeboard"))	return do_makeboard;
	if (!str_cmp (name, "do_makeclan"))	return do_makeclan;
	if (!str_cmp (name, "do_makecouncil"))	return do_makecouncil;
	if (!str_cmp (name, "do_makedeity"))	return do_makedeity;
	if (!str_cmp (name, "do_makeguild"))	return do_makeguild;
	if (!str_cmp (name, "do_makeorder"))	return do_makeorder;
	if (!str_cmp (name, "do_makerepair"))	return do_makerepair;
	if (!str_cmp (name, "do_makeshop"))	return do_makeshop;
	if (!str_cmp (name, "do_makewizlist"))	return do_makewizlist;
	if (!str_cmp (name, "do_massign"))		return do_massign;
	if (!str_cmp (name, "do_mcreate"))		return do_mcreate;
	if (!str_cmp (name, "do_mdelete"))		return do_mdelete;
	if (!str_cmp (name, "do_memory"))		return do_memory;
	if (!str_cmp (name, "do_mfind"))		return do_mfind;
	if (!str_cmp (name, "do_minvoke"))		return do_minvoke;
	if (!str_cmp (name, "do_mistwalk"))		return do_mistwalk;
	if (!str_cmp (name, "do_mlist"))		return do_mlist;
	if (!str_cmp (name, "do_mmenu"))		return do_mmenu;
	if (!str_cmp (name, "do_mount"))			return do_mount;
	if (!str_cmp (name, "do_mp_close_passage"))	return do_mp_close_passage;
	if (!str_cmp (name, "do_mp_damage"))		return do_mp_damage;
	if (!str_cmp (name, "do_mp_deposit"))		return do_mp_deposit;
	if (!str_cmp (name, "do_mp_open_passage"))	return do_mp_open_passage;
	if (!str_cmp (name, "do_mp_practice"))		return do_mp_practice;
	if (!str_cmp (name, "do_mp_restore"))	return do_mp_restore;
	if (!str_cmp (name, "do_mp_slay"))		return do_mp_slay;
	if (!str_cmp (name, "do_mp_withdraw"))	return do_mp_withdraw;
	if (!str_cmp (name, "do_mpadvance"))	return do_mpadvance;
	if (!str_cmp (name, "do_mpapply"))		return do_mpapply;
	if (!str_cmp (name, "do_mpapplyb"))		return do_mpapplyb;
	if (!str_cmp (name, "do_mpasound"))		return do_mpasound;
	if (!str_cmp (name, "do_mpasupress"))	return do_mpasupress;
	if (!str_cmp (name, "do_mpat"))			return do_mpat;
	if (!str_cmp (name, "do_mpbodybag"))	return do_mpbodybag;
	if (!str_cmp (name, "do_mpdream"))		return do_mpdream;
	if (!str_cmp (name, "do_mpecho"))		return do_mpecho;
	if (!str_cmp (name, "do_mpechoaround"))	return do_mpechoaround;
	if (!str_cmp (name, "do_mpechoat"))		return do_mpechoat;
	if (!str_cmp (name, "do_mpedit"))		return do_mpedit;
	if (!str_cmp (name, "do_mpfavor"))		return do_mpfavor;
	if (!str_cmp (name, "do_mpforce"))		return do_mpforce;
	if (!str_cmp (name, "do_mpgoto"))		return do_mpgoto;
	if (!str_cmp (name, "do_mpinvis"))		return do_mpinvis;
	if (!str_cmp (name, "do_mpjunk"))		return do_mpjunk;
	if (!str_cmp (name, "do_mpkill"))		return do_mpkill;
	if (!str_cmp (name, "do_mpmload"))		return do_mpmload;
	if (!str_cmp (name, "do_mpmset"))		return do_mpmset;
	if (!str_cmp (name, "do_mpnothing"))	return do_mpnothing;
	if (!str_cmp (name, "do_mpoload"))		return do_mpoload;
	if (!str_cmp (name, "do_mposet"))		return do_mposet;
	if (!str_cmp (name, "do_mppardon"))		return do_mppardon;
	if (!str_cmp (name, "do_mppkset"))		return do_mppkset;
	if (!str_cmp (name, "do_mppurge"))		return do_mppurge;
	if (!str_cmp (name, "do_mpstat"))		return do_mpstat;
	if (!str_cmp (name, "do_mptransfer"))	return do_mptransfer;
	if (!str_cmp (name, "do_mrange"))		return do_mrange;
	if (!str_cmp (name, "do_mset"))			return do_mset;
	if (!str_cmp (name, "do_mstat"))		return do_mstat;
	if (!str_cmp (name, "do_murde"))		return do_murde;
	if (!str_cmp (name, "do_murder"))		return do_murder;
	if (!str_cmp (name, "do_muse"))			return do_muse;
	if (!str_cmp (name, "do_music"))		return do_music;
	if (!str_cmp (name, "do_mwhere"))		return do_mwhere;
	break;
    case 'n':
	if (!str_cmp (name, "do_name"))			return do_name;
	if (!str_cmp (name, "do_newbiechat"))	return do_newbiechat;
	if (!str_cmp (name, "do_newbieset"))	return do_newbieset;
	if (!str_cmp (name, "do_newzones"))		return do_newzones;
	if (!str_cmp (name, "do_noemote"))		return do_noemote;
	if (!str_cmp (name, "do_noresolve"))	return do_noresolve;
	if (!str_cmp (name, "do_north"))		return do_north;
	if (!str_cmp (name, "do_northeast"))	return do_northeast;
	if (!str_cmp (name, "do_northwest"))	return do_northwest;
	if (!str_cmp (name, "do_notell"))		return do_notell;
	if (!str_cmp (name, "do_notitle"))		return do_notitle;
	if (!str_cmp (name, "do_noteroom"))		return do_noteroom;
	break;
    case 'o':
	if (!str_cmp (name, "do_oassign"))		return do_oassign;
	if (!str_cmp (name, "do_ocreate"))		return do_ocreate;
	if (!str_cmp (name, "do_odelete"))		return do_odelete;
	if (!str_cmp (name, "do_ofind"))		return do_ofind;
	if (!str_cmp (name, "do_ogrub"))		return do_ogrub;
	if (!str_cmp (name, "do_oinvoke"))		return do_oinvoke;
	if (!str_cmp (name, "do_oldscore"))		return do_oldscore;
	if (!str_cmp (name, "do_olist"))		return do_olist;
	if (!str_cmp (name, "do_omenu"))		return do_omenu;
	if (!str_cmp (name, "do_opedit"))		return do_opedit;
	if (!str_cmp (name, "do_open"))			return do_open;
/*	if (!str_cmp (name, "do_opentourney"))	return do_opentourney; */
	if (!str_cmp (name, "do_opstat"))		return do_opstat;
	if (!str_cmp (name, "do_orange"))		return do_orange;
	if (!str_cmp (name, "do_order"))		return do_order;
	if (!str_cmp (name, "do_orders"))		return do_orders;
	if (!str_cmp (name, "do_ordertalk"))	return do_ordertalk;
	if (!str_cmp (name, "do_oset"))			return do_oset;
	if (!str_cmp (name, "do_ostat"))		return do_ostat;
	if (!str_cmp (name, "do_outcast"))		return do_outcast;
	if (!str_cmp (name, "do_owhere"))		return do_owhere;
	break;
    case 'p':
	if (!str_cmp (name, "do_pager"))		return do_pager;
	if (!str_cmp (name, "do_pagelen"))		return do_pagelen;
	if (!str_cmp (name, "do_pardon"))		return do_pardon;
	if (!str_cmp (name, "do_password"))		return do_password;
	if (!str_cmp (name, "do_pcrename"))		return do_pcrename;
	if (!str_cmp (name, "do_peace"))		return do_peace;
	if (!str_cmp (name, "do_pick"))			return do_pick;
	if (!str_cmp (name, "do_pmenu"))		return do_pmenu;
	if (!str_cmp (name, "do_poison_weapon"))	return do_poison_weapon;
	if (!str_cmp (name, "do_practice"))		return do_practice;
	if (!str_cmp (name, "do_prompt"))		return do_prompt;
	if (!str_cmp (name, "do_pull"))			return do_pull;
	if (!str_cmp (name, "do_punch"))		return do_punch;
	if (!str_cmp (name, "do_purge"))		return do_purge;
	if (!str_cmp (name, "do_push"))			return do_push;
	if (!str_cmp (name, "do_put"))			return do_put;
	break;
    case 'q':
	if (!str_cmp (name, "do_qpset"))		return do_qpset;
	if (!str_cmp (name, "do_quaff"))		return do_quaff;
	if (!str_cmp (name, "do_quest"))		return do_quest;
	if (!str_cmp (name, "do_qui"))			return do_qui;
	if (!str_cmp (name, "do_quit"))			return do_quit;
	break;
    case 'r':
	if (!str_cmp (name, "do_racetalk"))		return do_racetalk;
	if (!str_cmp (name, "do_rank"))			return do_rank;
	if (!str_cmp (name, "do_rap"))			return do_rap;
	if (!str_cmp (name, "do_rassign"))		return do_rassign;
	if (!str_cmp (name, "do_rat"))			return do_rat;
	if (!str_cmp (name, "do_rdelete"))		return do_rdelete;
	if (!str_cmp (name, "do_reboo"))		return do_reboo;
	if (!str_cmp (name, "do_reboot"))		return do_reboot;
	if (!str_cmp (name, "do_recho"))		return do_recho;
	if (!str_cmp (name, "do_recite"))		return do_recite;
	if (!str_cmp (name, "do_redit"))		return do_redit;
	if (!str_cmp (name, "do_regoto"))		return do_regoto;
	if (!str_cmp (name, "do_remains"))		return do_remains;
	if (!str_cmp (name, "do_remove"))		return do_remove;
	if (!str_cmp (name, "do_rent"))			return do_rent;
	if (!str_cmp (name, "do_repair"))		return do_repair;
	if (!str_cmp (name, "do_repairset"))	return do_repairset;
	if (!str_cmp (name, "do_repairshops"))	return do_repairshops;
	if (!str_cmp (name, "do_repairstat"))	return do_repairstat;
	if (!str_cmp (name, "do_reply"))		return do_reply;
	if (!str_cmp (name, "do_report"))		return do_report;
	if (!str_cmp (name, "do_rescue"))		return do_rescue;
	if (!str_cmp (name, "do_reset"))		return do_reset;
	if (!str_cmp (name, "do_reserve"))		return do_reserve;
	if (!str_cmp (name, "do_rest"))			return do_rest;
	if (!str_cmp (name, "do_restore"))		return do_restore;
	if (!str_cmp (name, "do_restoretime"))	return do_restoretime;
	if (!str_cmp (name, "do_restrict"))		return do_restrict;
	if (!str_cmp (name, "do_retire"))		return do_retire;
	if (!str_cmp (name, "do_retran"))		return do_retran;
	if (!str_cmp (name, "do_return"))		return do_return;
	if (!str_cmp (name, "do_revert"))		return do_revert;
	if (!str_cmp (name, "do_rip"))			return do_rip;
	if (!str_cmp (name, "do_rlist"))		return do_rlist;
	if (!str_cmp (name, "do_rmenu"))		return do_rmenu;
	if (!str_cmp (name, "do_rpedit"))		return do_rpedit;
	if (!str_cmp (name, "do_rpstat"))		return do_rpstat;
	if (!str_cmp (name, "do_rreset"))		return do_rreset;
	if (!str_cmp (name, "do_rset"))			return do_rset;
	if (!str_cmp (name, "do_rstat"))		return do_rstat;
	break;
    case 's':
	if (!str_cmp (name, "do_sacrifice"))	return do_sacrifice;
	if (!str_cmp (name, "do_save"))			return do_save;
	if (!str_cmp (name, "do_savearea"))		return do_savearea;
	if (!str_cmp (name, "do_say"))			return do_say;
	if (!str_cmp (name, "do_scan"))			return do_scan;
	if (!str_cmp (name, "do_score"))		return do_score;
	if (!str_cmp (name, "do_scribe"))		return do_scribe;
	if (!str_cmp (name, "do_search"))		return do_search;
	if (!str_cmp (name, "do_sedit"))		return do_sedit;
	if (!str_cmp (name, "do_sell"))			return do_sell;
	if (!str_cmp (name, "do_set_boot_time"))	return do_set_boot_time;
	if (!str_cmp (name, "do_setclan"))		return do_setclan;
	if (!str_cmp (name, "do_setclass"))		return do_setclass;
	if (!str_cmp (name, "do_setcouncil"))	return do_setcouncil;
	if (!str_cmp (name, "do_setdeity"))		return do_setdeity;
	if (!str_cmp (name, "do_setguild"))		return do_setguild;
	if (!str_cmp (name, "do_setorder"))		return do_setorder;
	if (!str_cmp (name, "do_setrace"))		return do_setrace;
	if (!str_cmp (name, "do_shops"))		return do_shops;
	if (!str_cmp (name, "do_shopset"))		return do_shopset;
	if (!str_cmp (name, "do_shopstat"))		return do_shopstat;
	if (!str_cmp (name, "do_shout"))		return do_shout;
	if (!str_cmp (name, "do_shove"))		return do_shove;
	if (!str_cmp (name, "do_showclan"))		return do_showclan;
	if (!str_cmp (name, "do_showclass"))	return do_showclass;
	if (!str_cmp (name, "do_showcouncil"))	return do_showcouncil;
	if (!str_cmp (name, "do_showdeity"))	return do_showdeity;
	if (!str_cmp (name, "do_showguild"))	return do_showguild;
	if (!str_cmp (name, "do_showorder"))	return do_showorder;
	if (!str_cmp (name, "do_showrace"))		return do_showrace;
	if (!str_cmp (name, "do_showbytes"))	return do_showbytes;
	if (!str_cmp (name, "do_shutdow"))		return do_shutdow;
	if (!str_cmp (name, "do_shutdown"))		return do_shutdown;
	if (!str_cmp (name, "do_silence"))		return do_silence;
	if (!str_cmp (name, "do_sit"))			return do_sit;
	if (!str_cmp (name, "do_sla"))			return do_sla;
	if (!str_cmp (name, "do_slay"))			return do_slay;
	if (!str_cmp (name, "do_sleep"))		return do_sleep;
	if (!str_cmp (name, "do_slice"))		return do_slice;
	if (!str_cmp (name, "do_slist"))		return do_slist;
	if (!str_cmp (name, "do_slookup"))		return do_slookup;
	if (!str_cmp (name, "do_smoke"))		return do_smoke;
	if (!str_cmp (name, "do_snoop"))		return do_snoop;
	if (!str_cmp (name, "do_sober"))		return do_sober;
	if (!str_cmp (name, "do_socials"))		return do_socials;
	if (!str_cmp (name, "do_south"))		return do_south;
	if (!str_cmp (name, "do_southeast"))	return do_southeast;
	if (!str_cmp (name, "do_southwest"))	return do_southwest;
	if (!str_cmp (name, "do_speak"))		return do_speak;
	if (!str_cmp (name, "do_split"))		return do_split;
	if (!str_cmp (name, "do_sset"))			return do_sset;
	if (!str_cmp (name, "do_stand"))		return do_stand;
	if (!str_cmp (name, "do_stat"))			return do_stat;
	if (!str_cmp (name, "do_statreport"))	return do_statreport;
	if (!str_cmp (name, "do_steal"))		return do_steal;
	if (!str_cmp (name, "do_stun"))			return do_stun;
	if (!str_cmp (name, "do_style"))		return do_style;
	if (!str_cmp (name, "do_supplicate"))	return do_supplicate;
	if (!str_cmp (name, "do_switch"))		return do_switch;
	break;
    case 't':
	if (!str_cmp (name, "do_tamp"))			return do_tamp;
	if (!str_cmp (name, "do_tell"))			return do_tell;
	if (!str_cmp (name, "do_think"))		return do_think;
	if (!str_cmp (name, "do_time"))			return do_time;
	if (!str_cmp (name, "do_timecmd"))		return do_timecmd;
	if (!str_cmp (name, "do_title"))		return do_title;
	if (!str_cmp (name, "do_track"))		return do_track;
	if (!str_cmp (name, "do_transfer"))		return do_transfer;
	if (!str_cmp (name, "do_trust"))		return do_trust;
	if (!str_cmp (name, "do_typo"))			return do_typo;
	break;
    case 'u':
	if (!str_cmp (name, "do_unfoldarea"))	return do_unfoldarea;
	if (!str_cmp (name, "do_unhell"))		return do_unhell;
	if (!str_cmp (name, "do_unlock"))		return do_unlock;
        if (!str_cmp (name, "do_unsilence"))	return do_unsilence;
	if (!str_cmp (name, "do_up"))			return do_up;
	if (!str_cmp (name, "do_users"))		return do_users;
	break;
    case 'v':
	if (!str_cmp (name, "do_value"))		return do_value;
	if (!str_cmp (name, "do_versio"))		return do_versio;
	if (!str_cmp (name, "do_version"))		return do_version;
	if (!str_cmp (name, "do_visible"))		return do_visible;
	if (!str_cmp (name, "do_vnums"))		return do_vnums;
	if (!str_cmp (name, "do_vsearch"))		return do_vsearch;
	break;
    case 'w':
	if (!str_cmp (name, "do_wake"))			return do_wake;
	if (!str_cmp (name, "do_wartalk"))		return do_wartalk;
	if (!str_cmp (name, "do_wear"))			return do_wear;
	if (!str_cmp (name, "do_weather"))		return do_weather;
	if (!str_cmp (name, "do_west"))			return do_west;
	if (!str_cmp (name, "do_where"))		return do_where;
	if (!str_cmp (name, "do_who"))			return do_who;
	if (!str_cmp (name, "do_whois"))		return do_whois;
	if (!str_cmp (name, "do_wimpy"))		return do_wimpy;
	if (!str_cmp (name, "do_wizhelp"))		return do_wizhelp;
	if (!str_cmp (name, "do_wizlist"))		return do_wizlist;
	if (!str_cmp (name, "do_wizlock"))		return do_wizlock;
	break;
    case 'y':
	if (!str_cmp (name, "do_yell"))		return do_yell;
	break;
    case 'z':
	if (!str_cmp (name, "do_zap"))			return do_zap;
	if (!str_cmp (name, "do_zones"))		return do_zones;
    }

	return skill_notfound;
}


char *skill_name (DO_FUN *skill)
{
	if (skill == do_aassign)		return "do_aassign";
	if (skill == do_advance)		return "do_advance";
	if (skill == do_affected)		return "do_affected";
	if (skill == do_afk)			return "do_afk";
	if (skill == do_aid)			return "do_aid";
	if (skill == do_allow)			return "do_allow";
	if (skill == do_ansi)			return "do_ansi";
	if (skill == do_answer)			return "do_answer";
	if (skill == do_apply)			return "do_apply";
	if (skill == do_appraise)		return "do_appraise";
	if (skill == do_areas)			return "do_areas";
	if (skill == do_aset)			return "do_aset";
	if (skill == do_ask)			return "do_ask";
	if (skill == do_astat)			return "do_astat";
	if (skill == do_at)				return "do_at";
	if (skill == do_atmob)			return "do_atmob";
	if (skill == do_atobj)			return "do_atobj";
	if (skill == do_auction)		return "do_auction";
	if (skill == do_authorize)		return "do_authorize";
	if (skill == do_avtalk)			return "do_avtalk";
	if (skill == do_backstab)		return "do_backstab";
	if (skill == do_balzhur)		return "do_balzhur";
	if (skill == do_bamfin)			return "do_bamfin";
	if (skill == do_bamfout)		return "do_bamfout";
	if (skill == do_ban)			return "do_ban";
	if (skill == do_bash)			return "do_bash";
	if (skill == do_bashdoor)		return "do_bashdoor";
	if (skill == do_berserk)		return "do_berserk";
	if (skill == do_bestow)			return "do_bestow";
	if (skill == do_bestowarea)		return "do_bestowarea";
	if (skill == do_bio)			return "do_bio";
	if (skill == do_bite)			return "do_bite";
	if (skill == do_bloodlet)		return "do_bloodlet";
	if (skill == do_boards)			return "do_boards";
	if (skill == do_bodybag)		return "do_bodybag";
	if (skill == do_brandish)		return "do_brandish";
	if (skill == do_brew)			return "do_brew";
	if (skill == do_broach)			return "do_broach";
	if (skill == do_bset)			return "do_bset";
	if (skill == do_bstat)			return "do_bstat";
	if (skill == do_bug)			return "do_bug";
	if (skill == do_bury)			return "do_bury";
	if (skill == do_buy)			return "do_buy";
	if (skill == do_cast)			return "do_cast";
	if (skill == do_cedit)			return "do_cedit";
	if (skill == do_channels)		return "do_channels";
	if (skill == do_chat)			return "do_chat";
	if (skill == do_check_vnums)	return "do_check_vnums";
	if (skill == do_circle)			return "do_circle";
	if (skill == do_clans)			return "do_clans";
	if (skill == do_clantalk)		return "do_clantalk";
	if (skill == do_climb)			return "do_climb";
	if (skill == do_close)			return "do_close";
	if (skill == do_cmdtable)		return "do_cmdtable";
	if (skill == do_cmenu)			return "do_cmenu";
	if (skill == do_commands)		return "do_commands";
	if (skill == do_comment)		return "do_comment";
	if (skill == do_compare)		return "do_compare";
	if (skill == do_config)			return "do_config";
	if (skill == do_consider)		return "do_consider";
	if (skill == do_cook)			return "do_cook";
	if (skill == do_council_induct)		return "do_council_induct";
	if (skill == do_council_outcast)	return "do_council_outcast";
	if (skill == do_councils)			return "do_councils";
	if (skill == do_counciltalk)	return "do_counciltalk";
	if (skill == do_credits)		return "do_credits";
	if (skill == do_cset)			return "do_cset";
	if (skill == do_deities)		return "do_deities";
	if (skill == do_deny)			return "do_deny";
	if (skill == do_description)	return "do_description";
	if (skill == do_destro)			return "do_destro";
	if (skill == do_destroy)		return "do_destroy";
	if (skill == do_detrap)			return "do_detrap";
	if (skill == do_devote)			return "do_devote";
	if (skill == do_dig)			return "do_dig";
	if (skill == do_disarm)			return "do_disarm";
	if (skill == do_disconnect)		return "do_disconnect";
	if (skill == do_dismiss)		return "do_dismiss";
	if (skill == do_dismount)		return "do_dismount";
	if (skill == do_dmesg)			return "do_dmesg";
	if (skill == do_down)			return "do_down";
	if (skill == do_drag)			return "do_drag";
	if (skill == do_drink)			return "do_drink";
	if (skill == do_drop)			return "do_drop";
	if (skill == do_diagnose)		return "do_diagnose";
	if (skill == do_east)			return "do_east";
	if (skill == do_eat)			return "do_eat";
	if (skill == do_echo)			return "do_echo";
	if (skill == do_elevate)		return "do_elevate";
	if (skill == do_emote)			return "do_emote";
	if (skill == do_empty)			return "do_empty";
	if (skill == do_enter)			return "do_enter";
	if (skill == do_equipment)		return "do_equipment";
	if (skill == do_examine)		return "do_examine";
	if (skill == do_exits)			return "do_exits";
	if (skill == do_feed)			return "do_feed";
	if (skill == do_fill)			return "do_fill";
	if (skill == do_fire)			return "do_fire";
	if (skill == do_fixchar)		return "do_fixchar";
	if (skill == do_flee)			return "do_flee";
	if (skill == do_foldarea)		return "do_foldarea";
	if (skill == do_follow)			return "do_follow";
	if (skill == do_for)			return "do_for";
	if (skill == do_force)			return "do_force";
	if (skill == do_forceclose)		return "do_forceclose";
	if (skill == do_form_password)	return "do_form_password";
	if (skill == do_fprompt)		return "do_fprompt";
	if (skill == do_fquit)			return "do_fquit";
	if (skill == do_freeze)			return "do_freeze";
	if (skill == do_gfighting)		return "do_gfighting";
	if (skill == do_get)			return "do_get";
	if (skill == do_give)			return "do_give";
	if (skill == do_glance)			return "do_glance";
	if (skill == do_gold)			return "do_gold";
	if (skill == do_goto)			return "do_goto";
	if (skill == do_gouge)			return "do_gouge";
	if (skill == do_group)			return "do_group";
	if (skill == do_grub)			return "do_grub";
	if (skill == do_gtell)			return "do_gtell";
	if (skill == do_guilds)			return "do_guilds";
	if (skill == do_guildtalk)		return "do_guildtalk";
	if (skill == do_gwhere)			return "do_gwhere";
	if (skill == do_hedit)			return "do_hedit";
	if (skill == do_hell)			return "do_hell";
	if (skill == do_help)			return "do_help";
	if (skill == do_hide)			return "do_hide";
	if (skill == do_hitall)			return "do_hitall";
	if (skill == do_hlist)			return "do_hlist";
	if (skill == do_holylight)		return "do_holylight";
	if (skill == do_homepage)		return "do_homepage";
	if (skill == do_hset)			return "do_hset";
	if (skill == do_ide)			return "do_ide";
	if (skill == do_idea)			return "do_idea";
	if (skill == do_ignore)			return "do_ignore";
	if (skill == do_immortalize)	return "do_immortalize";
	if (skill == do_immtalk)		return "do_immtalk";
	if (skill == do_induct)			return "do_induct";
	if (skill == do_installarea)	return "do_installarea";
	if (skill == do_instaroom)		return "do_instaroom";
	if (skill == do_instazone)		return "do_instazone";
	if (skill == do_inventory)		return "do_inventory";
	if (skill == do_invis)			return "do_invis";
	if (skill == do_kick)			return "do_kick";
	if (skill == do_kill)			return "do_kill";
	if (skill == do_languages)		return "do_languages";
	if (skill == do_last)			return "do_last";
	if (skill == do_leave)			return "do_leave";
	if (skill == do_level)			return "do_level";
	if (skill == do_light)			return "do_light";
	if (skill == do_list)			return "do_list";
	if (skill == do_litterbug)		return "do_litterbug";
	if (skill == do_loadarea)		return "do_loadarea";
	if (skill == do_loadup)			return "do_loadup";
	if (skill == do_lock)			return "do_lock";
	if (skill == do_log)			return "do_log";
	if (skill == do_look)			return "do_look";
	if (skill == do_low_purge)		return "do_low_purge";
	if (skill == do_mailroom)		return "do_mailroom";
	if (skill == do_make)			return "do_make";
	if (skill == do_makeboard)		return "do_makeboard";
	if (skill == do_makeclan)		return "do_makeclan";
	if (skill == do_makecouncil)	return "do_makecouncil";
	if (skill == do_makedeity)		return "do_makedeity";
	if (skill == do_makeguild)		return "do_makeguild";
	if (skill == do_makeorder)		return "do_makeorder";
	if (skill == do_makerepair)		return "do_makerepair";
	if (skill == do_makeshop)		return "do_makeshop";
	if (skill == do_makewizlist)	return "do_makewizlist";
	if (skill == do_massign)		return "do_massign";
	if (skill == do_mcreate)		return "do_mcreate";
	if (skill == do_mdelete)		return "do_mdelete";
	if (skill == do_memory)			return "do_memory";
	if (skill == do_mfind)			return "do_mfind";
	if (skill == do_minvoke)		return "do_minvoke";
	if (skill == do_mistwalk)		return "do_mistwalk";
	if (skill == do_mlist)			return "do_mlist";
	if (skill == do_mmenu)			return "do_mmenu";
	if (skill == do_mount)				return "do_mount";
	if (skill == do_mp_close_passage)	return "do_mp_close_passage";
	if (skill == do_mp_damage)			return "do_mp_damage";
	if (skill == do_mp_deposit)			return "do_mp_deposit";
	if (skill == do_mp_open_passage)	return "do_mp_open_passage";
	if (skill == do_mp_practice)		return "do_mp_practice";
	if (skill == do_mp_restore)		return "do_mp_restore";
	if (skill == do_mp_slay)		return "do_mp_slay";
	if (skill == do_mp_withdraw)	return "do_mp_withdraw";
	if (skill == do_mpadvance)		return "do_mpadvance";
	if (skill == do_mpapply)		return "do_mpapply";
	if (skill == do_mpapplyb)		return "do_mpapplyb";
	if (skill == do_mpasound)		return "do_mpasound";
	if (skill == do_mpasupress)		return "do_mpasupress";
	if (skill == do_mpat)			return "do_mpat";
	if (skill == do_mpbodybag)		return "do_mpbodybag";
	if (skill == do_mpdream)		return "do_mpdream";
	if (skill == do_mpecho)			return "do_mpecho";
	if (skill == do_mpechoaround)	return "do_mpechoaround";
	if (skill == do_mpechoat)		return "do_mpechoat";
	if (skill == do_mpedit)			return "do_mpedit";
	if (skill == do_mpfavor)		return "do_mpfavor";
	if (skill == do_mpforce)		return "do_mpforce";
	if (skill == do_mpgoto)			return "do_mpgoto";
	if (skill == do_mpinvis)		return "do_mpinvis";
	if (skill == do_mpjunk)			return "do_mpjunk";
	if (skill == do_mpkill)			return "do_mpkill";
	if (skill == do_mpmload)		return "do_mpmload";
	if (skill == do_mpmset)			return "do_mpmset";
	if (skill == do_mpnothing)		return "do_mpnothing";
	if (skill == do_mpoload)		return "do_mpoload";
	if (skill == do_mposet)			return "do_mposet";
	if (skill == do_mppardon)		return "do_mppardon";
	if (skill == do_mppkset)		return "do_mppkset";
	if (skill == do_mppurge)		return "do_mppurge";
	if (skill == do_mpstat)			return "do_mpstat";
	if (skill == do_mptransfer)		return "do_mptransfer";
	if (skill == do_mrange)			return "do_mrange";
	if (skill == do_mset)			return "do_mset";
	if (skill == do_mstat)			return "do_mstat";
	if (skill == do_murde)			return "do_murde";
	if (skill == do_murder)			return "do_murder";
	if (skill == do_muse)			return "do_muse";
	if (skill == do_music)			return "do_music";
	if (skill == do_mwhere)			return "do_mwhere";
	if (skill == do_name)			return "do_name";
	if (skill == do_newbiechat)		return "do_newbiechat";
	if (skill == do_newbieset)		return "do_newbieset";
	if (skill == do_newzones)		return "do_newzones";
	if (skill == do_noemote)		return "do_noemote";
	if (skill == do_noresolve)		return "do_noresolve";
	if (skill == do_north)			return "do_north";
	if (skill == do_northeast)		return "do_northeast";
	if (skill == do_northwest)		return "do_northwest";
	if (skill == do_notell)			return "do_notell";
	if (skill == do_notitle)		return "do_notitle";
	if (skill == do_noteroom)		return "do_noteroom";
	if (skill == do_oassign)		return "do_oassign";
	if (skill == do_ocreate)		return "do_ocreate";
	if (skill == do_odelete)		return "do_odelete";
	if (skill == do_ofind)			return "do_ofind";
	if (skill == do_ogrub)			return "do_ogrub";
	if (skill == do_oinvoke)		return "do_oinvoke";
	if (skill == do_oldscore)		return "do_oldscore";
	if (skill == do_olist)			return "do_olist";
	if (skill == do_omenu)			return "do_omenu";
	if (skill == do_opedit)			return "do_opedit";
	if (skill == do_open)			return "do_open";
/*  if (skill == do_opentourney)	return "do_opentourney"; */
	if (skill == do_opstat)			return "do_opstat";
	if (skill == do_orange)			return "do_orange";
	if (skill == do_order)			return "do_order";
	if (skill == do_orders)			return "do_orders";
	if (skill == do_ordertalk)		return "do_ordertalk";
	if (skill == do_oset)			return "do_oset";
	if (skill == do_ostat)			return "do_ostat";
	if (skill == do_outcast)		return "do_outcast";
	if (skill == do_owhere)			return "do_owhere";
	if (skill == do_pagelen)		return "do_pagelen";
	if (skill == do_pager)			return "do_pager";
	if (skill == do_pardon)			return "do_pardon";
	if (skill == do_password)		return "do_password";
	if (skill == do_pcrename)		return "do_pcrename";
	if (skill == do_peace)			return "do_peace";
	if (skill == do_pick)			return "do_pick";
	if (skill == do_pmenu)			return "do_pmenu";
	if (skill == do_poison_weapon)	return "do_poison_weapon";
	if (skill == do_practice)		return "do_practice";
	if (skill == do_prompt)			return "do_prompt";
	if (skill == do_pull)			return "do_pull";
	if (skill == do_punch)			return "do_punch";
	if (skill == do_purge)			return "do_purge";
	if (skill == do_push)			return "do_push";
	if (skill == do_put)			return "do_put";
	if (skill == do_qpset)			return "do_qpset";
	if (skill == do_quaff)			return "do_quaff";
	if (skill == do_quest)			return "do_quest";
	if (skill == do_qui)			return "do_qui";
	if (skill == do_quit)			return "do_quit";
	if (skill == do_racetalk)		return "do_racetalk";
	if (skill == do_rank)			return "do_rank";
	if (skill == do_rap)			return "do_rap";
	if (skill == do_rassign)		return "do_rassign";
	if (skill == do_rat)			return "do_rat";
	if (skill == do_rdelete)		return "do_rdelete";
	if (skill == do_reboo)			return "do_reboo";
	if (skill == do_reboot)			return "do_reboot";
	if (skill == do_recho)			return "do_recho";
	if (skill == do_recite)			return "do_recite";
	if (skill == do_redit)			return "do_redit";
	if (skill == do_regoto)			return "do_regoto";
	if (skill == do_remains)		return "do_remains";
	if (skill == do_remove)			return "do_remove";
	if (skill == do_rent)			return "do_rent";
	if (skill == do_repair)			return "do_repair";
	if (skill == do_repairset)		return "do_repairset";
	if (skill == do_repairshops)	return "do_repairshops";
	if (skill == do_repairstat)		return "do_repairstat";
	if (skill == do_reply)			return "do_reply";
	if (skill == do_report)			return "do_report";
	if (skill == do_rescue)			return "do_rescue";
	if (skill == do_reserve)		return "do_reserve";
	if (skill == do_reset)			return "do_reset";
	if (skill == do_rest)			return "do_rest";
	if (skill == do_restore)		return "do_restore";
	if (skill == do_restoretime)	return "do_restoretime";
	if (skill == do_restrict)		return "do_restrict";
	if (skill == do_retire)			return "do_retire";
	if (skill == do_retran)			return "do_retran";
	if (skill == do_return)			return "do_return";
	if (skill == do_revert)			return "do_revert";
	if (skill == do_rip)			return "do_rip";
	if (skill == do_rlist)			return "do_rlist";
	if (skill == do_rmenu)			return "do_rmenu";
	if (skill == do_rpedit)			return "do_rpedit";
	if (skill == do_rpstat)			return "do_rpstat";
	if (skill == do_rreset)			return "do_rreset";
	if (skill == do_rset)			return "do_rset";
	if (skill == do_rstat)			return "do_rstat";
	if (skill == do_sacrifice)		return "do_sacrifice";
	if (skill == do_save)			return "do_save";
	if (skill == do_savearea)		return "do_savearea";
	if (skill == do_say)			return "do_say";
	if (skill == do_scan)			return "do_scan";
	if (skill == do_score)			return "do_score";
	if (skill == do_scribe)			return "do_scribe";
	if (skill == do_search)			return "do_search";
	if (skill == do_sedit)			return "do_sedit";
	if (skill == do_sell)			return "do_sell";
	if (skill == do_set_boot_time)	return "do_set_boot_time";
	if (skill == do_setclan)		return "do_setclan";
	if (skill == do_setclass)		return "do_setclass";
	if (skill == do_setcouncil)		return "do_setcouncil";
	if (skill == do_setdeity)		return "do_setdeity";
	if (skill == do_setguild)		return "do_setguild";
	if (skill == do_setorder)		return "do_setorder";
	if (skill == do_setrace)		return "do_setrace";
	if (skill == do_shops)			return "do_shops";
	if (skill == do_shopset)		return "do_shopset";
	if (skill == do_shopstat)		return "do_shopstat";
	if (skill == do_shout)			return "do_shout";
	if (skill == do_shove)			return "do_shove";
	if (skill == do_showclan)		return "do_showclan";
	if (skill == do_showclass)		return "do_showclass";
	if (skill == do_showcouncil)	return "do_showcouncil";
	if (skill == do_showdeity)		return "do_showdeity";
	if (skill == do_showguild)		return "do_showguild";
	if (skill == do_showorder)		return "do_showorder";
	if (skill == do_showrace)		return "do_showrace";
	if (skill == do_showbytes)		return "do_showbytes";
	if (skill == do_shutdow)		return "do_shutdow";
	if (skill == do_shutdown)		return "do_shutdown";
	if (skill == do_silence)		return "do_silence";
	if (skill == do_sit)			return "do_sit";
	if (skill == do_sla)			return "do_sla";
	if (skill == do_slay)			return "do_slay";
	if (skill == do_sleep)			return "do_sleep";
	if (skill == do_slice)			return "do_slice";
	if (skill == do_slist)			return "do_slist";
	if (skill == do_slookup)		return "do_slookup";
	if (skill == do_smoke)			return "do_smoke";
	if (skill == do_snoop)			return "do_snoop";
	if (skill == do_sober)			return "do_sober";
	if (skill == do_socials)		return "do_socials";
	if (skill == do_south)			return "do_south";
	if (skill == do_southeast)		return "do_southeast";
	if (skill == do_southwest)		return "do_southwest";
	if (skill == do_speak)			return "do_speak";
	if (skill == do_split)			return "do_split";
	if (skill == do_sset)			return "do_sset";
	if (skill == do_stand)			return "do_stand";
	if (skill == do_stat)			return "do_stat";
	if (skill == do_statreport)		return "do_statreport";
	if (skill == do_steal)			return "do_steal";
	if (skill == do_stun)			return "do_stun";
	if (skill == do_style)			return "do_style";
	if (skill == do_supplicate)		return "do_supplicate";
	if (skill == do_switch)			return "do_switch";
	if (skill == do_tamp)			return "do_tamp";
	if (skill == do_tell)			return "do_tell";
	if (skill == do_think)			return "do_think";
	if (skill == do_time)			return "do_time";
	if (skill == do_timecmd)		return "do_timecmd";
	if (skill == do_title)			return "do_title";
	if (skill == do_track)			return "do_track";
	if (skill == do_transfer)		return "do_transfer";
	if (skill == do_trust)			return "do_trust";
	if (skill == do_typo)			return "do_typo";
	if (skill == do_unfoldarea)		return "do_unfoldarea";
	if (skill == do_unhell)			return "do_unhell";
	if (skill == do_unlock)			return "do_unlock";
	if (skill == do_unsilence)		return "do_unsilence";
	if (skill == do_up)				return "do_up";
	if (skill == do_users)			return "do_users";
	if (skill == do_value)			return "do_value";
	if (skill == do_versio)			return "do_versio";
	if (skill == do_version)		return "do_version";
	if (skill == do_visible)		return "do_visible";
	if (skill == do_vnums)			return "do_vnums";
	if (skill == do_vsearch)		return "do_vsearch";
	if (skill == do_wake)			return "do_wake";
	if (skill == do_wartalk)		return "do_wartalk";
	if (skill == do_wear)			return "do_wear";
	if (skill == do_weather)		return "do_weather";
	if (skill == do_west)			return "do_west";
	if (skill == do_where)			return "do_where";
	if (skill == do_who)			return "do_who";
	if (skill == do_whois)			return "do_whois";
	if (skill == do_wimpy)			return "do_wimpy";
	if (skill == do_wizhelp)		return "do_wizhelp";
	if (skill == do_wizlist)		return "do_wizlist";
	if (skill == do_wizlock)		return "do_wizlock";
	if (skill == do_yell)			return "do_yell";
	if (skill == do_zap)			return "do_zap";
	if (skill == do_zones)			return "do_zones";
	return "reserved";
}


int CSkillTable::GetSaveEffectFromName (const char* n)
{
	for (int i=0; i < DIM (SpellSaveEffectNames); ++i)
		if (! str_cmp (n, SpellSaveEffectNames [i]))
			return i;

	return -1;
}


//  New check to see if you can use skills to support morphs --Shaddai
BOOL can_use_skill (CCharacter* ch, int percent, int gsn)
{
	if (ch->IsNpc () && percent < 85)
		return TRUE;

	if (! ch->IsNpc () && percent < ch->GetLearnedPercent (gsn))
		return TRUE;

// 1.4add
//	if (ch->morph && ch->morph->morph && ch->morph->morph->skills &&
//		ch->morph->morph->skills [0] != '\0' &&
//		is_name (SkillTable.GetName (gsn), ch->morph->morph->skills) && 
//		percent < 85)
//			return TRUE;
//
//	if (! (ch->morph && ch->morph->morph && ch->morph->morph->no_skills &&
//		ch->morph->morph->no_skills [0] != '\0' &&
//		is_name (skillTable.GetName (gsn), ch->morph->morph->no_skills)))
//			return TRUE;

	return FALSE;
}


// Cook was coded by Blackmane and heavily modified by Shaddai
void do_cook (CCharacter* ch, char* argument)
{
	char		arg [MAX_INPUT_LENGTH];
	char		buf [MAX_STRING_LENGTH];

	one_argument (argument, arg);
	if (ch->IsNpc () || ch->GetLevel () <
	  SkillTable.GetClassLevel (gsn_cook, ch->GetClass ())) {
		ch->SendText ("That skill is beyond your understanding.\n\r");
		return;
	}
	if (arg [0] == '\0') {
		ch->SendText ("Cook what?\n\r");
		return;
	}

	if (ms_find_obj (ch))
		return;

	CObjData	*pFood = get_obj_carry (ch, arg);
	if (! pFood) {
		ch->SendText ("You do not have that item.\n\r");
		return;
	}
	if (pFood->item_type != ITEM_COOK) {
		ch->SendText ("How can you cook that?\n\r");
		return;
	}
	if (pFood->value [2] > 2) {
		ch->SendText ("That is already burnt to a crisp.\n\r");
		return;
	}

	CObjData	*pFire = NULL;
	POSITION	pos = ch->GetInRoom ()->GetHeadContentPos ();
	while (pFire = ch->GetInRoom ()->GetNextContent (pos))
		if (pFire->item_type == ITEM_FIRE)
			break;

	if (! pFire ) {
		ch->SendText ("There is no fire here!\n\r");
		return;
	}
	if (number_percent () > ch->GetPcData ()->learned [gsn_cook]) {
		pFood->timer /= 2;
		pFood->value [0] = 0;
		pFood->value [2] = 3;
		act (AT_MAGIC, "$p catches on fire burning it to a crisp!\n\r",
			ch, pFood, NULL, TO_CHAR);
		act (AT_MAGIC, "$n catches $p on fire burning it to a crisp.",
			ch, pFood, NULL, TO_ROOM);
		sprintf (buf, "a burnt %s", pFood->pIndexData->GetName ());
		pFood->SetShortDescr (buf);
		sprintf (buf, "A burnt %s.", pFood->pIndexData->GetName ());
		pFood->SetDescription (buf);
		return;
	}

	if (number_percent() > 85) {
		pFood->timer *= 3;
		pFood->value [2] += 2;
		act (AT_MAGIC, "$n overcooks a $p.", ch, pFood, NULL, TO_ROOM);
		act (AT_MAGIC, "You overcook a $p.", ch, pFood, NULL, TO_CHAR);
		sprintf (buf, "an overcooked %s", pFood->pIndexData->GetName ());
		pFood->SetShortDescr (buf);
		sprintf (buf, "An overcooked %s.", pFood->pIndexData->GetName ());
		pFood->SetDescription (buf);
	} else {
		pFood->timer *= 4;
		pFood->value [0] *= 2;
		act (AT_MAGIC, "$n roasts a $p.", ch, pFood, NULL, TO_ROOM);
		act (AT_MAGIC, "You roast a $p.", ch, pFood, NULL, TO_CHAR);
		sprintf (buf, "a roasted %s", pFood->pIndexData->GetName ());
		pFood->SetShortDescr (buf);
		sprintf (buf, "A roasted %s.", pFood->pIndexData->GetName ());
		pFood->SetDescription (buf);
		++pFood->value [2];
	}
	learn_from_success (ch, gsn_cook);
}


// Function to strip off the "a" or "an" or "the" or "some" from an object's
// short description for the purpose of using it in a sentence sent to
// the owner of the object.  (Ie: an object with the short description
// "a long dark blade" would return "long dark blade" for use in a sentence
// like "Your long dark blade".  The object name isn't always appropriate
// since it contains keywords that may not look proper.		-Thoric
char *myobj (CObjData *obj)
{
	int	offset = 0;

	if (! str_prefix ("a ", obj->GetShortDescr ()))
		offset = 2;
	else if (! str_prefix ("an ", obj->GetShortDescr ()))
		offset = 3;
	else if (! str_prefix ("the ", obj->GetShortDescr ()))
		offset = 4;
	else if (! str_prefix ("some ", obj->GetShortDescr ()))
		offset = 5;

	return NCCP (obj->GetShortDescr () + offset);
}