/*	$NetBSD: hack.read.c,v 1.11 2011/08/06 20:29:37 dholland Exp $	*/

/*
 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
 * Amsterdam
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Stichting Centrum voor Wiskunde en
 * Informatica, nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior
 * written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: hack.read.c,v 1.11 2011/08/06 20:29:37 dholland Exp $");
#endif				/* not lint */

#include <stdlib.h>
#include "hack.h"
#include "extern.h"

static int identify(struct obj *);
static int monstersym(int);

int
doread(void)
{
	struct obj     *scroll;
	boolean         confused = (Confusion != 0);
	boolean         known = FALSE;

	scroll = getobj("?", "read");
	if (!scroll)
		return (0);
	if (!scroll->dknown && Blind) {
		pline("Being blind, you cannot read the formula on the scroll.");
		return (0);
	}
	if (Blind)
		pline("As you pronounce the formula on it, the scroll disappears.");
	else
		pline("As you read the scroll, it disappears.");
	if (confused)
		pline("Being confused, you mispronounce the magic words ... ");

	switch (scroll->otyp) {
#ifdef MAIL
	case SCR_MAIL:
		readmail( /* scroll */ );
		break;
#endif	/* MAIL */
	case SCR_ENCHANT_ARMOR:
		{
			struct obj     *otmp = some_armor();
			if (!otmp) {
				strange_feeling(scroll, "Your skin glows then fades.");
				return (1);
			}
			if (confused) {
				pline("Your %s glows silver for a moment.",
				      objects[otmp->otyp].oc_name);
				otmp->rustfree = 1;
				break;
			}
			if (otmp->spe > 3 && rn2(otmp->spe)) {
				pline("Your %s glows violently green for a while, then evaporates.",
				      objects[otmp->otyp].oc_name);
				useup(otmp);
				break;
			}
			pline("Your %s glows green for a moment.",
			      objects[otmp->otyp].oc_name);
			otmp->cursed = 0;
			otmp->spe++;
			break;
		}
	case SCR_DESTROY_ARMOR:
		if (confused) {
			struct obj     *otmp = some_armor();
			if (!otmp) {
				strange_feeling(scroll, "Your bones itch.");
				return (1);
			}
			pline("Your %s glows purple for a moment.",
			      objects[otmp->otyp].oc_name);
			otmp->rustfree = 0;
			break;
		}
		if (uarm) {
			pline("Your armor turns to dust and falls to the floor!");
			useup(uarm);
		} else if (uarmh) {
			pline("Your helmet turns to dust and is blown away!");
			useup(uarmh);
		} else if (uarmg) {
			pline("Your gloves vanish!");
			useup(uarmg);
			selftouch("You");
		} else {
			strange_feeling(scroll, "Your skin itches.");
			return (1);
		}
		break;
	case SCR_CONFUSE_MONSTER:
		if (confused) {
			pline("Your hands begin to glow purple.");
			Confusion += rnd(100);
		} else {
			pline("Your hands begin to glow blue.");
			u.umconf = 1;
		}
		break;
	case SCR_SCARE_MONSTER:
		{
			int             ct = 0;
			struct monst   *mtmp;

			for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
				if (cansee(mtmp->mx, mtmp->my)) {
					if (confused)
						mtmp->mflee = mtmp->mfroz =
							mtmp->msleep = 0;
					else
						mtmp->mflee = 1;
					ct++;
				}
			if (!ct) {
				if (confused)
					pline("You hear sad wailing in the distance.");
				else
					pline("You hear maniacal laughter in the distance.");
			}
			break;
		}
	case SCR_BLANK_PAPER:
		if (confused)
			pline("You see strange patterns on this scroll.");
		else
			pline("This scroll seems to be blank.");
		break;
	case SCR_REMOVE_CURSE:
		{
			struct obj     *obj;
			if (confused)
				pline("You feel like you need some help.");
			else
				pline("You feel like someone is helping you.");
			for (obj = invent; obj; obj = obj->nobj)
				if (obj->owornmask)
					obj->cursed = confused;
			if (Punished && !confused) {
				Punished = 0;
				freeobj(uchain);
				unpobj(uchain);
				free(uchain);
				uball->spe = 0;
				uball->owornmask &= ~W_BALL;
				uchain = uball = (struct obj *) 0;
			}
			break;
		}
	case SCR_CREATE_MONSTER:
		{
			int             cnt = 1;

			if (!rn2(73))
				cnt += rnd(4);
			if (confused)
				cnt += 12;
			while (cnt--)
				(void) makemon(confused ? PM_ACID_BLOB :
					 (struct permonst *) 0, u.ux, u.uy);
			break;
		}
	case SCR_ENCHANT_WEAPON:
		if (uwep && confused) {
			pline("Your %s glows silver for a moment.",
			      objects[uwep->otyp].oc_name);
			uwep->rustfree = 1;
		} else if (!chwepon(scroll, 1))	/* tests for !uwep */
			return (1);
		break;
	case SCR_DAMAGE_WEAPON:
		if (uwep && confused) {
			pline("Your %s glows purple for a moment.",
			      objects[uwep->otyp].oc_name);
			uwep->rustfree = 0;
		} else if (!chwepon(scroll, -1))	/* tests for !uwep */
			return (1);
		break;
	case SCR_TAMING:
		{
			int             i, j;
			int             bd = confused ? 5 : 1;
			struct monst   *mtmp;

			for (i = -bd; i <= bd; i++)
				for (j = -bd; j <= bd; j++)
					if ((mtmp = m_at(u.ux + i, u.uy + j)) != NULL)
						(void) tamedog(mtmp, (struct obj *) 0);
			break;
		}
	case SCR_GENOCIDE:
		{
			char            buf[BUFSZ];
			struct monst   *mtmp, *mtmp2;

			pline("You have found a scroll of genocide!");
			known = TRUE;
			if (confused)
				*buf = u.usym;
			else
				do {
					pline("What monster do you want to genocide (Type the letter)? ");
					getlin(buf);
				} while (strlen(buf) != 1 || !monstersym(*buf));
			if (!strchr(fut_geno, *buf))
				charcat(fut_geno, *buf);
			if (!strchr(genocided, *buf))
				charcat(genocided, *buf);
			else {
				pline("Such monsters do not exist in this world.");
				break;
			}
			for (mtmp = fmon; mtmp; mtmp = mtmp2) {
				mtmp2 = mtmp->nmon;
				if (mtmp->data->mlet == *buf)
					mondead(mtmp);
			}
			pline("Wiped out all %c's.", *buf);
			if (*buf == u.usym) {
				killer = "scroll of genocide";
				u.uhp = -1;
			}
			break;
		}
	case SCR_LIGHT:
		if (!Blind)
			known = TRUE;
		litroom(!confused);
		break;
	case SCR_TELEPORTATION:
		if (confused)
			level_tele();
		else {
#ifdef QUEST
			int             oux = u.ux, ouy = u.uy;
			tele();
			if (dist(oux, ouy) > 100)
				known = TRUE;
#else	/* QUEST */
			int             uroom = inroom(u.ux, u.uy);
			tele();
			if (uroom != inroom(u.ux, u.uy))
				known = TRUE;
#endif	/* QUEST */
		}
		break;
	case SCR_GOLD_DETECTION:
		/*
		 * Unfortunately this code has become slightly less elegant,
		 * now that gold and traps no longer are of the same type.
		 */
		if (confused) {
			struct trap    *ttmp;

			if (!ftrap) {
				strange_feeling(scroll, "Your toes stop itching.");
				return (1);
			} else {
				for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
					if (ttmp->tx != u.ux || ttmp->ty != u.uy)
						goto outtrapmap;
				/*
				 * only under me - no separate display
				 * required
				 */
				pline("Your toes itch!");
				break;
		outtrapmap:
				cls();
				for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
					at(ttmp->tx, ttmp->ty, '$');
				prme();
				pline("You feel very greedy!");
			}
		} else {
			struct gold    *gtmp;

			if (!fgold) {
				strange_feeling(scroll, "You feel materially poor.");
				return (1);
			} else {
				known = TRUE;
				for (gtmp = fgold; gtmp; gtmp = gtmp->ngold)
					if (gtmp->gx != u.ux || gtmp->gy != u.uy)
						goto outgoldmap;
				/*
				 * only under me - no separate display
				 * required
				 */
				pline("You notice some gold between your feet.");
				break;
		outgoldmap:
				cls();
				for (gtmp = fgold; gtmp; gtmp = gtmp->ngold)
					at(gtmp->gx, gtmp->gy, '$');
				prme();
				pline("You feel very greedy, and sense gold!");
			}
		}
		/* common sequel */
		more();
		docrt();
		break;
	case SCR_FOOD_DETECTION:
		{
			int ct = 0, ctu = 0;
			struct obj     *obj;
			char            foodsym = confused ? POTION_SYM : FOOD_SYM;

			for (obj = fobj; obj; obj = obj->nobj)
				if (obj->olet == FOOD_SYM) {
					if (obj->ox == u.ux && obj->oy == u.uy)
						ctu++;
					else
						ct++;
				}
			if (!ct && !ctu) {
				strange_feeling(scroll, "Your nose twitches.");
				return (1);
			} else if (!ct) {
				known = TRUE;
				pline("You smell %s close nearby.",
				      confused ? "something" : "food");

			} else {
				known = TRUE;
				cls();
				for (obj = fobj; obj; obj = obj->nobj)
					if (obj->olet == foodsym)
						at(obj->ox, obj->oy, FOOD_SYM);
				prme();
				pline("Your nose tingles and you smell %s!",
				      confused ? "something" : "food");
				more();
				docrt();
			}
			break;
		}
	case SCR_IDENTIFY:
		/* known = TRUE; */
		if (confused)
			pline("You identify this as an identify scroll.");
		else
			pline("This is an identify scroll.");
		useup(scroll);
		objects[SCR_IDENTIFY].oc_name_known = 1;
		if (!confused)
			while (
			 !ggetobj("identify", identify, rn2(5) ? 1 : rn2(5))
			       && invent
				);
		return (1);
	case SCR_MAGIC_MAPPING:
		{
			struct rm      *lev;
			int             num, zx, zy;

			known = TRUE;
			pline("On this scroll %s a map!",
			      confused ? "was" : "is");
			for (zy = 0; zy < ROWNO; zy++)
				for (zx = 0; zx < COLNO; zx++) {
					if (confused && rn2(7))
						continue;
					lev = &(levl[zx][zy]);
					if ((num = lev->typ) == 0)
						continue;
					if (num == SCORR) {
						lev->typ = CORR;
						lev->scrsym = CORR_SYM;
					} else if (num == SDOOR) {
						lev->typ = DOOR;
						lev->scrsym = '+';
						/* do sth in doors ? */
					} else if (lev->seen)
						continue;
#ifndef QUEST
					if (num != ROOM)
#endif	/* QUEST */
					{
						lev->seen = lev->new = 1;
						if (lev->scrsym == ' ' || !lev->scrsym)
							newsym(zx, zy);
						else
							on_scr(zx, zy);
					}
				}
			break;
		}
	case SCR_AMNESIA:
		{
			int             zx, zy;

			known = TRUE;
			for (zx = 0; zx < COLNO; zx++)
				for (zy = 0; zy < ROWNO; zy++)
					if (!confused || rn2(7))
						if (!cansee(zx, zy))
							levl[zx][zy].seen = 0;
			docrt();
			pline("Thinking of Maud you forget everything else.");
			break;
		}
	case SCR_FIRE:
		{
			int             num = 0;
			struct monst   *mtmp;

			known = TRUE;
			if (confused) {
				pline("The scroll catches fire and you burn your hands.");
				losehp(1, "scroll of fire");
			} else {
				pline("The scroll erupts in a tower of flame!");
				if (Fire_resistance)
					pline("You are uninjured.");
				else {
					num = rnd(6);
					u.uhpmax -= num;
					losehp(num, "scroll of fire");
				}
			}
			num = (2 * num + 1) / 3;
			for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
				if (dist(mtmp->mx, mtmp->my) < 3) {
					mtmp->mhp -= num;
					if (strchr("FY", mtmp->data->mlet))
						mtmp->mhp -= 3 * num;	/* this might well kill
									 * 'F's */
					if (mtmp->mhp < 1) {
						killed(mtmp);
						break;	/* primitive */
					}
				}
			}
			break;
		}
	case SCR_PUNISHMENT:
		known = TRUE;
		if (confused) {
			pline("You feel guilty.");
			break;
		}
		pline("You are being punished for your misbehaviour!");
		if (Punished) {
			pline("Your iron ball gets heavier.");
			uball->owt += 15;
			break;
		}
		Punished = INTRINSIC;
		setworn(mkobj_at(CHAIN_SYM, u.ux, u.uy), W_CHAIN);
		setworn(mkobj_at(BALL_SYM, u.ux, u.uy), W_BALL);
		uball->spe = 1;	/* special ball (see save) */
		break;
	default:
		impossible("What weird language is this written in? (%u)",
			   scroll->otyp);
	}
	if (!objects[scroll->otyp].oc_name_known) {
		if (known && !confused) {
			objects[scroll->otyp].oc_name_known = 1;
			more_experienced(0, 10);
		} else if (!objects[scroll->otyp].oc_uname)
			docall(scroll);
	}
	useup(scroll);
	return (1);
}

