/*      SCCS Id: @(#)spell.c    3.1     93/05/15        */
/*      Copyright (c) M. Stephenson 1988                          */
/* NetHack may be freely redistributed.  See license for details. */

#include "hack.h"

static NEARDATA schar delay;            /* moves left for this spell */
static NEARDATA struct obj *book;       /* last/current book being xscribed */

#define spelluses(spell)        spl_book[spell-1].sp_uses
#define decrnuses(spell)        spl_book[spell-1].sp_uses--
#define spellev(spell)          spl_book[spell-1].sp_lev
#define spellname(spell)        OBJ_NAME(objects[spl_book[spell-1].sp_id])
#define spellid(spell)          spl_book[spell-1].sp_id

static void FDECL(cursed_book, (int));
static void FDECL(deadbook, (struct obj *));
STATIC_PTR int NDECL(learn);
static int NDECL(getspell);
static char FDECL(spellet, (int));
static char NDECL(dospellmenu);

static void
cursed_book(lev)
	register int    lev;
{
	switch(rn2(lev)) {
	case 0:
		You("feel a wrenching sensation.");
		tele();         /* teleport him */
		break;
	case 1:
		You("feel threatened.");
		aggravate();
		break;
	case 2:
		/* [Tom] lowered this (used to be 100,250) */
		make_blinded(Blinded + rn1(50,25),TRUE);
		break;
	case 3:
		take_gold();
		break;
	case 4:
		pline("These runes were just too much to comprehend.");
		make_confused(HConfusion + rn1(7,16),FALSE);
		break;
	case 5:
		pline("The book was coated with contact poison!");
		if (uarmg) {
		    /* Note: at this writing, there are no corrodeable
		     * gloves in the game.  If no one plans on adding
		     * copper gauntlets, most of this could be removed. -3.
		     */
		    if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {
			Your("gloves seem unaffected.");
		    } else if (uarmg->oeroded < MAX_ERODE) {
			Your("gloves corrode%s!",
			     uarmg->oeroded+1 == MAX_ERODE ? " completely" :
			     uarmg->oeroded ? " further" : "");
			uarmg->oeroded++;
		    } else
			Your("gloves %s completely corroded.",
			     Blind ? "feel" : "look");
		    break;
		}
		if(Poison_resistance) {
		    losestr(rn1(1,2));
		    losehp(rnd(6), "contact-poisoned spellbook", KILLED_BY_AN);
		} else {
		    losestr(rn1(4,3));
		    losehp(rnd(10), "contact-poisoned spellbook", KILLED_BY_AN);
		}
		break;
	case 6:
		if(Antimagic) {
		    shieldeff(u.ux, u.uy);
		    pline("The book explodes, but you are unharmed!");
		} else {
		    pline("As you read the book, it explodes in your %s!",
			body_part(FACE));
		    losehp (2*rnd(10)+5, "exploding rune", KILLED_BY_AN);
		}
		break;
	default:
		rndcurse();
		break;
	}
	return;
}

/* special effects for The Book of the Dead */
static void
deadbook(book2)
struct obj *book2;
{
    You("turn the pages of the Book of the Dead...");
    makeknown(SPE_BOOK_OF_THE_DEAD);
    if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
	register struct obj *otmp;
	register boolean arti1_primed = FALSE, arti2_primed = FALSE,
			 arti_cursed = FALSE;

	if(book2->cursed) {
	    pline("The runes appear scrambled.  You can't read them!");
	    return;
	}

	if(!u.uhave.bell || !u.uhave.menorah) {
	    pline("A chill runs down your %s.", body_part(SPINE));
	    if(!u.uhave.bell) You("hear a faint chime...");
	    if(!u.uhave.menorah) pline("Vlad's doppelganger is amused.");
	    return;
	}

	for(otmp = invent; otmp; otmp = otmp->nobj) {
	    if(otmp->otyp == CANDELABRUM_OF_INVOCATION &&
	       otmp->spe == 7 && otmp->lamplit) {
		if(!otmp->cursed) arti1_primed = TRUE;
		else arti_cursed = TRUE;
	    }
	    if(otmp->otyp == BELL_OF_OPENING &&
	       (moves - otmp->age) < 5L) { /* you rang it recently */
		if(!otmp->cursed) arti2_primed = TRUE;
		else arti_cursed = TRUE;
	    }
	}

