/*      SCCS Id: @(#)polyself.c 3.2     95/07/19        */
/*      Copyright (C) 1987, 1988, 1989 by Ken Arromdee */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * Polymorph self routine.
 *
 * Note:  the light source handling code assumes that both youmonst.m_id
 * and youmonst.mx will always remain 0 when it handles the case of the
 * player polymorphed into a light-emitting monster.
 */

#include "hack.h"

#ifdef OVLB
static void FDECL(polyman, (const char *,const char *));
static void NDECL(break_armor);
static void FDECL(drop_weapon,(int));
static void NDECL(skinback);
static void NDECL(uunstick);
static int FDECL(armor_to_dragon,(int));
static void NDECL(special_poly);


/* update the "uasmon" structure */
void
set_uasmon()
{
	if (u.umonnum >= LOW_PM) {
		uasmon = &mons[u.umonnum];
	} else {
		uasmon = &playermon;
#if 0
	    /* We do not currently use uasmon or playermon to check the below
	     * attributes of the player, and it is generally pointless to do
	     * so.  Furthermore, we do not call set_uasmon() often enough to
	     * use uasmon or playermon for this purpose; for instance, we do
	     * not call it every time we wear armor.
	     */
	    	playermon.mlevel = u.ulevel;
	    	playermon.ac = u.uac;
	    	playermon.mr = (u.ulevel > 8) ? 5 * (u.ulevel - 7) : u.ulevel;
#endif
	}
	set_mon_data(&youmonst, uasmon, 0);
	return;
}

/* make a (new) human out of the player */
static void
polyman(fmt, arg)
const char *fmt, *arg;
{
	boolean sticky = sticks(uasmon) && u.ustuck && !u.uswallow,
                was_mimicking_gold = (u.usym == 0);

	if (u.umonnum != PM_PLAYERMON) {
		u.acurr = u.macurr;     /* restore old attribs */
		u.amax = u.mamax;
		u.umonnum = PM_PLAYERMON;
		flags.female = u.mfemale;
	}
	u.usym = S_HUMAN;
	set_uasmon();

	u.mh = u.mhmax = 0;
	u.mtimedone = 0;
	skinback();
	u.uundetected = 0;
	newsym(u.ux,u.uy);

	if (sticky) uunstick();
	find_ac();
	if (was_mimicking_gold) {
		if (multi < 0) unmul("");
	}

	You(fmt, arg);
	/* check whether player foolishly genocided self while poly'd */
#ifdef ROLE_PATCH
	if ((mvitals[urole.malenum].mvflags & G_GENOD) ||
			(urole.femalenum &&
			(mvitals[urole.femalenum].mvflags & G_GENOD)))
#else
	if (mvitals[u.umonster].mvflags & G_GENOD)
#endif
	{
		/* intervening activity might have clobbered genocide info */
		if (!killer || !strstri(killer, "genocid")) {
			killer_format = KILLED_BY;
			killer = "self-genocide";
		}
		done(GENOCIDED);
	}

	if(!Levitation && !u.ustuck &&
	    (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy)))
		spoteffects();
}

void
change_sex()
{
	flags.female = !flags.female;
	max_rank_sz();
#ifdef ROLE_PATCH
	/* KMH -- Should now be obsolete */
	if (flags.female && urole.name.f)
		Strcpy(pl_character, urole.name.f);
	else
		Strcpy(pl_character, urole.name.m);
	u.umonster = (flags.female && urole.femalenum) ? urole.femalenum :
			urole.malenum;
#else
	if (Role_if(PM_PRIEST)) {
		Strcpy(pl_character+6, flags.female ? "ess" : "");
		u.umonster = flags.female ? PM_PRIESTESS : PM_PRIEST;
	} else if (Role_if(PM_CAVEMAN)) {
		Strcpy(pl_character+4, flags.female ? "woman" : "man");
		u.umonster = flags.female ? PM_CAVEWOMAN : PM_CAVEMAN;
	}
#endif
}

void
newman()
{
	int     basehp, hp, bonushp, energy, energybase, tmp;        
	/* STEPHEN WHITE'S NEW CODE */
	if (!rn2(10)) change_sex();

	switch  (Role_switch) {        
		case PM_ARCHEOLOGIST:
			energybase = 6;
			basehp = 13;  
			bonushp = 1;  
			break;
		case PM_BARBARIAN:
			energybase = 2;
			basehp = 16;  
			bonushp = 3;            
			break;
		case PM_CAVEMAN:
			energybase = 2;
			basehp = 16;  
			bonushp = 3;            
			break;
		case PM_DOPPELGANGER:
			energybase = 7;
			basehp = 12;
			bonushp = 1;  
			break;
		case PM_ELF: case PM_DROW:
			energybase = 7;
			basehp = 15;
			bonushp = 2;
			break;
		case PM_FLAME_MAGE:
			energybase = 10;
			basehp = 12;
			bonushp = 1;
			break;
		case PM_GNOME:
			energybase = 7;
			basehp = 15;
			bonushp = 2;
			break;
		case PM_HEALER:
			energybase = 10;
			basehp = 13;
			bonushp = 1;
			break;
		case PM_ICE_MAGE:
			energybase = 10;
			basehp = 12;
			bonushp = 1;
			break;
#ifdef YEOMAN
		case PM_YEOMAN:
#endif
		case PM_KNIGHT:
			energybase = 3;
			basehp = 16;
			bonushp = 3;
			break;
		case PM_MONK:
			energybase = 7;
			basehp = 14;
			bonushp = 2;
			break;
		case PM_NECROMANCER:
			energybase = 10;
			basehp = 12;  
			bonushp = 1;  
			break;
		case PM_PRIEST:
			energybase = 10;
			basehp = 14;
			bonushp = 2;            
			break;
		case PM_ROGUE:
			energybase = 6;
			basehp = 12;  
			bonushp = 2;            
			break;
		case PM_SAMURAI:
			energybase = 2;
			basehp = 15;  
			bonushp = 3;            
			break;
#ifdef TOURIST        
		case PM_TOURIST:
			energybase = 6;
			basehp = 10;  
			bonushp = 1;
			break;
#endif        
		case PM_UNDEAD_SLAYER:
			energybase = 3;
			basehp = 16;
			bonushp = 3;
			break;
		case PM_VALKYRIE:
			energybase = 2;
			basehp = 16;  
			bonushp = 3;            
			break;
		case PM_WIZARD:
			energybase = 10;
			basehp = 12;  
			bonushp = 1;  
			break;
		case PM_HUMAN_WEREWOLF:
		default:
			energybase = 2;
			basehp = 12;
			bonushp = 1;            
			break;
	}

	if (!Role_if(PM_DOPPELGANGER)) {
		tmp = u.ulevel;
		u.ulevel = u.ulevel + rn1(5, -2);
		if (u.ulevel > 127 || u.ulevel < 1) u.ulevel = 1;
		if (u.ulevel > MAXULEV) u.ulevel = MAXULEV;

		adjabil(tmp, (int)u.ulevel);
		reset_rndmonst(NON_PM); /* new monster generation criteria */

		/* random experience points for the new experience level */
		u.uexp = rndexp();
	}

	/* u.uhpmax * u.ulevel / tmp2: proportionate hit points to new level
	 * -10 and +10: don't apply proportionate HP to 10 of a starting
	 *   character's hit points (since a starting character's hit points
	 *   are not on the same scale with hit points obtained through level
	 *   gain)
	 * 9 - rn2(19): random change of -9 to +9 hit points
	 */
	if (!Role_if(PM_DOPPELGANGER) && rn2(4)) {
		if (u.ulevel > 1)
		  	hp = basehp + d((u.ulevel-1),8) + bonushp * u.ulevel;
		else  hp = basehp + bonushp;
		u.uhpbase = hp;
		u.uhpmax = hp + 7 * u.ulevel;
		u.uhp = u.uhpmax;

		if (u.ulevel > 1) 
			energy = d(u.ulevel, energybase) + 2 * rnd(5);
		else  energy = 2 * rnd(6);                
		u.uenbase = energy;
		u.uenmax = energy + 10 * u.ulevel;
		u.uen = energy;
	}
	redist_attr();
	u.uhunger = rn1(500,500);
	newuhs(FALSE);
	if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL);
	Sick = 0;
	Stoned = 0;
	if (Role_if(PM_DOPPELGANGER)) {        
		if (u.uhp <= 10) u.uhp = 10;
		if (u.uhpmax <= 10) u.uhpmax = 10;
		if (u.uhpbase <= 10) u.uhpbase = 10;
		if (u.uen <= u.ulevel) u.uen = u.ulevel;
		if (u.uenmax <= u.ulevel) u.uenmax = u.ulevel;
		if (u.uenbase <= u.ulevel) u.uenbase = u.ulevel;
	}
	if (u.uhp <= 0 || u.uhpmax <= 0) {
		if (Polymorph_control) {
			if (u.uhp <= 0) u.uhp = 1;
			if (u.uhpmax <= 0) u.uhpmax = 1;
			if (u.uhpbase <= 0) u.uhpbase = 1;                
		} else {
			Your("new form doesn't seem healthy enough to survive.");
			killer_format = KILLED_BY_AN;
			killer="unsuccessful polymorph";
			done(DIED);
		}
	}
