/*	SCCS Id: @(#)spell.c	1.3	87/07/14
/* hack.spell.c - version 1.0.1		M. Stephenson 07-04-86 */

#include "hack.h"
#ifdef SPELLS
extern char *nomovemsg;

doxcribe() {
	register struct obj *book;
	struct	 obj	*getobj();
	register boolean confused = (Confusion != 0);
	register boolean oops;
	register schar	 delay;
	register int   booktype;
	register int	 i;

	book = getobj("+", "transcribe");
	if(!book) return(0);

	if(Blind) {
	    pline("Being blind, you cannot read the mystic runes.");
	    useup(book);		/* well, if you are stupid... */
	    return(0);
	}

	if(confused) {
	    pline("Being confused, you cannot grasp the meaning of this tome.");
	    useup(book);		/* and more stupidity... */
	    return(0);
	}
	booktype = book->otyp;
	oops = !rn2(u.ulevel - objects[booktype].spl_lev + 7);
	switch(booktype)  {

/* level 1 spells */
	case SPE_HEALING:
	case SPE_DETECT_MONSTERS:
	case SPE_FORCE_BOLT:
	case SPE_LIGHT:
	case SPE_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_DETECT_FOOD:
		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:
/* level 4 spells */
	case SPE_LEVITATION:
	case SPE_RESTORE_STRENGTH:
	case SPE_INVISIBILITY:
	case SPE_FIREBALL:
	case SPE_DETECT_TREASURE:
		delay = -(objects[booktype].spl_lev - 1) * objects[booktype].oc_delay;
		break;
/* level 5 spells */
	case SPE_REMOVE_CURSE:
	case SPE_MAGIC_MAPPING:
	case SPE_CONE_OF_COLD:
	case SPE_IDENTIFY:
	case SPE_DIG:
/* level 6 spells */
	case SPE_TURN_UNDEAD:
	case SPE_POLYMORPH:
	case SPE_CREATE_FAMILIAR:
	case SPE_TELEPORT_AWAY:
		delay = -objects[booktype].spl_lev * objects[booktype].oc_delay;
		break;
/* level 7 spells */
	case SPE_CANCELLATION:
	case SPE_FINGER_OF_DEATH:
	case SPE_GENOCIDE:
		delay = -8 * objects[booktype].oc_delay;
		break;
/* impossible */
	default:
		impossible("Unknown spell-book, %d;", booktype);
		return(0);
	}

	pline("You begin to transcribe the spell.");
	if(oops || book->cursed)  {
		cursed_book(objects[booktype].spl_lev);
		nomul(delay);			/* study time */
	} else  {
		nomul(delay);			/* study time */
		for(i = 0; i < MAXSPELL; i++)  {
			if(spl_book[i].sp_id == booktype)  {
				nomovemsg = "Oh, you already know that one!";
				useup(book);
				return(1);
			} else if (spl_book[i].sp_id == NO_SPELL)  {
				spl_book[i].sp_id = booktype;
				spl_book[i].sp_lev = objects[booktype].spl_lev;
				spl_book[i].sp_flags = objects[booktype].bits;
				nomovemsg = "You add the spell to your books.";
				objects[booktype].oc_name_known = 1;
				useup(book);
				return(1);
			}
		}
		impossible("Too many spells in spellbook!");
	}
	useup(book);
	return(1);
}

cursed_book(level)
	register int	level;
{
	register int	nobj, cnt, onum;
	register struct obj	*otmp;

	switch(rnd(level)) {
	case 0:
		pline("you feel a wrenching sensation.");
		tele();		/* teleport him */
		break;
	case 1:
		pline("you feel threatened.");
		aggravate();
		break;
	case 2:
		if(!Blind)	pline("a cloud of darkness falls upon you.");
		Blind += rn1(100,250);
		seeoff(0);
		break;
	case 3:
		if (u.ugold <= 0)  {
			pline("you feel a strange sensation.");
		} else {
			pline("you notice you have no gold!");
			u.ugold = 0;
			flags.botl = 1;
		}
		break;
	case 4:
		pline("These runes were just too much to comprehend.");
		HConfusion += rn1(7,16);
		break;
	case 5:
		pline("The book was coated with contact poison!");
		if(Poison_resistance) {
		    losestr(rn1(1,2));
		    losehp(rnd(6), "contact poison");
		} else {
		    losestr(rn1(4,3));
		    losehp(rnd(10), "contact poison");
		}
		break;
	case 6:
		pline("As you read the book, it explodes in your face!");
		losehp (2*rnd(10)+5, "exploding rune");
		break;
	case 7:
		/* curse a few inventory items at random! */
		nobj = 0;
		for (otmp = invent; otmp; otmp = otmp->nobj)  nobj++;

		for (cnt = rnd(6); cnt > 0; cnt--)  {

			onum = rn2(nobj);
			for(otmp = invent; onum != 0; onum--) otmp = otmp->nobj;
			otmp->cursed++;
		}
		break;
	}
	return(0);
}