	if(arti_cursed) {
	    pline("The invocation fails!");
	    pline("At least one of your artifacts is cursed...");
	} else if(arti1_primed && arti2_primed) {
	    mkinvokearea();
	    u.uevent.invoked = 1;
	} else {        /* at least one artifact not prepared properly */
	    You("have a feeling that something is amiss...");
	    goto raise_dead;
	}
	return;
    }

    /* when not an invocation situation */
    if(book2->cursed)
raise_dead:
    {
	register struct monst *mtmp;
	coord mm;

	You("raised the dead!");
	mm.x = u.ux;
	mm.y = u.uy;
	mkundead(&mm);
	if(!rn2(4))
	    if ((mtmp = makemon(&mons[PM_MASTER_LICH],u.ux,u.uy)) != 0) {
		mtmp->mpeaceful = 0;
		set_malign(mtmp);
	    }
    } else if(book2->blessed) {
	register struct monst *mtmp, *mtmp2;

	for(mtmp = fmon; mtmp; mtmp = mtmp2) {
	    mtmp2 = mtmp->nmon;         /* tamedog() changes chain */
	    if(is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {
		mtmp->mpeaceful = TRUE;
		if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
		   && distu(mtmp->mx, mtmp->my) < 4)
		    if (mtmp->mtame)
			mtmp->mtame++;
		    else
			(void) tamedog(mtmp, (struct obj *)0);
		else mtmp->mflee = TRUE;
	    }
	}
    } else {
	switch(rn2(3)) {
	case 0: 
	    Your("ancestors are annoyed with you!"); 
	    break;
	case 1: 
	    pline("The headstones in the cemetery begin to move!");
	    break;
	default:
	    pline("Oh my!  Your name appears in the book!");
	}
    }
    return;
}

STATIC_PTR
int
learn()
{
	register int    i;
	register unsigned booktype;

	if (delay) {    /* not if (delay++), so at end delay == 0 */
		delay++;
		return(1); /* still busy */
	}
	exercise(A_WIS, TRUE);          /* you're studying. */
	booktype = book->otyp;
	if(booktype == SPE_BOOK_OF_THE_DEAD) {
	    deadbook(book);
	    return(0);
	}

	for (i = 0; i < MAXSPELL; i++)  {
		if (spl_book[i].sp_id == booktype)  {
			if (book->spestudied >= rn1(1,30-spl_book[i].sp_lev)) {
			    pline("This spellbook is too faint to be read anymore.");
			    book->otyp = booktype = SPE_BLANK_PAPER;
			    makeknown((int)booktype);
			}
			else if (spl_book[i].sp_uses < 20-spl_book[i].sp_lev) {
			    Your("knowledge of that spell is keener.");
			    spl_book[i].sp_uses += 10-spl_book[i].sp_lev;
			    book->spestudied++;
			    exercise(A_WIS, TRUE);      /* extra study */
			} else
			    You("know that spell quite well already.");
			break;
		} else if (spl_book[i].sp_id == NO_SPELL)  {
			spl_book[i].sp_id = booktype;
			spl_book[i].sp_lev = objects[booktype].oc_level;
			/* spells have 1 .. 9-level uses. */
			/* ie 1 or 2 uses w/ most potent */
			spl_book[i].sp_uses = 30-spl_book[i].sp_lev;
			book->spestudied++;
			You("add the spell to your repertoire.");
			makeknown((int)booktype);
			break;
		}
	}
	if (i == MAXSPELL) impossible("Too many spells memorized!");

	if (book->cursed) {     /* maybe a demon cursed it */
		cursed_book(objects[booktype].oc_level);
	}
	check_unpaid(book);
	book = 0;
	return(0);
}