/* WAC added case for gnomes */
	polyman("feel like a new %s!",
			Race_elven ? "elf" :
#ifdef DWARF
			Role_if(PM_DWARF) ? "dwarf" :
#endif
			Role_if(PM_GNOME) ? "gnome" :
			flags.female ? "woman" : "man");
	flags.botl = 1;
	vision_full_recalc = 1;
	(void) encumber_msg();
}

void
polyself()
{
	char buf[BUFSZ];
	int old_light, new_light;
	int mntmp = NON_PM;
	int tries=0;
	boolean draconian = (uarm &&
				uarm->otyp >= GRAY_DRAGON_SCALE_MAIL &&
				uarm->otyp <= YELLOW_DRAGON_SCALES);

	boolean iswere = (u.ulycn >= LOW_PM || is_were(uasmon));
	boolean isvamp = (u.usym == S_VAMPIRE || u.umonnum == PM_VAMPIRE_BAT);

	/* [Tom] I made the chance of dying from Con check only possible for
		 really weak people (it was out of 20) */

	if(!Polymorph_control && !draconian && !iswere && !isvamp &&
			!Role_if(PM_DOPPELGANGER)) {
		if (rn2(12) > ACURR(A_CON)) {
			You(shudder_for_moment);
			losehp(rnd(30), "system shock", KILLED_BY_AN);
			exercise(A_CON, FALSE);
			return;
		}
	}
	old_light = (u.umonnum >= LOW_PM) ? emits_light(uasmon) : 0;

	if (Polymorph_control) {
		do {
			getlin("Become what kind of monster? [type the name]",
				buf);
			mntmp = name_to_mon(buf);
			if (mntmp < LOW_PM)
				pline("I've never heard of such monsters.");
			/* Note:  humans are illegal as monsters, but an
			 * illegal monster forces newman(), which is what we
			 * want if they specified a human.... */

			/* [Tom] gnomes are polyok, so this doesn't apply for
			   player gnomes */
            /* WAC but we want to catch player gnomes and not
               so add an extra check */
            else if (!polyok(&mons[mntmp]) &&
					(Race_elven ? !is_elf(&mons[mntmp]) :
#ifdef DWARF
					Role_if(PM_DWARF) ? !is_dwarf(&mons[mntmp]) :
#endif
					/* WAC
					 * should always fail (for now) gnome check
					 * unless gnomes become not polyok.  Then, it'll
					 * still work ;B
					 */
					Role_if(PM_GNOME) ? !is_gnome(&mons[mntmp]) :
					!is_human(&mons[mntmp])))
				You("cannot polymorph into that.");
			else break;
		} while(++tries < 5);
		if (tries==5) pline(thats_enough_tries);
		/* allow skin merging, even when polymorph is controlled */
		if (draconian &&
		    (mntmp == armor_to_dragon(uarm->otyp) || tries == 5))
		    goto do_merge;
	} else if (draconian || iswere || isvamp) {
		/* special changes that don't require polyok() */
		if (draconian) {
		    do_merge:
			mntmp = armor_to_dragon(uarm->otyp);
			if (!(mvitals[mntmp].mvflags & G_GENOD)) {
				/* allow G_EXTINCT */
				You("merge with your scaly armor.");
				uskin = uarm;
				uarm = (struct obj *)0;
				/* save/restore hack */
				uskin->owornmask |= I_SPECIAL;
			}
		} else if (iswere) {
			if (is_were(uasmon))
				mntmp = PM_HUMAN; /* Illegal; force newman() */
			else
				mntmp = u.ulycn;
		} else {
			if (u.usym == S_VAMPIRE)
				mntmp = PM_VAMPIRE_BAT;
			else
				mntmp = PM_VAMPIRE;
		}
		if (polymon(mntmp))
			goto made_change;
	}
	if (mntmp < LOW_PM) {
		tries = 0;
		do {
			/* randomly pick an "ordinary" monster */
			mntmp = rn1(SPECIAL_PM - LOW_PM, LOW_PM);
		} while((!polyok(&mons[mntmp]) || is_placeholder(&mons[mntmp]))
				&& tries++ < 200);
	}

	/* The below polyok() fails either if everything is genocided, or if
	 * we deliberately chose something illegal to force newman().
	 */
        /* Also catches player gnomes going to gnomes */
        /* WAC Doppelgangers go through a 1/20 check rather than 1/5 */
        if (!polyok(&mons[mntmp])
                || (Role_if(PM_DOPPELGANGER) ? !rn2(20) : !rn2(5))
#ifdef DWARF
				|| (Role_if(PM_DWARF) && is_dwarf(&mons[mntmp]))
#endif
                || (Role_if(PM_GNOME) && is_gnome(&mons[mntmp])))
		newman();
	else if(!polymon(mntmp)) return;

	if (!uarmg) selftouch("No longer petrify-resistant, you");

 made_change:
	new_light = (u.umonnum >= LOW_PM) ? emits_light(uasmon) : 0;
	if (old_light != new_light) {
	    if (old_light)
		del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
	    if (new_light == 1) ++new_light;  /* otherwise it's undetectable */
	    if (new_light)
		new_light_source(u.ux, u.uy, new_light,
				 LS_MONSTER, (genericptr_t)&youmonst);
	}
}

