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