int
study_book(spellbook)
register struct obj *spellbook;
{
	register int     booktype = spellbook->otyp;
	register boolean confused = (Confusion != 0);

	if (delay && spellbook == book)
		You("continue your efforts to memorize the spell.");
	else {
		switch(booktype)  {

/* blank spellbook */
	case SPE_BLANK_PAPER:
		pline("This spellbook is all blank.");
		makeknown(SPE_BLANK_PAPER);
		return(1);
/* level 1 spells */
	case SPE_HEALING:
	case SPE_DETECT_MONSTERS:
	case SPE_FORCE_BOLT:
	case SPE_LIGHT:
	case SPE_SLEEP:
	case SPE_KNOCK:
	case SPE_WIZARD_LOCK:
	case SPE_DETECT_FOOD:
	case SPE_RESIST_POISON:
	case SPE_RESIST_SLEEP:
/* level 2 spells */
	case SPE_MAGIC_MISSILE:
	case SPE_CONFUSE_MONSTER:
	case SPE_SLOW_MONSTER:
	case SPE_CURE_BLINDNESS:
	case SPE_CREATE_MONSTER:
	case SPE_ENDURE_COLD:
	case SPE_ENDURE_HEAT:
	case SPE_INSULATE:
		delay = -objects[booktype].oc_delay;
		break;
/* level 3 spells */
	case SPE_HASTE_SELF:
	case SPE_CAUSE_FEAR:
	case SPE_CURE_SICKNESS:
	case SPE_DETECT_UNSEEN:
	case SPE_EXTRA_HEALING:
	case SPE_CHARM_MONSTER:
	case SPE_CLAIRVOYANCE:
	case SPE_IDENTIFY:
/* level 4 spells */
	case SPE_LEVITATION:
	case SPE_RESTORE_ABILITY:
	case SPE_INVISIBILITY:
	case SPE_FIREBALL:
	case SPE_ENLIGHTEN:
	case SPE_DETECT_TREASURE:
		delay = -(objects[booktype].oc_level - 1) * objects[booktype].oc_delay;
		break;
/* level 5 spells */
	case SPE_REMOVE_CURSE:
	case SPE_SUMMON_UNDEAD:
	case SPE_COMMAND_UNDEAD:
	case SPE_MAGIC_MAPPING:
	case SPE_CONE_OF_COLD:
	case SPE_DIG:
/* level 6 spells */
	case SPE_TURN_UNDEAD:
	case SPE_POLYMORPH:
	case SPE_CREATE_FAMILIAR:
	case SPE_TELEPORT_AWAY:
		delay = -objects[booktype].oc_level * objects[booktype].oc_delay;
		break;
/* level 7 spells */
	case SPE_CANCELLATION:
	case SPE_FINGER_OF_DEATH:
	case SPE_BOOK_OF_THE_DEAD:
	case SPE_ENCHANT_WEAPON:
	case SPE_ENCHANT_ARMOR:
		delay = -8 * objects[booktype].oc_delay;
		break;
/* impossible */
	default:
		impossible("Unknown spellbook, %d;", booktype);
		return(0);
	}

		/* Books are often wiser than their readers (Rus.) */
		if(!spellbook->blessed &&
			spellbook->otyp != SPE_BOOK_OF_THE_DEAD &&
			(spellbook->cursed ||
			    rn2(20) > (ACURR(A_INT) + 4 + (int)(u.ulevel/2)
					- 2*objects[booktype].oc_level))) {
			cursed_book(objects[booktype].oc_level);
			nomul(delay);                   /* study time */
			delay = 0;
			if(!rn2(3)) {
				useup(spellbook);
				pline("The spellbook crumbles to dust!");
			}
			return(1);
		}
		else if(confused) {
			if(!rn2(3) && 
			    spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
				useup(spellbook);
				pline("Being confused you have difficulties in controlling your actions.");
				display_nhwindow(WIN_MESSAGE, FALSE);
				You("accidentally tear the spellbook to pieces.");
			}
			else
				You("find yourself reading the first line over and over again.");
			nomul(delay);
			delay = 0;
			return(1);
		}

		You("begin to %s the runes.",
		    spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" :
		    "memorize");
	}

	book = spellbook;
	set_occupation(learn, "studying", 0);
	return(1);
}