/* (try to) make a mntmp monster out of the player */
int
polymon(mntmp)  /* returns 1 if polymorph successful */
int     mntmp;
{
	boolean sticky = sticks(uasmon) && u.ustuck && !u.uswallow;
	boolean dochange = FALSE;
	int     tmp;

	if (mvitals[mntmp].mvflags & G_GENOD) { /* allow G_EXTINCT */
		You_feel("rather %s-ish.",mons[mntmp].mname);
		exercise(A_WIS, TRUE);
		return(0);
	}

	/* STEPHEN WHITE'S NEW CODE */        
  
	/* If your an Undead Slayer, you can't become undead! */
  
	if (is_undead(&mons[mntmp]) && Role_if(PM_UNDEAD_SLAYER)) {
		if (Polymorph_control) { 
			You("hear a voice boom out: \"How dare you take such a form!\"");
			u.ualign.record -= 5;
#ifdef NOARTIFACTWISH
			u.usacrifice = 0;
#endif
			exercise(A_WIS, FALSE);
		 } else {
			You("start to change into %s, but a voice booms out:", an(mons[mntmp].mname));
			pline("\"No, I will not allow such a change!\"");
		 }
		 return(0);
	}

	/* KMH, ethics */
	u.uethics.polyselfs++;

	if (u.umonnum == PM_PLAYERMON) {
		/* Human to monster; save human stats */
		u.macurr = u.acurr;
		u.mamax = u.amax;
		u.mfemale = flags.female;
	} else {
		/* Monster to monster; restore human stats, to be
		 * immediately changed to provide stats for the new monster
		 */
		u.acurr = u.macurr;
		u.amax = u.mamax;
		flags.female = u.mfemale;
	}

	if (is_male(&mons[mntmp])) {
		if(flags.female) dochange = TRUE;
	} else if (is_female(&mons[mntmp])) {
		if(!flags.female) dochange = TRUE;
	} else if (!is_neuter(&mons[mntmp]) && mntmp != u.ulycn) {
		if(!rn2(10)) dochange = TRUE;
	}
	if (dochange) {
		flags.female = !flags.female;
		You("%s %s %s!",
		    (u.umonnum != mntmp) ? "turn into a" : "feel like a new",
		    flags.female ? "female" : "male",
		    mons[mntmp].mname);
	} else {
		if (u.umonnum != mntmp)
			You("turn into %s!", an(mons[mntmp].mname));
		else
			You_feel("like a new %s!", mons[mntmp].mname);
	}
	if (Stoned && poly_when_stoned(&mons[mntmp])) {
		/* poly_when_stoned already checked stone golem genocide */
		You("turn to stone!");
		mntmp = PM_STONE_GOLEM;
		Stoned = 0;
	}

	u.umonnum = mntmp;
	u.usym = mons[mntmp].mlet;
	set_uasmon();

	/* New stats for monster, to last only as long as polymorphed.
	 * Currently only strength gets changed.
	 */
	if(strongmonst(&mons[mntmp])) ABASE(A_STR) = AMAX(A_STR) = 118;

	if (resists_ston(&youmonst) && Stoned) { /* parnes@eniac.seas.upenn.edu */
		Stoned = 0;
		You("no longer seem to be petrifying.");
	}
	if (u.usym == S_FUNGUS && Sick) {
		make_sick(0L, (char *) 0, FALSE, SICK_ALL);
		You("no longer feel sick.");
	}
	if (nohands(uasmon)) Glib = 0;

	if (u.usym == S_DRAGON && mntmp >= PM_GRAY_DRAGON)
		u.mhmax = 8 * mons[mntmp].mlevel;
	else if (is_golem(uasmon)) u.mhmax = golemhp(mntmp);
	else {
		/*
		tmp = adj_lev(&mons[mntmp]);
		 * We can't do this, since there's no such thing as an
		 * "experience level of you as a monster" for a polymorphed
		 * character.
		 */
		tmp = mons[mntmp].mlevel;
		if (!tmp) u.mhmax = rnd(4);
		else u.mhmax = d(tmp, 8);
	}
	u.mh = u.mhmax;

	u.mtimedone = rn1(500, 500);
	if (u.ulevel < mons[mntmp].mlevel)
	/* Low level characters can't become high level monsters for long */
#ifdef DUMB
		{
		/* DRS/NS 2.2.6 messes up -- Peter Kendell */
			int     mtd = u.mtimedone,
				ulv = u.ulevel,
				mlv = mons[mntmp].mlevel;

			u.mtimedone = mtd * ulv / mlv;
		}
#else
		u.mtimedone = u.mtimedone * u.ulevel / mons[mntmp].mlevel;
#endif

#ifdef EATEN_MEMORY
	/* WAC Doppelgangers can stay much longer in a form they know well */
	if (Role_if(PM_DOPPELGANGER) && mvitals[mntmp].eaten) {
		u.mtimedone *= 2;
		u.mtimedone += mvitals[mntmp].eaten;
	}
#endif

	if (uskin && mntmp != armor_to_dragon(uskin->otyp))
		skinback();
	break_armor();
	drop_weapon(1);
	if (hides_under(uasmon))
		u.uundetected = OBJ_AT(u.ux, u.uy);
	else if (uasmon->mlet == S_EEL)
		u.uundetected = is_pool(u.ux, u.uy);
	else
		u.uundetected = 0;
	newsym(u.ux,u.uy);              /* Change symbol */

	if (!sticky && !u.uswallow && u.ustuck && sticks(uasmon)) u.ustuck = 0;
	else if (sticky && !sticks(uasmon)) uunstick();
#ifdef YEOMAN
	check_steed();
#endif

	if (flags.verbose) {
	    static const char use_thec[] = "Use the command #%s to %s.";
	    static const char monsterc[] = "monster";
		if (can_breathe(uasmon))
			pline(use_thec,monsterc,"use your breath weapon");
		else if (attacktype(uasmon, AT_SPIT))
			pline(use_thec,monsterc,"spit venom");
		else if (attacktype(uasmon, AT_GAZE))
			pline(use_thec,monsterc,"gaze");
		else if (u.usym == S_NYMPH)
			pline(use_thec,monsterc,"remove an iron ball");
		else if (u.usym == S_UMBER)
			pline(use_thec,monsterc,"confuse monsters");
		else if (is_hider(uasmon))
			pline(use_thec,monsterc,"hide");
		else if (is_were(uasmon))
			pline(use_thec,monsterc,"summon help");
		else if (webmaker(uasmon))
			pline(use_thec,monsterc,"spin a web");
		else if (u.umonnum == PM_GREMLIN)
			pline(use_thec,monsterc,"multiply in a fountain");
		else if (is_unicorn(uasmon))
			pline(use_thec,monsterc,"use your horn");
		else if (is_mind_flayer(uasmon))
			pline(use_thec,monsterc,"emit a mental blast");
		else if (uasmon->msound == MS_SHRIEK) /* worthless, actually */
			pline(use_thec,monsterc,"shriek");

	    if (lays_eggs(uasmon) && flags.female)
		pline(use_thec,"sit","lay an egg");
	}
	/* you now know what an egg of your type looks like */
	if (lays_eggs(uasmon)) {
	    learn_egg_type(u.umonnum);
	    /* make queen bees recognize killer bee eggs */
	    learn_egg_type(egg_type_from_parent(u.umonnum, TRUE));
	}
	find_ac();
	/* KMH, balance patch -- new intrinsic */
	if((!Levitation && !u.ustuck && !Flying &&
	    (is_pool(u.ux,u.uy) || is_lava(u.ux,u.uy))) ||
	   (Underwater && !is_swimmer(uasmon)))
	    spoteffects();
	if (Passes_walls && u.utrap && u.utraptype == TT_INFLOOR) {
	    u.utrap = 0;
	    pline_The("rock seems to no longer trap you.");
	} else if (likes_lava(uasmon) && u.utrap && u.utraptype == TT_LAVA) {
	    u.utrap = 0;
	    pline_The("lava now feels soothing.");
	}
	if (amorphous(uasmon) || is_whirly(uasmon) || unsolid(uasmon)) {
	    if (Punished) {
		You("slip out of the iron chain.");
		unpunish();
	    }
	}
	if (u.utrap && (u.utraptype == TT_WEB || u.utraptype == TT_BEARTRAP) &&
		(amorphous(uasmon) || is_whirly(uasmon) || unsolid(uasmon) ||
		  (uasmon->msize <= MZ_SMALL && u.utraptype == TT_BEARTRAP))) {
	    You("are no longer stuck in the %s.",
		    u.utraptype == TT_WEB ? "web" : "bear trap");
	    /* probably should burn webs too if PM_FIRE_ELEMENTAL */
	    u.utrap = 0;
	}
	if (uasmon->mlet == S_SPIDER && u.utrap && u.utraptype == TT_WEB) {
	    You("orient yourself on the web.");
	    u.utrap = 0;
	}
	flags.botl = 1;
	vision_full_recalc = 1;
	exercise(A_CON, FALSE);
	exercise(A_WIS, TRUE);
	(void) encumber_msg();
	return(1);
}