static int
identify(struct obj *otmp)		/* also called by newmail() */
{
	objects[otmp->otyp].oc_name_known = 1;
	otmp->known = otmp->dknown = 1;
	prinv(otmp);
	return (1);
}

void
litroom(boolean on)
{
#ifndef QUEST
	int num, zx, zy;
#endif

	/* first produce the text (provided he is not blind) */
	if (Blind)
		goto do_it;
	if (!on) {
		if (u.uswallow || !xdnstair || levl[u.ux][u.uy].typ == CORR ||
		    !levl[u.ux][u.uy].lit) {
			pline("It seems even darker in here than before.");
			return;
		} else
			pline("It suddenly becomes dark in here.");
	} else {
		if (u.uswallow) {
			pline("%s's stomach is lit.", Monnam(u.ustuck));
			return;
		}
		if (!xdnstair) {
			pline("Nothing Happens.");
			return;
		}
#ifdef QUEST
		pline("The cave lights up around you, then fades.");
		return;
#else	/* QUEST */
		if (levl[u.ux][u.uy].typ == CORR) {
			pline("The corridor lights up around you, then fades.");
			return;
		} else if (levl[u.ux][u.uy].lit) {
			pline("The light here seems better now.");
			return;
		} else
			pline("The room is lit.");
#endif	/* QUEST */
	}

do_it:
#ifdef QUEST
	return;
#else	/* QUEST */
	if (levl[u.ux][u.uy].lit == on)
		return;
	if (levl[u.ux][u.uy].typ == DOOR) {
		if (IS_ROOM(levl[u.ux][u.uy + 1].typ))
			zy = u.uy + 1;
		else if (IS_ROOM(levl[u.ux][u.uy - 1].typ))
			zy = u.uy - 1;
		else
			zy = u.uy;
		if (IS_ROOM(levl[u.ux + 1][u.uy].typ))
			zx = u.ux + 1;
		else if (IS_ROOM(levl[u.ux - 1][u.uy].typ))
			zx = u.ux - 1;
		else
			zx = u.ux;
	} else {
		zx = u.ux;
		zy = u.uy;
	}
	for (seelx = u.ux; (num = levl[seelx - 1][zy].typ) != CORR && num != 0;
	     seelx--);
	for (seehx = u.ux; (num = levl[seehx + 1][zy].typ) != CORR && num != 0;
	     seehx++);
	for (seely = u.uy; (num = levl[zx][seely - 1].typ) != CORR && num != 0;
	     seely--);
	for (seehy = u.uy; (num = levl[zx][seehy + 1].typ) != CORR && num != 0;
	     seehy++);
	for (zy = seely; zy <= seehy; zy++)
		for (zx = seelx; zx <= seehx; zx++) {
			levl[zx][zy].lit = on;
			if (!Blind && dist(zx, zy) > 2) {
				if (on)
					prl(zx, zy);
				else
					nosee(zx, zy);
			}
		}
	if (!on)
		seehx = 0;
#endif	/* QUEST */
}

/* Test whether we may genocide all monsters with symbol  ch  */
static int
monstersym(int ch)		/* arnold@ucsfcgl */
{
	const struct permonst *mp;

	/*
	 * can't genocide certain monsters
	 */
	if (strchr("12 &:", ch))
		return FALSE;

	if (ch == pm_eel.mlet)
		return TRUE;
	for (mp = mons; mp < &mons[CMNUM + 2]; mp++)
		if (mp->mlet == ch)
			return TRUE;
	return FALSE;
}