static int
getspell()
{
	register int    maxs, ilet, i;
	char     lets[BUFSZ], buf[BUFSZ], qbuf[QBUFSZ];

	if (spl_book[0].sp_id == NO_SPELL)  {

		You("don't know any spells right now.");
		return(0);
	} else  {

	    for(maxs = 1; (maxs < MAXSPELL) && (spl_book[maxs].sp_id != NO_SPELL); maxs++);
	    if (maxs >= MAXSPELL)  {

		impossible("Too many spells memorized.");
		return(0);
	    }

	    for(i = 0; (i < maxs) && (i < 26); buf[++i] = 0)  buf[i] = 'a' + i;
	    for(i = 26; (i < maxs) && (i < 52); buf[++i] = 0) buf[i] = 'A' + i - 26;

	    if (maxs == 1)  Strcpy(lets, "a");
	    else if (maxs < 27)  Sprintf(lets, "a-%c", 'a' + maxs - 1);
	    else if (maxs == 27)  Sprintf(lets, "a-z A");
	    else Sprintf(lets, "a-z A-%c", 'A' + maxs - 27);
	    for(;;)  {

		Sprintf(qbuf, "Cast which spell? [%s ?]", lets);
		if ((ilet = yn_function(qbuf, NULL, '\0')) == '?') {
			ilet = dospellmenu();
			if(!ilet)
			    continue;
		}
		if (index(quitchars, ilet))
			return(0);
		else for(i = 0; buf[i] != 0; i++)
		    if(ilet == buf[i])  return(++i);
		You("don't know that spell.");
	    }
	}
}

int
docast()
{
	register int     spell;

	spell = getspell();
	if (!spell) return(0);

	return(spelleffects(spell,FALSE));
}

int
spelleffects(spell,atme)
register int spell;
boolean atme;
{
	register int energy, damage, splcaster, statused,
		     chanch, roll, special;
	boolean confused = (Confusion != 0);
	struct obj *pseudo;
	int easy_spell,fake_spell_lev;

	/* note that trying to cast it decrements the # of uses,    */
	/* even if the mage does not have enough food/energy to use */
	/* the spell */
	switch (spelluses(spell)) {
		case 0:
		    pline ("Curdled magical energy twists through you...");
		    pline ("...you have overloaded and burned out this spell.");
		    make_confused((long)spellev(spell) * 3, FALSE);
		    return(0);
		case 1:
		case 2:
		case 3:
		    Your("nerves tingle warningly.");
		    break;
		case 4:
		case 5:
		case 6:
		    pline ("This spell is starting to be over-used.");
		    break;
		default:
		    break;
	}
	decrnuses(spell);
	energy = (spellev(spell) * 5);    /* 5 <= energy <= 35 */

	if (u.uhunger <= 10 && spellid(spell) != SPE_DETECT_FOOD) {
		You("are too hungry to cast that spell.");
		return(0);
	} else if (ACURR(A_STR) < 4)  {
		You("lack the strength to cast spells.");
		return(0);
	} else if(check_capacity(
		"Your concentration falters while carrying so much stuff.")) {
		return (1);
	}
	
	if (u.uhave.amulet) {
		You("feel the amulet draining your energy away.");
		energy *= rnd(3);
	}
	if(energy > u.uen)  {
		You("don't have enough energy to cast that spell.");
		return(0);
	} else {
/* [Tom] removed this stupid food thing
 *              if (spellid(spell) != SPE_DETECT_FOOD)
 *                      morehungry(energy * 2);
 */
	}
	flags.botl = 1;

	easy_spell = 0;
	switch  (pl_character[0]) {

	    case 'A':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			statused = ACURR(A_INT);
			break;
	    case 'B':   splcaster =17;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'C':   splcaster =17;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'D':   splcaster = 15;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'E':   splcaster = 9;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'F':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			if (spellid(spell) == SPE_FIREBALL) easy_spell = 1;
			statused = ACURR(A_INT);
			break;
	    case 'G':   splcaster = 11;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'H':   splcaster = 7;
			special = -2;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_WIS);
			break;
	    case 'I':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			if (spellid(spell) == SPE_CONE_OF_COLD) easy_spell = 1;
			statused = ACURR(A_INT);
			break;
	    case 'K':   splcaster = 13;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'L':   splcaster = 17;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'M':   splcaster = 9;
			special = -2;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_WIS);
			break;
	    case 'N':   splcaster = 7;
			special = 2;
			if (spellid(spell) == SPE_SUMMON_UNDEAD) easy_spell = 1;
			if (spellid(spell) == SPE_COMMAND_UNDEAD) easy_spell = 1;
			if (spellid(spell) == SPE_TURN_UNDEAD) easy_spell = 1;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_INT);
			break;
	    case 'P':   splcaster = 7;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'R':   splcaster = 13;
			special = 2;
			if (uarms) splcaster += 2;
			statused = ACURR(A_INT);
			break;
	    case 'S':   splcaster =17;
			special = 2;
			statused = ACURR(A_INT);
			break;