static void
break_armor()
{
    register struct obj *otmp;

    if (breakarm(uasmon)) {
	if ((otmp = uarm) != 0) {
		if(otmp->oartifact) {
			if (donning(otmp)) cancel_don();
			Your("armor falls off!");
			(void) Armor_gone();
                        dropx(otmp); /*WAC Drop instead of destroy*/
		} else if ((Role_if(PM_DOPPELGANGER) ||
				(Role_if(PM_HUMAN_WEREWOLF) && u.umonnum == PM_WEREWOLF)) &&
				!otmp->cursed) {
			if (donning(otmp)) cancel_don();
			You("quickly remove your armor as you start to change.");
			(void) Armor_gone();
                        dropx(otmp); /*WAC Drop instead of destroy*/
		} else {
		if (donning(otmp)) cancel_don();
		You("break out of your armor!");
		exercise(A_STR, FALSE);
		(void) Armor_gone();
		useup(otmp);
		}
	}
	if ((otmp = uarmc) != 0) {
	    if(otmp->oartifact) {
		Your("cloak falls off!");
		(void) Cloak_off();
		dropx(otmp);
		} else if ((Role_if(PM_DOPPELGANGER) ||
				(Role_if(PM_HUMAN_WEREWOLF) && u.umonnum == PM_WEREWOLF)) &&
				!otmp->cursed) {
		You("remove your cloak before you transform.");
		(void) Cloak_off();
		dropx(otmp);
	    } else {
		Your("cloak tears apart!");
		(void) Cloak_off();
		useup(otmp);
	    }
	}
#ifdef TOURIST
	if ((otmp = uarmu) != 0) {
		if ((Role_if(PM_DOPPELGANGER) || otmp->oartifact || 
			    (Role_if(PM_HUMAN_WEREWOLF) && u.umonnum == PM_WEREWOLF))
			    && !otmp->cursed) {
			You("take off your shirt just before it starts to rip.");
			setworn((struct obj *)0, otmp->owornmask & W_ARMU);
			dropx(otmp);
		} else {                
		Your("shirt rips to shreds!");
		useup(uarmu);
	}
	}
#endif
    } else if (sliparm(uasmon)) {
	if ((otmp = uarm) != 0) {
		if (donning(otmp)) cancel_don();
		Your("armor falls around you!");
		(void) Armor_gone();
		dropx(otmp);
	}
	if ((otmp = uarmc) != 0) {
		if (is_whirly(uasmon))
			Your("cloak falls, unsupported!");
		else You("shrink out of your cloak!");
		(void) Cloak_off();
		dropx(otmp);
	}
#ifdef TOURIST
	if ((otmp = uarmu) != 0) {
		if (is_whirly(uasmon))
			You("seep right through your shirt!");
		else You("become much too small for your shirt!");
		setworn((struct obj *)0, otmp->owornmask & W_ARMU);
		dropx(otmp);
	}
#endif
    }
    if (nohands(uasmon) || verysmall(uasmon)) {
	if ((otmp = uarmg) != 0) {
	    if (donning(otmp)) cancel_don();
	    /* Drop weapon along with gloves */
	    You("drop your gloves%s!", uwep ? " and weapon" : "");
	    drop_weapon(0);
	    (void) Gloves_off();
	    dropx(otmp);
	}
	if ((otmp = uarms) != 0) {
	    You("can no longer hold your shield!");
	    (void) Shield_off();
	    dropx(otmp);
	}
	if ((otmp = uarmh) != 0) {
	    if (donning(otmp)) cancel_don();
	    Your("helmet falls to the %s!", surface(u.ux, u.uy));
	    (void) Helmet_off();
	    dropx(otmp);
	}
    }
    if (nohands(uasmon) || verysmall(uasmon) || slithy(uasmon) ||
		u.usym == S_CENTAUR) {
	if ((otmp = uarmf) != 0) {
	    if (donning(otmp)) cancel_don();
	    if (is_whirly(uasmon))
		Your("boots fall away!");
	    else Your("boots %s off your feet!",
			verysmall(uasmon) ? "slide" : "are pushed");
	    (void) Boots_off();
	    dropx(otmp);
	}
    }
}

