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