#ifdef TOURIST
	    case 'T':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			statused = ACURR(A_INT);
			break;
#endif
	    case 'U':   splcaster = 13;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'V':   splcaster =13;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'W':   splcaster = 7;
			special = 2;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||  
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_INT);
			break;
	    default:    splcaster = 10;
			special = 2;
			statused = ACURR(A_INT);
			break;
	}
	if (spellid(spell) == SPE_HEALING ||
	    spellid(spell) == SPE_EXTRA_HEALING ||
	    spellid(spell) == SPE_CURE_BLINDNESS ||
	    spellid(spell) == SPE_CURE_SICKNESS ||
	    spellid(spell) == SPE_RESTORE_ABILITY ||
	    spellid(spell) == SPE_REMOVE_CURSE ||
	    spellid(spell) == SPE_ENLIGHTEN) splcaster += special;

	if (uarm && uarm->otyp == ROBE_OF_POWER) splcaster -= 3;
	
	if (uarms && uarms->otyp != SMALL_SHIELD) {
		You("can't cast spells and use a large shield!");
		return(0);
	}
	
	if(splcaster < 5) splcaster = 5;
	if(splcaster > 20) splcaster = 20;
	
	fake_spell_lev = spellev(spell);

	if (easy_spell) fake_spell_lev = (fake_spell_lev / 3) + 1;

	chanch = ((5.5 * statused) - (splcaster * fake_spell_lev));
	if ((fake_spell_lev * 2) > (u.ulevel + 1))
		chanch -= ((fake_spell_lev * 2) - (u.ulevel + 1)) * 15;
	if (chanch < 0) chanch = 0;
	roll = rnd(100);

	if (confused ||
	   (roll > chanch)) {
		pline("You fail to cast the spell correctly.");
		u.uen -= (energy * 0.50);
		return(1);
	}

	u.uen -= energy;
	exercise(A_WIS, TRUE);