static void
drop_weapon(alone)
int alone;
{
	struct obj *otmp = uwep;
	struct obj *otmp2 = uswapwep;

	if ((otmp != 0) || (otmp2 != 0)) {
	/* !alone check below is currently superfluous but in the
	 * future it might not be so if there are monsters which cannot
	 * wear gloves but can wield weapons
	 */
		if (!alone || cantwield(uasmon)) {
			struct obj *wep = uwep;
			struct obj *wep2 = uswapwep;

			if (wep) {
			    if (alone)
			        You("find you must drop your weapon!");
			    uwepgone();
			    if (!wep->cursed || wep->otyp != LOADSTONE)
			        dropx(otmp);
			}
#ifdef WEAPON_SKILLS
			untwoweapon();
#endif
			/* KMH -- Secondary weapon isn't considered "in hand"
			if (wep2) {
			    if (alone)
			        You("no longer have your secondary weapon readied!");
			    uswapwepgone();
			    if (!wep2->cursed || wep2->otyp != LOADSTONE)
			        dropx(otmp2);
			} */
		}
	}
}

void
rehumanize()
{
	const char *uform = Race_elven ? "elven" :
#ifdef DWARF
			Role_if(PM_DWARF) ? "dwarven" :
#endif
			Role_if(PM_GNOME) ? "gnomish" : "human";

	/* KMH, balance patch -- you can't revert back while unchanging */
	if (Unchanging && (u.mh < 1)) {
		killer_format = NO_KILLER_PREFIX;
		killer = "killed while stuck in creature form";
		done(DIED);
	}
  
	if (emits_light(uasmon))
	    del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
	polyman("return to %s form!", uform);

	if (u.uhp < 1) {
	    char kbuf[256];
	    
	    Sprintf(kbuf, "reverting to unhealthy %s form", uform);
	    killer_format = KILLED_BY;
	    killer = kbuf;
	    done(DIED);
	}
	if (u.mh < 1 || (!Role_if(PM_DOPPELGANGER) && (rn2(20) > ACURR(A_CON)))) { 
	/* Exhaustion for "forced" rehumaization & must pass con chack for 
    	 * non-doppelgangers 
    	 * Don't penalize doppelgangers/polymorph running out */
    	 
   	/* WAC Polymorph Exhaustion 1/2 HP to prevent abuse */
	    u.uhp = (u.uhp/2) + 1;
	}

	if (!uarmg) selftouch("No longer petrify-resistant, you");
	nomul(0);

	flags.botl = 1;
	vision_full_recalc = 1;
	(void) encumber_msg();
}


/* WAC -- MUHAHAHAAHA - Gaze attacks! 
 * Note - you can only gaze at one monster at a time, to keep this 
 * from getting out of hand ;B  Also costs 20 energy.
 */
int
dogaze()
{
	coord cc;
	struct monst *mtmp;

	if (Blind) {
		You("can't see a thing!");
		return(0);
	}
	if (u.uen < 20) {
		You("lack the energy to use your special gaze!");
		return(0);
	}
	pline("Where do you wish to look?");
	cc.x = u.ux;
	cc.y = u.uy;
	getpos(&cc, TRUE, "the spot to look");

	if (cc.x == -10) return (0); /* user pressed esc */

	mtmp = m_at(cc.x, cc.y);

	if (!mtmp || !canseemon(mtmp)) {
		You("don't see a monster there!");
		return (0);
	}


	if ((flags.safe_dog && !Confusion && !Hallucination
		  && mtmp->mtame) || (flags.confirm && mtmp->mpeaceful 
		  && !Confusion && !Hallucination)) {
		  	char qbuf[QBUFSZ];
		  	
			Sprintf(qbuf, "Really gaze at %s?", mon_nam(mtmp));
			if (yn(qbuf) != 'y') return (0);
			if (mtmp->mpeaceful) setmangry(mtmp);
	}

	u.uen -= 20;

	You("gaze at %s...", mon_nam(mtmp));

	if ((mtmp->data==&mons[PM_MEDUSA]) && !mtmp->mcan) {
		pline("Gazing at the awake Medusa is not a very good idea.");
		/* as if gazing at a sleeping anything is fruitful... */
		You("turn to stone...");
		killer_format = KILLED_BY;
		killer = "deliberately gazing at Medusa's hideous countenance";
		done(STONING);
	} else if (!mtmp->mcansee || mtmp->msleep) {
	    pline("But nothing happens.");
	    return (1);
	} else if (Invis && !perceives(mtmp->data)) {
	    pline("%s seems not to notice your gaze.", Monnam(mtmp));
	    return (1);
	} else {
	    register struct attack *mattk;
	    register int i;

	    for(i = 0; i < NATTK; i++) {
		mattk = &(uasmon->mattk[i]);
		if(mattk->aatyp == AT_GAZE) break;
	    }
	    damageum(mtmp, mattk);
	}
	return(1);
}


int
dobreathe()
{
	int energy = 0;


	if (Strangled) {
	    You_cant("breathe.  Sorry.");
	    return(0);
	}

	/* WAC -- no more belching.  Use up energy instead */
	if (Role_if(PM_DOPPELGANGER)
			|| (Role_if(PM_FLAME_MAGE) && u.umonnum == PM_RED_DRAGON)
			|| (Role_if(PM_ICE_MAGE) && u.umonnum == PM_WHITE_DRAGON)) {
		if (u.uen < 10) {
			You("don't have enough energy to breathe!");
			return(0);
		} 
		energy = 10;
	} else {
		if (u.uen < 15) {
			You("don't have enough energy to breathe!");
			return(0);
		} 
		energy = 15;
	}
	if (!getdir((char *)0)) return(0);
	else {
	    register struct attack *mattk;
	    register int i;

		u.uen -= energy;
		flags.botl = 1;

	    for(i = 0; i < NATTK; i++) {
		mattk = &(uasmon->mattk[i]);
		if(mattk->aatyp == AT_BREA) break;
	    }
	    buzz((int) (20 + mattk->adtyp-1), (int)mattk->damn,
		u.ux, u.uy, u.dx, u.dy);
	}
	return(1);
}

int
dospit() {
	struct obj *otmp;

	if (!getdir((char *)0)) return(0);
	otmp = mksobj(u.umonnum==PM_COBRA ? BLINDING_VENOM : ACID_VENOM,
			TRUE, FALSE);
	otmp->spe = 1; /* to indicate it's yours */
	throwit(otmp, 1);
	return(1);
}

int
doremove() {
	if (!Punished) {
		You("are not chained to anything!");
		return(0);
	}
	unpunish();
	return(1);
}

