1/*	$NetBSD: hack.read.c,v 1.10 2009/08/12 07:28:41 dholland Exp $	*/
2
3/*
4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5 * Amsterdam
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * - Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 *
15 * - Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * - Neither the name of the Stichting Centrum voor Wiskunde en
20 * Informatica, nor the names of its contributors may be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37/*
38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39 * All rights reserved.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. The name of the author may not be used to endorse or promote products
50 *    derived from this software without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 */
63
64#include <sys/cdefs.h>
65#ifndef lint
66__RCSID("$NetBSD: hack.read.c,v 1.10 2009/08/12 07:28:41 dholland Exp $");
67#endif				/* not lint */
68
69#include <stdlib.h>
70#include "hack.h"
71#include "extern.h"
72
73static int identify(struct obj *);
74static int monstersym(int);
75
76int
77doread(void)
78{
79	struct obj     *scroll;
80	boolean         confused = (Confusion != 0);
81	boolean         known = FALSE;
82
83	scroll = getobj("?", "read");
84	if (!scroll)
85		return (0);
86	if (!scroll->dknown && Blind) {
87		pline("Being blind, you cannot read the formula on the scroll.");
88		return (0);
89	}
90	if (Blind)
91		pline("As you pronounce the formula on it, the scroll disappears.");
92	else
93		pline("As you read the scroll, it disappears.");
94	if (confused)
95		pline("Being confused, you mispronounce the magic words ... ");
96
97	switch (scroll->otyp) {
98#ifdef MAIL
99	case SCR_MAIL:
100		readmail( /* scroll */ );
101		break;
102#endif	/* MAIL */
103	case SCR_ENCHANT_ARMOR:
104		{
105			struct obj     *otmp = some_armor();
106			if (!otmp) {
107				strange_feeling(scroll, "Your skin glows then fades.");
108				return (1);
109			}
110			if (confused) {
111				pline("Your %s glows silver for a moment.",
112				      objects[otmp->otyp].oc_name);
113				otmp->rustfree = 1;
114				break;
115			}
116			if (otmp->spe > 3 && rn2(otmp->spe)) {
117				pline("Your %s glows violently green for a while, then evaporates.",
118				      objects[otmp->otyp].oc_name);
119				useup(otmp);
120				break;
121			}
122			pline("Your %s glows green for a moment.",
123			      objects[otmp->otyp].oc_name);
124			otmp->cursed = 0;
125			otmp->spe++;
126			break;
127		}
128	case SCR_DESTROY_ARMOR:
129		if (confused) {
130			struct obj     *otmp = some_armor();
131			if (!otmp) {
132				strange_feeling(scroll, "Your bones itch.");
133				return (1);
134			}
135			pline("Your %s glows purple for a moment.",
136			      objects[otmp->otyp].oc_name);
137			otmp->rustfree = 0;
138			break;
139		}
140		if (uarm) {
141			pline("Your armor turns to dust and falls to the floor!");
142			useup(uarm);
143		} else if (uarmh) {
144			pline("Your helmet turns to dust and is blown away!");
145			useup(uarmh);
146		} else if (uarmg) {
147			pline("Your gloves vanish!");
148			useup(uarmg);
149			selftouch("You");
150		} else {
151			strange_feeling(scroll, "Your skin itches.");
152			return (1);
153		}
154		break;
155	case SCR_CONFUSE_MONSTER:
156		if (confused) {
157			pline("Your hands begin to glow purple.");
158			Confusion += rnd(100);
159		} else {
160			pline("Your hands begin to glow blue.");
161			u.umconf = 1;
162		}
163		break;
164	case SCR_SCARE_MONSTER:
165		{
166			int             ct = 0;
167			struct monst   *mtmp;
168
169			for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
170				if (cansee(mtmp->mx, mtmp->my)) {
171					if (confused)
172						mtmp->mflee = mtmp->mfroz =
173							mtmp->msleep = 0;
174					else
175						mtmp->mflee = 1;
176					ct++;
177				}
178			if (!ct) {
179				if (confused)
180					pline("You hear sad wailing in the distance.");
181				else
182					pline("You hear maniacal laughter in the distance.");
183			}
184			break;
185		}
186	case SCR_BLANK_PAPER:
187		if (confused)
188			pline("You see strange patterns on this scroll.");
189		else
190			pline("This scroll seems to be blank.");
191		break;
192	case SCR_REMOVE_CURSE:
193		{
194			struct obj     *obj;
195			if (confused)
196				pline("You feel like you need some help.");
197			else
198				pline("You feel like someone is helping you.");
199			for (obj = invent; obj; obj = obj->nobj)
200				if (obj->owornmask)
201					obj->cursed = confused;
202			if (Punished && !confused) {
203				Punished = 0;
204				freeobj(uchain);
205				unpobj(uchain);
206				free(uchain);
207				uball->spe = 0;
208				uball->owornmask &= ~W_BALL;
209				uchain = uball = (struct obj *) 0;
210			}
211			break;
212		}
213	case SCR_CREATE_MONSTER:
214		{
215			int             cnt = 1;
216
217			if (!rn2(73))
218				cnt += rnd(4);
219			if (confused)
220				cnt += 12;
221			while (cnt--)
222				(void) makemon(confused ? PM_ACID_BLOB :
223					 (struct permonst *) 0, u.ux, u.uy);
224			break;
225		}
226	case SCR_ENCHANT_WEAPON:
227		if (uwep && confused) {
228			pline("Your %s glows silver for a moment.",
229			      objects[uwep->otyp].oc_name);
230			uwep->rustfree = 1;
231		} else if (!chwepon(scroll, 1))	/* tests for !uwep */
232			return (1);
233		break;
234	case SCR_DAMAGE_WEAPON:
235		if (uwep && confused) {
236			pline("Your %s glows purple for a moment.",
237			      objects[uwep->otyp].oc_name);
238			uwep->rustfree = 0;
239		} else if (!chwepon(scroll, -1))	/* tests for !uwep */
240			return (1);
241		break;
242	case SCR_TAMING:
243		{
244			int             i, j;
245			int             bd = confused ? 5 : 1;
246			struct monst   *mtmp;
247
248			for (i = -bd; i <= bd; i++)
249				for (j = -bd; j <= bd; j++)
250					if ((mtmp = m_at(u.ux + i, u.uy + j)) != NULL)
251						(void) tamedog(mtmp, (struct obj *) 0);
252			break;
253		}
254	case SCR_GENOCIDE:
255		{
256			char            buf[BUFSZ];
257			struct monst   *mtmp, *mtmp2;
258
259			pline("You have found a scroll of genocide!");
260			known = TRUE;
261			if (confused)
262				*buf = u.usym;
263			else
264				do {
265					pline("What monster do you want to genocide (Type the letter)? ");
266					getlin(buf);
267				} while (strlen(buf) != 1 || !monstersym(*buf));
268			if (!strchr(fut_geno, *buf))
269				charcat(fut_geno, *buf);
270			if (!strchr(genocided, *buf))
271				charcat(genocided, *buf);
272			else {
273				pline("Such monsters do not exist in this world.");
274				break;
275			}
276			for (mtmp = fmon; mtmp; mtmp = mtmp2) {
277				mtmp2 = mtmp->nmon;
278				if (mtmp->data->mlet == *buf)
279					mondead(mtmp);
280			}
281			pline("Wiped out all %c's.", *buf);
282			if (*buf == u.usym) {
283				killer = "scroll of genocide";
284				u.uhp = -1;
285			}
286			break;
287		}
288	case SCR_LIGHT:
289		if (!Blind)
290			known = TRUE;
291		litroom(!confused);
292		break;
293	case SCR_TELEPORTATION:
294		if (confused)
295			level_tele();
296		else {
297#ifdef QUEST
298			int             oux = u.ux, ouy = u.uy;
299			tele();
300			if (dist(oux, ouy) > 100)
301				known = TRUE;
302#else	/* QUEST */
303			int             uroom = inroom(u.ux, u.uy);
304			tele();
305			if (uroom != inroom(u.ux, u.uy))
306				known = TRUE;
307#endif	/* QUEST */
308		}
309		break;
310	case SCR_GOLD_DETECTION:
311		/*
312		 * Unfortunately this code has become slightly less elegant,
313		 * now that gold and traps no longer are of the same type.
314		 */
315		if (confused) {
316			struct trap    *ttmp;
317
318			if (!ftrap) {
319				strange_feeling(scroll, "Your toes stop itching.");
320				return (1);
321			} else {
322				for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
323					if (ttmp->tx != u.ux || ttmp->ty != u.uy)
324						goto outtrapmap;
325				/*
326				 * only under me - no separate display
327				 * required
328				 */
329				pline("Your toes itch!");
330				break;
331		outtrapmap:
332				cls();
333				for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
334					at(ttmp->tx, ttmp->ty, '$');
335				prme();
336				pline("You feel very greedy!");
337			}
338		} else {
339			struct gold    *gtmp;
340
341			if (!fgold) {
342				strange_feeling(scroll, "You feel materially poor.");
343				return (1);
344			} else {
345				known = TRUE;
346				for (gtmp = fgold; gtmp; gtmp = gtmp->ngold)
347					if (gtmp->gx != u.ux || gtmp->gy != u.uy)
348						goto outgoldmap;
349				/*
350				 * only under me - no separate display
351				 * required
352				 */
353				pline("You notice some gold between your feet.");
354				break;
355		outgoldmap:
356				cls();
357				for (gtmp = fgold; gtmp; gtmp = gtmp->ngold)
358					at(gtmp->gx, gtmp->gy, '$');
359				prme();
360				pline("You feel very greedy, and sense gold!");
361			}
362		}
363		/* common sequel */
364		more();
365		docrt();
366		break;
367	case SCR_FOOD_DETECTION:
368		{
369			int ct = 0, ctu = 0;
370			struct obj     *obj;
371			char            foodsym = confused ? POTION_SYM : FOOD_SYM;
372
373			for (obj = fobj; obj; obj = obj->nobj)
374				if (obj->olet == FOOD_SYM) {
375					if (obj->ox == u.ux && obj->oy == u.uy)
376						ctu++;
377					else
378						ct++;
379				}
380			if (!ct && !ctu) {
381				strange_feeling(scroll, "Your nose twitches.");
382				return (1);
383			} else if (!ct) {
384				known = TRUE;
385				pline("You smell %s close nearby.",
386				      confused ? "something" : "food");
387
388			} else {
389				known = TRUE;
390				cls();
391				for (obj = fobj; obj; obj = obj->nobj)
392					if (obj->olet == foodsym)
393						at(obj->ox, obj->oy, FOOD_SYM);
394				prme();
395				pline("Your nose tingles and you smell %s!",
396				      confused ? "something" : "food");
397				more();
398				docrt();
399			}
400			break;
401		}
402	case SCR_IDENTIFY:
403		/* known = TRUE; */
404		if (confused)
405			pline("You identify this as an identify scroll.");
406		else
407			pline("This is an identify scroll.");
408		useup(scroll);
409		objects[SCR_IDENTIFY].oc_name_known = 1;
410		if (!confused)
411			while (
412			 !ggetobj("identify", identify, rn2(5) ? 1 : rn2(5))
413			       && invent
414				);
415		return (1);
416	case SCR_MAGIC_MAPPING:
417		{
418			struct rm      *lev;
419			int             num, zx, zy;
420
421			known = TRUE;
422			pline("On this scroll %s a map!",
423			      confused ? "was" : "is");
424			for (zy = 0; zy < ROWNO; zy++)
425				for (zx = 0; zx < COLNO; zx++) {
426					if (confused && rn2(7))
427						continue;
428					lev = &(levl[zx][zy]);
429					if ((num = lev->typ) == 0)
430						continue;
431					if (num == SCORR) {
432						lev->typ = CORR;
433						lev->scrsym = CORR_SYM;
434					} else if (num == SDOOR) {
435						lev->typ = DOOR;
436						lev->scrsym = '+';
437						/* do sth in doors ? */
438					} else if (lev->seen)
439						continue;
440#ifndef QUEST
441					if (num != ROOM)
442#endif	/* QUEST */
443					{
444						lev->seen = lev->new = 1;
445						if (lev->scrsym == ' ' || !lev->scrsym)
446							newsym(zx, zy);
447						else
448							on_scr(zx, zy);
449					}
450				}
451			break;
452		}
453	case SCR_AMNESIA:
454		{
455			int             zx, zy;
456
457			known = TRUE;
458			for (zx = 0; zx < COLNO; zx++)
459				for (zy = 0; zy < ROWNO; zy++)
460					if (!confused || rn2(7))
461						if (!cansee(zx, zy))
462							levl[zx][zy].seen = 0;
463			docrt();
464			pline("Thinking of Maud you forget everything else.");
465			break;
466		}
467	case SCR_FIRE:
468		{
469			int             num = 0;
470			struct monst   *mtmp;
471
472			known = TRUE;
473			if (confused) {
474				pline("The scroll catches fire and you burn your hands.");
475				losehp(1, "scroll of fire");
476			} else {
477				pline("The scroll erupts in a tower of flame!");
478				if (Fire_resistance)
479					pline("You are uninjured.");
480				else {
481					num = rnd(6);
482					u.uhpmax -= num;
483					losehp(num, "scroll of fire");
484				}
485			}
486			num = (2 * num + 1) / 3;
487			for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
488				if (dist(mtmp->mx, mtmp->my) < 3) {
489					mtmp->mhp -= num;
490					if (strchr("FY", mtmp->data->mlet))
491						mtmp->mhp -= 3 * num;	/* this might well kill
492									 * 'F's */
493					if (mtmp->mhp < 1) {
494						killed(mtmp);
495						break;	/* primitive */
496					}
497				}
498			}
499			break;
500		}
501	case SCR_PUNISHMENT:
502		known = TRUE;
503		if (confused) {
504			pline("You feel guilty.");
505			break;
506		}
507		pline("You are being punished for your misbehaviour!");
508		if (Punished) {
509			pline("Your iron ball gets heavier.");
510			uball->owt += 15;
511			break;
512		}
513		Punished = INTRINSIC;
514		setworn(mkobj_at(CHAIN_SYM, u.ux, u.uy), W_CHAIN);
515		setworn(mkobj_at(BALL_SYM, u.ux, u.uy), W_BALL);
516		uball->spe = 1;	/* special ball (see save) */
517		break;
518	default:
519		impossible("What weird language is this written in? (%u)",
520			   scroll->otyp);
521	}
522	if (!objects[scroll->otyp].oc_name_known) {
523		if (known && !confused) {
524			objects[scroll->otyp].oc_name_known = 1;
525			more_experienced(0, 10);
526		} else if (!objects[scroll->otyp].oc_uname)
527			docall(scroll);
528	}
529	useup(scroll);
530	return (1);
531}
532
533static int
534identify(struct obj *otmp)		/* also called by newmail() */
535{
536	objects[otmp->otyp].oc_name_known = 1;
537	otmp->known = otmp->dknown = 1;
538	prinv(otmp);
539	return (1);
540}
541
542void
543litroom(boolean on)
544{
545#ifndef QUEST
546	int num, zx, zy;
547#endif
548
549	/* first produce the text (provided he is not blind) */
550	if (Blind)
551		goto do_it;
552	if (!on) {
553		if (u.uswallow || !xdnstair || levl[u.ux][u.uy].typ == CORR ||
554		    !levl[u.ux][u.uy].lit) {
555			pline("It seems even darker in here than before.");
556			return;
557		} else
558			pline("It suddenly becomes dark in here.");
559	} else {
560		if (u.uswallow) {
561			pline("%s's stomach is lit.", Monnam(u.ustuck));
562			return;
563		}
564		if (!xdnstair) {
565			pline("Nothing Happens.");
566			return;
567		}
568#ifdef QUEST
569		pline("The cave lights up around you, then fades.");
570		return;
571#else	/* QUEST */
572		if (levl[u.ux][u.uy].typ == CORR) {
573			pline("The corridor lights up around you, then fades.");
574			return;
575		} else if (levl[u.ux][u.uy].lit) {
576			pline("The light here seems better now.");
577			return;
578		} else
579			pline("The room is lit.");
580#endif	/* QUEST */
581	}
582
583do_it:
584#ifdef QUEST
585	return;
586#else	/* QUEST */
587	if (levl[u.ux][u.uy].lit == on)
588		return;
589	if (levl[u.ux][u.uy].typ == DOOR) {
590		if (IS_ROOM(levl[u.ux][u.uy + 1].typ))
591			zy = u.uy + 1;
592		else if (IS_ROOM(levl[u.ux][u.uy - 1].typ))
593			zy = u.uy - 1;
594		else
595			zy = u.uy;
596		if (IS_ROOM(levl[u.ux + 1][u.uy].typ))
597			zx = u.ux + 1;
598		else if (IS_ROOM(levl[u.ux - 1][u.uy].typ))
599			zx = u.ux - 1;
600		else
601			zx = u.ux;
602	} else {
603		zx = u.ux;
604		zy = u.uy;
605	}
606	for (seelx = u.ux; (num = levl[seelx - 1][zy].typ) != CORR && num != 0;
607	     seelx--);
608	for (seehx = u.ux; (num = levl[seehx + 1][zy].typ) != CORR && num != 0;
609	     seehx++);
610	for (seely = u.uy; (num = levl[zx][seely - 1].typ) != CORR && num != 0;
611	     seely--);
612	for (seehy = u.uy; (num = levl[zx][seehy + 1].typ) != CORR && num != 0;
613	     seehy++);
614	for (zy = seely; zy <= seehy; zy++)
615		for (zx = seelx; zx <= seehx; zx++) {
616			levl[zx][zy].lit = on;
617			if (!Blind && dist(zx, zy) > 2) {
618				if (on)
619					prl(zx, zy);
620				else
621					nosee(zx, zy);
622			}
623		}
624	if (!on)
625		seehx = 0;
626#endif	/* QUEST */
627}
628
629/* Test whether we may genocide all monsters with symbol  ch  */
630static int
631monstersym(int ch)		/* arnold@ucsfcgl */
632{
633	const struct permonst *mp;
634
635	/*
636	 * can't genocide certain monsters
637	 */
638	if (strchr("12 &:", ch))
639		return FALSE;
640
641	if (ch == pm_eel.mlet)
642		return TRUE;
643	for (mp = mons; mp < &mons[CMNUM + 2]; mp++)
644		if (mp->mlet == ch)
645			return TRUE;
646	return FALSE;
647}
648