/*      pseudo is a temporary "false" object containing the spell stats. */
	pseudo = mksobj(spellid(spell), FALSE, FALSE);
	pseudo->blessed = pseudo->cursed = 0;
	pseudo->quan = 20L;                     /* do not let useup get it */
	switch(pseudo->otyp)  {

/* These spells are all duplicates of wand effects */
	case SPE_FORCE_BOLT:
	case SPE_SLEEP:
	case SPE_MAGIC_MISSILE:
	case SPE_KNOCK:
	case SPE_SLOW_MONSTER:
	case SPE_WIZARD_LOCK:
	case SPE_FIREBALL:
	case SPE_CONE_OF_COLD:
	case SPE_DIG:
	case SPE_TURN_UNDEAD:
	case SPE_POLYMORPH:
	case SPE_TELEPORT_AWAY:
	case SPE_CANCELLATION:
	case SPE_FINGER_OF_DEATH:
	case SPE_LIGHT:
	case SPE_DETECT_UNSEEN:
	case SPE_HEALING:
	case SPE_EXTRA_HEALING:
		if (!(objects[pseudo->otyp].oc_dir == NODIR)) {
			if (atme) u.dx = u.dy = u.dz = 0;
			else (void) getdir(NULL);
			if(!u.dx && !u.dy && !u.dz) {
			    if((damage = zapyourself(pseudo)))
				losehp(damage, 
		self_pronoun("zapped %sself with a spell", "him"),
					NO_KILLER_PREFIX);
			} else  weffects(pseudo);
		} else weffects(pseudo);
		break;
/* These are all duplicates of scroll effects */
	case SPE_CONFUSE_MONSTER:
	case SPE_DETECT_FOOD:
	case SPE_CAUSE_FEAR:
	case SPE_CHARM_MONSTER:
	case SPE_REMOVE_CURSE:
	case SPE_MAGIC_MAPPING:
	case SPE_CREATE_MONSTER:
	case SPE_IDENTIFY:
	case SPE_COMMAND_UNDEAD:
	case SPE_SUMMON_UNDEAD:
		(void) seffects(pseudo);
		break;
	case SPE_ENCHANT_WEAPON:                
	case SPE_ENCHANT_ARMOR:
		if (!rn2(10)) 
			(void) seffects(pseudo);
		else Your("enchantment failed!");                                               
		break;
	case SPE_HASTE_SELF:
	case SPE_DETECT_TREASURE:
	case SPE_DETECT_MONSTERS:
	case SPE_LEVITATION:
	case SPE_RESTORE_ABILITY:
	case SPE_INVISIBILITY:
		(void) peffects(pseudo);
		break;
	case SPE_CURE_BLINDNESS:
		healup(0, 0, FALSE, TRUE);
		break;
	case SPE_CURE_SICKNESS:
		if (Sick) You("are no longer ill.");
		healup(0, 0, TRUE, FALSE);
		break;
	case SPE_CREATE_FAMILIAR:
		make_familiar((struct obj *)0, u.ux, u.uy);
		break;
	case SPE_CLAIRVOYANCE:
		do_vicinity_map();
		break;
	case SPE_RESIST_POISON:
		if(!(HPoison_resistance & FROMOUTSIDE)) {
			You("feel healthy ..... for the moment at least.");
			HPoison_resistance += rn1(100,50);
		} else pline("Hmmm ... nothing seems to have happened.");
		break;
	case SPE_RESIST_SLEEP:
		if(!(HSleep_resistance & FROMOUTSIDE)) {
			if (Hallucination)
				pline("Too much coffee!");
			else
				You("no longer feel tired.");
			HSleep_resistance += rn1(100,50);
		}
		break;
	case SPE_ENDURE_COLD:
		if(!(HCold_resistance & FROMOUTSIDE)) {
			You("feel warmer.");
			HCold_resistance += rn1(100,50);
		}
		break;
	case SPE_ENDURE_HEAT:
		if(!(HFire_resistance & FROMOUTSIDE)) {
			if (Hallucination)
				pline("Excellent! You feel, like, totally cool!");
			else
				You("feel colder.");
			HFire_resistance += rn1(100,50);
		}
		break;
	case SPE_INSULATE:
		if(!(HShock_resistance & FROMOUTSIDE)) {
			if (Hallucination)
				pline("Bummer! You've been grounded!");
			else
				You("are not at all shocked by this feeling.");
			HShock_resistance += rn1(100,50);
		}
		break;
	case SPE_ENLIGHTEN: 
		You("feel self-knowledgeable...");
		display_nhwindow(WIN_MESSAGE, FALSE);
		enlightenment(FALSE);
		pline("The feeling subsides.");
		exercise(A_WIS, TRUE);
		break;

	default:
		impossible("Unknown spell %d attempted.", spell);
		obfree(pseudo, (struct obj *)0);
		return(0);
	}
	obfree(pseudo, (struct obj *)0);        /* now, get rid of it */
	return(1);
}

void
losespells() {
	register boolean confused = (Confusion != 0);
	register int     n, nzap, i;

	book = 0;
	for(n = 0;(spl_book[n].sp_id != NO_SPELL) && (n < MAXSPELL); n++);
	if (!n) return;
	if (n < MAXSPELL) {
		nzap = rnd(n);
		if (nzap < n) nzap += confused;
		for (i = 0; i < nzap; i++) {
		    spl_book[n-i-1].sp_id = NO_SPELL;
		    exercise(A_WIS, FALSE);     /* ouch! */
		}
	} else impossible("Too many spells memorized!");
	return;
}

static char
spellet(spl)
int spl;
{
	return((char)((spl < 27) ? ('a' + spl - 1) : ('A' + spl - 27)));
}

int
dovspell()
{
    (void) dospellmenu();
    return 0;
}