int
dospinweb()
{
	register struct trap *ttmp = t_at(u.ux,u.uy);

	if (Levitation || Is_airlevel(&u.uz)
	    || Underwater || Is_waterlevel(&u.uz)) {
		You("must be on the ground to spin a web.");
		return(0);
	}
	if (u.uswallow) {
		You("release web fluid inside %s.", mon_nam(u.ustuck));
		if (is_animal(u.ustuck->data)) {
			expels(u.ustuck, u.ustuck->data, TRUE);
			return(0);
		}
		if (is_whirly(u.ustuck->data)) {
			int i;

			for (i = 0; i < NATTK; i++)
				if (u.ustuck->data->mattk[i].aatyp == AT_ENGL)
					break;
			if (i == NATTK)
			       impossible("Swallower has no engulfing attack?");
			else {
				char sweep[30];

				sweep[0] = '\0';
				switch(u.ustuck->data->mattk[i].adtyp) {
					case AD_FIRE:
						Strcpy(sweep, "ignites and ");
						break;
					case AD_ELEC:
						Strcpy(sweep, "fries and ");
						break;
					case AD_COLD:
						Strcpy(sweep,
						      "freezes, shatters and ");
						break;
				}
				pline_The("web %sis swept away!", sweep);
			}
			return(0);
		}                    /* default: a nasty jelly-like creature */
		pline_The("web dissolves into %s.", mon_nam(u.ustuck));
		return(0);
	}
	if (u.utrap) {
		You("cannot spin webs while stuck in a trap.");
		return(0);
	}
	exercise(A_DEX, TRUE);
	if (ttmp) switch (ttmp->ttyp) {
		case PIT:
		case SPIKED_PIT: You("spin a web, covering up the pit.");
			deltrap(ttmp);
			bury_objs(u.ux, u.uy);
			if (Invisible) newsym(u.ux, u.uy);
			return(1);
		case SQKY_BOARD: pline_The("squeaky board is muffled.");
			deltrap(ttmp);
			if (Invisible) newsym(u.ux, u.uy);
			return(1);
		case TELEP_TRAP:
		case LEVEL_TELEP:
		case MAGIC_PORTAL:
			Your("webbing vanishes!");
			return(0);
		case WEB: You("make the web thicker.");
			return(1);
		case HOLE:
		case TRAPDOOR:
			You("web over the %s.",
			    (ttmp->ttyp == TRAPDOOR) ? "trap door" : "hole");
			deltrap(ttmp);
			if (Invisible) newsym(u.ux, u.uy);
			return 1;
		case ARROW_TRAP:
		case DART_TRAP:
		case BEAR_TRAP:
		case LANDMINE:
		case SLP_GAS_TRAP:
		case RUST_TRAP:
		case MAGIC_TRAP:
		case ANTI_MAGIC:
		case POLY_TRAP:
			You("have triggered a trap!");
			dotrap(ttmp);
			return(1);
		default:
			impossible("Webbing over trap type %d?", ttmp->ttyp);
			return(0);
	}
	ttmp = maketrap(u.ux, u.uy, WEB);
	if (ttmp) {
		ttmp->tseen = 1;
		ttmp->madeby_u = 1;
	}
	if (Invisible) newsym(u.ux, u.uy);
	return(1);
}

int
dosummon()
{
	/* STEPHEN WHITE'S NEW CODE */        
	if(u.uen < 10) {
		You("lack the energy to send forth a call for help!");
		return(0);
		}
	You("call upon your brethren for help!");
	exercise(A_WIS, TRUE);
	if (!were_summon(uasmon,TRUE))
		pline("But none arrive.");
	u.uen -= 10;
	return(1);
}


#if 0
/* WAC supplanted by dogaze (). */
int
doconfuse()
{
	register struct monst *mtmp;
	int looked = 0;
	char qbuf[QBUFSZ];

	if (Blind) {
		You_cant("see anything to gaze at.");
		return 0;
	}
	for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
	    if (canseemon(mtmp)) {
		looked++;
		if (Invis && !perceives(mtmp->data))
		    pline("%s seems not to notice your gaze.", Monnam(mtmp));
		else if (mtmp->minvis && !See_invisible)
		    You_cant("see where to gaze at %s.", Monnam(mtmp));
		else if (mtmp->m_ap_type == M_AP_FURNITURE
			|| mtmp->m_ap_type == M_AP_OBJECT) {
		    looked--;
		    continue;
		} else if (flags.safe_dog && !Confusion && !Hallucination
		  && mtmp->mtame) {
		    if (mtmp->mnamelth)
			You("avoid gazing at %s.", NAME(mtmp));
		    else
			You("avoid gazing at your %s.",
						mtmp->data->mname);
		} else {
		    if (flags.confirm && mtmp->mpeaceful && !Confusion
							&& !Hallucination) {
			Sprintf(qbuf, "Really confuse %s?", mon_nam(mtmp));
			if (yn(qbuf) != 'y') continue;
			setmangry(mtmp);
		    }
		    if (!mtmp->mcanmove || mtmp->mstun || mtmp->msleep ||
				    !mtmp->mcansee || !haseyes(mtmp->data)) {
			looked--;
			continue;
		    }
		    if (!mon_reflects(mtmp,"Your gaze is reflected by %s %s.")){
			if (!mtmp->mconf)
			    Your("gaze confuses %s!", mon_nam(mtmp));
			else
			    pline("%s is getting more and more confused.",
							    Monnam(mtmp));
			mtmp->mconf = 1;
		    }
		    if ((mtmp->data==&mons[PM_FLOATING_EYE]) && !mtmp->mcan) {
			if (!Free_action) {
			You("are frozen by %s gaze!",
					 s_suffix(mon_nam(mtmp)));
			nomul((u.ulevel > 6 || rn2(4)) ?
				-d((int)mtmp->m_lev+1,
					(int)mtmp->data->mattk[0].damd)
				: -200);
			return 1;
			}
			else You("stiffen momentarily under %s gaze.",s_suffix(mon_nam(mtmp)));                    
		    }
		    if ((mtmp->data==&mons[PM_MEDUSA]) && !mtmp->mcan) {
			pline(
                         "Gazing at the awake Medusa is not a very good idea.");
			/* as if gazing at a sleeping anything is fruitful... */
			You("turn to stone...");
                        killer_format = KILLED_BY;
                        killer =
                         "deliberately gazing at Medusa's hideous countenance";
			done(STONING);
		    }
		}
	    }
	}
	if (!looked) You("gaze at no place in particular.");
	return 1;
}
#endif


int
dohide()
{
	if (u.uundetected || u.usym == S_MIMIC_DEF) {
		You("are already hiding.");
		return(0);
	}
	if (u.usym == S_MIMIC) {
		u.usym = S_MIMIC_DEF;
	} else {
		u.uundetected = 1;
	}
	newsym(u.ux,u.uy);
	return(1);
}

int
domindblast()
{
	struct monst *mtmp, *nmon;

	/* STEPHEN WHITE'S NEW CODE */
	if(u.uen < 2) {
		You("concentrate but lack the energy to maintain doing so.");
		return(0);
	} else You("concentrate.");
	if (!rn2(3)) return 1;
	u.uen -= 2;
	pline("A wave of psychic energy pours out.");
	for(mtmp=fmon; mtmp; mtmp = nmon) {
		int u_sen;

		nmon = mtmp->nmon;
		if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM)
			continue;
		if(mtmp->mpeaceful)
			continue;
		u_sen = telepathic(mtmp->data) && !mtmp->mcansee;
		if (u_sen || (telepathic(mtmp->data) && rn2(3)) || !rn2(2)) {
			You("lock in on %s %s.", s_suffix(mon_nam(mtmp)),
				u_sen ? "telepathy" :
				telepathic(mtmp->data) ? "latent telepathy" :
				"mind");
			mtmp->mhp -= rn1(4,4);
			if (mtmp->mhp <= 0)
				killed(mtmp);
		}
	}
	return 1;
}