docast()
{
	register int	 spell, energy;
	register boolean confused = (Confusion != 0);
	register struct  obj	*pseudo;
	struct	 obj	 *mksobj();

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

		energy = spellev(spell);
		if(energy > u.uen)  {
			pline("You are too weak to cast that spell.");
			return(0);
		} else  if ((u.uhunger <= 100) || (u.ustr < 6))  {
			pline("You miss the strength for that spell.");
			return(0);
		} else	{
			morehungry(energy * 10);
			u.uen -= energy;
		}
	}
#ifdef HARD
	if ((rn2(10) + u.ulevel + u.uluck - spellev(spell)) < 0) {

		pline("Fizzle.....");
		return(0);
	}
#endif

/*	pseudo is a temporary "false" object containing the spell stats. */
	pseudo = mksobj(spellid(spell));
	pseudo->quan = 20;			/* 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_SLOW_MONSTER:
	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:
		if (!(objects[pseudo->otyp].bits & NODIR))	getdir(1);
		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_GENOCIDE:
		seffects(pseudo);
		break;
	case SPE_HASTE_SELF:
	case SPE_DETECT_TREASURE:
	case SPE_DETECT_MONSTERS:
	case SPE_LEVITATION:
	case SPE_RESTORE_STRENGTH:
	case SPE_INVISIBILITY:
		peffects(pseudo);
		break;
	case SPE_HEALING:
		pline("You feel a bit better.");
		healup(rnd(8), 1, 0, 0);
		break;
	case SPE_CURE_BLINDNESS:
		healup(0, 0, 0, 1);
		break;
	case SPE_CURE_SICKNESS:
		pline("You are no longer ill.");
		healup(0, 0, 1, 0);
		break;
	case SPE_EXTRA_HEALING:
		pline("You feel a fair bit better.");
		healup(d(2,8), 1, 0, 0);
		break;
	case SPE_CREATE_FAMILIAR:
		{	register struct monst *mtmp;
			struct   monst  *makedog();

			mtmp = makedog();
			if(mtmp) {
				/* make it into something else */
				(void) newcham(mtmp, &mons[dlevel+14+rn2(CMNUM-14-dlevel)]);
				if(confused)
					mtmp->mtame = mtmp->mpeaceful = 0;
			}
		}
		break;
	default:
		impossible("Unknown spell %d attempted.", spell);
		obfree(pseudo, (struct obj *)0);
		return(0);
	}
	flags.botl = 1;
	obfree(pseudo, (struct obj *)0);	/* now, get rid of it */
	return(1);
}

getspell()  {

	register int	max, ilet, i;
	char	 lets[BUFSZ], buf[BUFSZ];

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

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

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

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

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

	    if (max == 1)  strcpy(lets, "a");
	    else if (max < 27)  sprintf(lets, "a-%c", 'a' + max - 1);
	    else if (max == 27)  sprintf(lets, "a-z A");
	    else sprintf(lets, "a-z A-%c", 'A' + max - 27);
	    for(;;)  {

		pline("Cast which spell [%s ?]: ", lets);
		if ((ilet = readchar()) == '?')  {
			dovspell();
			continue;
		} else if ((ilet == '\033')||(ilet == '\n')||(ilet == ' '))
			return(0);
		else for(i = 0; buf[i] != 0; i++)  if(ilet == buf[i])  return(++i);
		pline("You don't know that spell.");
	    }
	}
}

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

	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;
	} else impossible("Too many spells in spellbook!");
	return;
}

dovspell() {

	register int max, i, side;
	char     buf[BUFSZ],
		 *spellname();

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

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

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

		impossible("Too many spells memorized.");
		return(0);
	    }
	}
	set_pager(0);
	side = (max + 1) / 2;
	if(page_line("Currently known spells:") || page_line(""))  goto quit;

	for(i = 1; i <= side; i++) {

		if((i < side) || !(max % 2))  {

		    (void) sprintf(buf, "%c - (%d) %22s          %c - (%d) %22s",
				   spellet(i), spellev(i), spellname(i),
				   spellet(i + side), spellev(i + side), spellname(i + side));
		} else {

		    (void) sprintf(buf, "%c - (%d) %22s", spellet(i), spellev(i), spellname(i));
		}
		if(page_line(buf)) goto quit;
	}

	set_pager(1);
	return(0);
quit:
	set_pager(2);
	return(0);
}

spellet(spl)  {

	if (spl < 27)	return('a' + spl - 1);
	else		return('A' + spl - 27);
}

spellev(spl)  {

	return(spl_book[spl-1].sp_lev);
}

char *
spellname(spl)  {

	return(objects[spl_book[spl-1].sp_id].oc_name);
}

spellid(spl)  {

	return(spl_book[spl-1].sp_id);
}

#endif /* SPELLS /**/