static char
dospellmenu()
{
	winid tmpwin;
	register int maxs, i, k, fail, statused, splcaster, length, special;
	char rval;
	char     buf[BUFSZ], buf2[BUFSZ];
	int easy_spell,fake_spell_lev;

	if (spl_book[0].sp_id == NO_SPELL)  {

		You("don't know any spells right now.");
		return 0;
	}

	for(maxs = 1; (maxs < MAXSPELL) && (spl_book[maxs].sp_id != NO_SPELL); maxs++);
	if (maxs >= MAXSPELL)  {

		impossible("Too many spells memorized.");
		return 0;
	}
	tmpwin = create_nhwindow(NHW_MENU);
	start_menu(tmpwin);
	add_menu(tmpwin, 0, 0, "Currently known spells:");
	add_menu(tmpwin, 0, 0, "(L)evel, (F)ail");
	add_menu(tmpwin, 0, 0, "");



	for(i = 1; i <= maxs; i++) {
	easy_spell = 0;

	switch  (pl_character[0]) {

	    case 'A':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			statused = ACURR(A_INT);
			break;
	    case 'B':   splcaster =17;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'C':   splcaster =17;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'D':   splcaster = 9;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'E':   splcaster = 9;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'F':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			if (spellid(i) == SPE_FIREBALL) easy_spell = 1;
			statused = ACURR(A_INT);
			break;
	    case 'G':   splcaster = 11;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'H':   splcaster = 7;
			special = -2;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_WIS);
			break;
	    case 'I':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			if (spellid(i) == SPE_CONE_OF_COLD) easy_spell = 1;
			statused = ACURR(A_INT);
			break;
	    case 'K':   splcaster = 13;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'L':   splcaster = 17;
			special = 2;
			statused = ACURR(A_INT);
			break;
	    case 'M':   splcaster = 9;
			special = -2;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_WIS);
			break;
	    case 'N':   splcaster = 7;
			special = 2;
			if (spellid(i) == SPE_SUMMON_UNDEAD) easy_spell = 1;
			if (spellid(i) == SPE_COMMAND_UNDEAD) easy_spell = 1;
			if (spellid(i) == SPE_TURN_UNDEAD) easy_spell = 1;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_INT);
			break;
	    case 'P':   splcaster = 7;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'R':   splcaster = 13;
			special = 2;
			if (uarms) splcaster += 2;
			statused = ACURR(A_INT);
			break;
	    case 'S':   splcaster =17;
			special = 2;
			statused = ACURR(A_INT);
			break;
#ifdef TOURIST
	    case 'T':   splcaster = 9;
			special = 2;
			if (uarms) splcaster += 2;
			statused = ACURR(A_INT);
			break;
#endif
	    case 'U':   splcaster = 13;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'V':   splcaster =13;
			special = -2;
			statused = ACURR(A_WIS);
			break;
	    case 'W':   splcaster = 7;
			special = 2;
			if (uarms) splcaster += 2;
			if (uarm && !(uarm->otyp == ROBE ||
				      uarm->otyp == ROBE_OF_POWER ||
				      uarm->otyp == ROBE_OF_PROTECTION))
				 splcaster += 5;
			statused = ACURR(A_INT);
			break;
	    default:    splcaster = 10;
			special = 2;
			statused = ACURR(A_INT);
			break;
	}
	if (spellid(i) == SPE_HEALING ||
	    spellid(i) == SPE_EXTRA_HEALING ||
	    spellid(i) == SPE_CURE_BLINDNESS ||
	    spellid(i) == SPE_CURE_SICKNESS ||
	    spellid(i) == SPE_RESTORE_ABILITY ||
	    spellid(i) == SPE_REMOVE_CURSE ||
	    spellid(i) == SPE_ENLIGHTEN) splcaster += special;

	if (uarm && uarm->otyp == ROBE_OF_POWER) splcaster -= 3;
	if(splcaster < 5) splcaster = 5;
	if(splcaster > 20) splcaster = 20;

	fake_spell_lev = spellev(i);

	if (easy_spell) fake_spell_lev = ( fake_spell_lev / 3 ) + 1;

	fail = 100 - ((5.5 * statused) - (splcaster * fake_spell_lev));
	if ((fake_spell_lev * 2) > (u.ulevel + 1))
		fail += ((fake_spell_lev * 2) - (u.ulevel + 1)) * 15;
	if (uarms && uarms->otyp != SMALL_SHIELD) fail = 100;
	if (fail > 100) fail = 100;
	if (fail < 0) fail = 0;

		Sprintf(buf, "%c %c %s",
		       spellet(i), (spelluses(i)) ? '-' : '*', spellname(i));

		Sprintf(buf2, "(L:%d F:%d%%)", spellev(i), fail);
		
		length = 19 - strlen(buf);
		if (length < 1) length = 1;

		for(k = 0; k <= length; k++) Strcat(buf, " ");

		Strcat(buf, buf2);

		add_menu(tmpwin, spellet(i), 0, buf);
	}
	end_menu(tmpwin, '\0', "\033 ", NULL);
	rval = select_menu(tmpwin);
	destroy_nhwindow(tmpwin);

	return rval;
}

/*spell.c*/