static void
uunstick()
{
	pline("%s is no longer in your clutches.", Monnam(u.ustuck));
	u.ustuck = 0;
}

static void
skinback()
{
	if (uskin) {
		Your("skin returns to its original form.");
		uarm = uskin;
		uskin = (struct obj *)0;
		/* undo save/restore hack */
		uarm->owornmask &= ~I_SPECIAL;
	}
}

#endif /* OVLB */
#ifdef OVL1
const char *
body_part(part)
int part;
{
	static NEARDATA const char
	*humanoid_parts[] = { "arm", "eye", "face", "finger",
		"fingertip", "foot", "hand", "handed", "head", "leg",
		"light headed", "neck", "spine", "toe", "hair" },
	*jelly_parts[] = { "pseudopod", "dark spot", "front",
		"pseudopod extension", "pseudopod extremity",
		"pseudopod root", "grasp", "grasped", "cerebral area",
		"lower pseudopod", "viscous", "middle", "surface",
		"pseudopod extremity", "ripples" },
	*animal_parts[] = { "forelimb", "eye", "face", "foreclaw", "claw tip",
		"rear claw", "foreclaw", "clawed", "head", "rear limb",
		"light headed", "neck", "spine", "rear claw tip", "fur" },
	*horse_parts[] = { "forelimb", "eye", "face", "forehoof", "hoof tip",
		"rear hoof", "foreclaw", "hooved", "head", "rear limb",
		"light headed", "neck", "backbone", "rear hoof tip", "mane" },
	*sphere_parts[] = { "appendage", "optic nerve", "body", "tentacle",
		"tentacle tip", "lower appendage", "tentacle", "tentacled",
		"body", "lower tentacle", "rotational", "equator", "body",
		"lower tentacle tip", "cilia" },
	*fungus_parts[] = { "mycelium", "visual area", "front", "hypha",
		"hypha", "root", "strand", "stranded", "cap area",
		"rhizome", "sporulated", "stalk", "root", "rhizome tip",
		"spores" },
	*vortex_parts[] = { "region", "eye", "front", "minor current",
		"minor current", "lower current", "swirl", "swirled",
		"central core", "lower current", "addled", "center",
		"currents", "edge", "currents" },
	*snake_parts[] = { "vestigial limb", "eye", "face", "large scale",
		"large scale tip", "rear region", "scale gap", "scale gapped",
		"head", "rear region", "light headed", "neck", "length",
		"rear scale", "scales" };
	/* claw attacks are overloaded in mons[]; most humanoids with
	   such attacks should still reference hands rather than claws */
	static const char not_claws[] = {
		S_HUMAN, S_MUMMY, S_ZOMBIE, S_ANGEL,
		S_NYMPH, S_LEPRECHAUN, S_QUANTMECH, S_VAMPIRE,
		S_ORC, S_GIANT,         /* quest nemeses */
		'\0'            /* string terminator; assert( S_xxx != 0 ); */
	};

	if (part == HAND || part == HANDED) {   /* some special cases */
	    if (u.usym == S_DOG || u.usym == S_FELINE || u.usym == S_YETI)
		return part == HAND ? "paw" : "pawed";
	    if (humanoid(uasmon) && attacktype(uasmon, AT_CLAW) &&
		  !index(not_claws, u.usym) && u.umonnum != PM_STONE_GOLEM
		  && u.umonnum != PM_INCUBUS && u.umonnum != PM_SUCCUBUS)
		return part == HAND ? "claw" : "clawed";
	}
	if (humanoid(uasmon) && (part==ARM || part==FINGER || part==FINGERTIP
		|| part==HAND || part==HANDED)) return humanoid_parts[part];
	if (u.usym==S_CENTAUR || u.usym==S_UNICORN) return horse_parts[part];
	if (slithy(uasmon)) return snake_parts[part];
	if (u.usym==S_EYE) return sphere_parts[part];
	if (u.usym==S_JELLY || u.usym==S_PUDDING || u.usym==S_BLOB)
		return jelly_parts[part];
	if (u.usym==S_VORTEX || u.usym==S_ELEMENTAL) return vortex_parts[part];
	if (u.usym==S_FUNGUS) return fungus_parts[part];
	if (humanoid(uasmon)) return humanoid_parts[part];
	return animal_parts[part];
}

#endif /* OVL1 */
#ifdef OVL0

int
poly_gender()
{
/* Returns gender of polymorphed player; 0/1=same meaning as flags.female,
 * 2=none.
 * Used in:
 *      - Seduction by succubus/incubus
 *      - Talking to nymphs (sounds.c)
 * Not used in:
 *      - Messages given by nymphs stealing armor (they can't steal from
 *        incubi/succubi/nymphs, and nonhumanoids can't wear armor).
 *      - Amulet of change (must refer to real gender no matter what
 *        polymorphed into).
 *      - Priest/Priestess, Caveman/Cavewoman (ditto)
 *      - Polymorph self (only happens when human)
 *      - Shopkeeper messages (since referred to as "creature" and not "sir"
 *        or "lady" when polymorphed)
 */
	if (!humanoid(uasmon)) return 2;
	return flags.female;
}

#endif /* OVL0 */
#ifdef OVLB

void
ugolemeffects(damtype, dam)
int damtype, dam;
{
	int heal = 0;
	/* We won't bother with "slow"/"haste" since players do not
	 * have a monster-specific slow/haste so there is no way to
	 * restore the old velocity once they are back to human.
	 */
	if (u.umonnum != PM_FLESH_GOLEM && u.umonnum != PM_IRON_GOLEM)
		return;
	switch (damtype) {
		case AD_ELEC: if (u.umonnum == PM_IRON_GOLEM)
				heal = dam / 6; /* Approx 1 per die */
			break;
		case AD_FIRE: if (u.umonnum == PM_IRON_GOLEM)
				heal = dam;
			break;
	}
	if (heal && (u.mh < u.mhmax)) {
		u.mh += heal;
		if (u.mh > u.mhmax) u.mh = u.mhmax;
		flags.botl = 1;
		pline("Strangely, you feel better than before.");
		exercise(A_STR, TRUE);
	}
}

static int
armor_to_dragon(atyp)
int atyp;
{
	switch(atyp) {
	    case GRAY_DRAGON_SCALE_MAIL:
	    case GRAY_DRAGON_SCALES:
		return PM_GRAY_DRAGON;
	    case SILVER_DRAGON_SCALE_MAIL:
	    case SILVER_DRAGON_SCALES:
		return PM_SILVER_DRAGON;
	    case DEEP_DRAGON_SCALE_MAIL:
	    case DEEP_DRAGON_SCALES:
		return PM_DEEP_DRAGON;
	    case SHIMMERING_DRAGON_SCALE_MAIL:
	    case SHIMMERING_DRAGON_SCALES:
		return PM_SHIMMERING_DRAGON;
	    case RED_DRAGON_SCALE_MAIL:
	    case RED_DRAGON_SCALES:
		return PM_RED_DRAGON;
	    case ORANGE_DRAGON_SCALE_MAIL:
	    case ORANGE_DRAGON_SCALES:
		return PM_ORANGE_DRAGON;
	    case WHITE_DRAGON_SCALE_MAIL:
	    case WHITE_DRAGON_SCALES:
		return PM_WHITE_DRAGON;
	    case BLACK_DRAGON_SCALE_MAIL:
	    case BLACK_DRAGON_SCALES:
		return PM_BLACK_DRAGON;
	    case BLUE_DRAGON_SCALE_MAIL:
	    case BLUE_DRAGON_SCALES:
		return PM_BLUE_DRAGON;
	    case GREEN_DRAGON_SCALE_MAIL:
	    case GREEN_DRAGON_SCALES:
		return PM_GREEN_DRAGON;
	    case YELLOW_DRAGON_SCALE_MAIL:
	    case YELLOW_DRAGON_SCALES:
		return PM_YELLOW_DRAGON;
	    default:
		return -1;
	}
}


int
polyatwill()      /* Polymorph at will for Doppelganger class */
{
	int mon;
	boolean scales = ((uarm && uarm->otyp == RED_DRAGON_SCALE_MAIL   
				&& Role_if(PM_FLAME_MAGE)) ||
			  (uarm && uarm->otyp == WHITE_DRAGON_SCALE_MAIL 
				&& Role_if(PM_ICE_MAGE)));  

	/* KMH, balance patch -- new intrinsic */
	if (Unchanging) {
		pline("You cannot change your form.");
		return (0);
	}

	if (yn("Polymorph at will?") == 'n') return(0);

	if (Role_if(PM_DOPPELGANGER)) {
		if(!Upolyd) {
			if (u.uen < 10) pline("You don't have the energy to polymorph!");
			else {
				u.uen -= 10;
				if(Polymorph) {
					if (multi >= 0) {
						if (occupation) stop_occupation();
						else nomul(0);
					}
					polyself();
				}
			}
		} else rehumanize();
	} else if (Role_if(PM_HUMAN_WEREWOLF)) {
	    if (u.ulevel <= 2) You("can't polymorph at will yet.");
	    else if(!Upolyd) {
		if (u.uen < 10) pline("You don't have the energy to polymorph!");
		else {
		   u.uen -= 10;
		   if (Polymorph) {
		      if (multi >= 0) {
			 if (occupation) stop_occupation();
			 else nomul(0);
		      }
		      polyself();
		   } else if (u.ulycn >= 0) {
		      if(!Upolyd) {
			  if (multi >= 0) {
			     if (occupation) stop_occupation();
			     else nomul(0);
			  }
			  you_were();
		      } else rehumanize();
		   }
		}
	    } else if (u.uen > 9) rehumanize();
			else pline("You don't have the energy to polymorph!");
	} else if (Role_if(PM_ICE_MAGE) || Role_if(PM_FLAME_MAGE)) {
		if ((u.ulevel > 13 && u.uen > 49) || (scales && u.uen > 34)) {
		    if (scales) u.uen -= 35; else u.uen -= 50;
			if(!Upolyd) {
			   if (Role_if(PM_FLAME_MAGE)) mon = PM_RED_DRAGON;
			   else mon = PM_WHITE_DRAGON;
			   if (!(mons[mon].geno & G_GENOD) && scales) {
			      /* allow G_EXTINCT */
			      You("merge with your scaly armor.");
			      uskin = uarm;
			      uarm = (struct obj *)0;
			   }
			   polymon(mon);
			} else rehumanize();
		} else if (u.ulevel > 6 && u.uen > 24) {
		    u.uen -= 25;
		    if(!Upolyd) {        
			if (Role_if(PM_FLAME_MAGE)) mon = PM_BABY_RED_DRAGON;
			else mon = PM_BABY_WHITE_DRAGON;
			polymon(mon);
		    } else rehumanize();
		} else 
		    if (u.ulevel < 7) You("can't polymorph at will yet.");
		    else You("don't have the energy to polymorph.");
	} else {
		pline("You can't polymorph at will.");
		flags.botl = 1;
		return 0;
	}
	flags.botl = 1;
	return 1;
}


#if 0	/* What the f*** is this for? -- KMH */
static void
special_poly()
{
	char buf[BUFSZ];
	int old_light, new_light;
	int mntmp = NON_PM;
	int tries=0;

	old_light = (u.umonnum >= LOW_PM) ? emits_light(uasmon) : 0;
	do {
		getlin("Become what kind of monster? [type the name]", buf);
		mntmp = name_to_mon(buf);
		if (mntmp < LOW_PM)
				pline("I've never heard of such monsters.");
			/* Note:  humans are illegal as monsters, but an
			 * illegal monster forces newman(), which is what we
			 * want if they specified a human.... */

			/* [Tom] gnomes are polyok, so this doesn't apply for
			   player gnomes */
	                     /* WAC but we want to catch player gnomes and not
	                        so add an extra check */
		else if (!polyok(&mons[mntmp]) &&
				(Role_elven ? !is_elf(&mons[mntmp]) :
#ifdef DWARF
				Role_if(PM_DWARF) ? !is_gnome(&mons[mntmp]) :
#endif
				/* WAC
				 * should always fail (for now) gnome check
				 * unless gnomes become not polyok.  Then, it'll
				 * still work ;B
				 */
				Role_if(PM_GNOME) ? !is_gnome(&mons[mntmp]) :
				!is_human(&mons[mntmp])))
			You("cannot polymorph into that.");
#ifdef EATEN_MEMORY
		else if (!mvitals[mntmp].eaten && (rn2((u.ulevel + 25)) < 20)) {
			You("don't have the knowledge to polymorph into that.");
			return;  /* Nice try */
		} else {
			You("attempt an unfamiliar polymorph.");
			break;
		}
#endif
	} while(++tries < 5);
	if (tries==5) {
		pline(thats_enough_tries);
		return;
	} else if (polymon(mntmp)) {
		/* same as made_change above */
		new_light = (u.umonnum >= LOW_PM) ? emits_light(uasmon) : 0;
		if (old_light != new_light) {
		    if (old_light)
			del_light_source(LS_MONSTER, (genericptr_t)&youmonst);
		    if (new_light == 1) ++new_light;  /* otherwise it's undetectable */
		    if (new_light)
			new_light_source(u.ux, u.uy, new_light,
					 LS_MONSTER, (genericptr_t)&youmonst);
		}
	}
	return;
}
#endif


#endif /* OVLB */

/*polyself.c*/
