1 /* SCCS Id: @(#)detect.c 3.4 2003/08/13 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 /*
6 * Detection routines, including crystal ball, magic mapping, and search
7 * command.
8 */
9
10 #include "hack.h"
11 #include "artifact.h"
12
13 extern boolean known; /* from read.c */
14
15 STATIC_DCL void FDECL(do_dknown_of, (struct obj *));
16 STATIC_DCL boolean FDECL(check_map_spot, (int,int,CHAR_P,unsigned));
17 STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P,unsigned));
18 STATIC_DCL void FDECL(sense_trap, (struct trap *,XCHAR_P,XCHAR_P,int));
19 STATIC_DCL void FDECL(show_map_spot, (int,int));
20 STATIC_PTR void FDECL(findone,(int,int,genericptr_t));
21 STATIC_PTR void FDECL(openone,(int,int,genericptr_t));
22
23 /* Recursively search obj for an object in class oclass and return 1st found */
24 struct obj *
o_in(obj,oclass)25 o_in(obj, oclass)
26 struct obj* obj;
27 char oclass;
28 {
29 register struct obj* otmp;
30 struct obj *temp;
31
32 if (obj->oclass == oclass) return obj;
33
34 if (Has_contents(obj)) {
35 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
36 if (otmp->oclass == oclass) return otmp;
37 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)))
38 return temp;
39 }
40 return (struct obj *) 0;
41 }
42
43 /* Recursively search obj for an object made of specified material and return 1st found */
44 struct obj *
o_material(obj,material)45 o_material(obj, material)
46 struct obj* obj;
47 unsigned material;
48 {
49 register struct obj* otmp;
50 struct obj *temp;
51
52 if (objects[obj->otyp].oc_material == material) return obj;
53
54 if (Has_contents(obj)) {
55 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
56 if (objects[otmp->otyp].oc_material == material) return otmp;
57 else if (Has_contents(otmp) && (temp = o_material(otmp, material)))
58 return temp;
59 }
60 return (struct obj *) 0;
61 }
62
63 STATIC_OVL void
do_dknown_of(obj)64 do_dknown_of(obj)
65 struct obj *obj;
66 {
67 struct obj *otmp;
68
69 obj->dknown = 1;
70 if (Has_contents(obj)) {
71 for(otmp = obj->cobj; otmp; otmp = otmp->nobj)
72 do_dknown_of(otmp);
73 }
74 }
75
76 /* Check whether the location has an outdated object displayed on it. */
77 STATIC_OVL boolean
check_map_spot(x,y,oclass,material)78 check_map_spot(x, y, oclass, material)
79 int x, y;
80 register char oclass;
81 unsigned material;
82 {
83 register int glyph;
84 register struct obj *otmp;
85 register struct monst *mtmp;
86
87 glyph = glyph_at(x,y);
88 if (glyph_is_object(glyph)) {
89 /* there's some object shown here */
90 if (oclass == ALL_CLASSES) {
91 return((boolean)( !(level.objects[x][y] || /* stale if nothing here */
92 ((mtmp = m_at(x,y)) != 0 &&
93 (
94 #ifndef GOLDOBJ
95 mtmp->mgold ||
96 #endif
97 mtmp->minvent)))));
98 } else {
99 if (material && objects[glyph_to_obj(glyph)].oc_material == material) {
100 /* the object shown here is of interest because material matches */
101 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
102 if (o_material(otmp, GOLD)) return FALSE;
103 /* didn't find it; perhaps a monster is carrying it */
104 if ((mtmp = m_at(x,y)) != 0) {
105 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
106 if (o_material(otmp, GOLD)) return FALSE;
107 }
108 /* detection indicates removal of this object from the map */
109 return TRUE;
110 }
111 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
112 /* the object shown here is of interest because its class matches */
113 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
114 if (o_in(otmp, oclass)) return FALSE;
115 /* didn't find it; perhaps a monster is carrying it */
116 #ifndef GOLDOBJ
117 if ((mtmp = m_at(x,y)) != 0) {
118 if (oclass == COIN_CLASS && mtmp->mgold)
119 return FALSE;
120 else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
121 if (o_in(otmp, oclass)) return FALSE;
122 }
123 #else
124 if ((mtmp = m_at(x,y)) != 0) {
125 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
126 if (o_in(otmp, oclass)) return FALSE;
127 }
128 #endif
129 /* detection indicates removal of this object from the map */
130 return TRUE;
131 }
132 }
133 }
134 return FALSE;
135 }
136
137 /*
138 When doing detection, remove stale data from the map display (corpses
139 rotted away, objects carried away by monsters, etc) so that it won't
140 reappear after the detection has completed. Return true if noticeable
141 change occurs.
142 */
143 STATIC_OVL boolean
clear_stale_map(oclass,material)144 clear_stale_map(oclass, material)
145 register char oclass;
146 unsigned material;
147 {
148 register int zx, zy;
149 register boolean change_made = FALSE;
150
151 for (zx = 1; zx < COLNO; zx++)
152 for (zy = 0; zy < ROWNO; zy++)
153 if (check_map_spot(zx, zy, oclass,material)) {
154 unmap_object(zx, zy);
155 change_made = TRUE;
156 }
157
158 return change_made;
159 }
160
161 /* look for gold, on the floor or in monsters' possession */
162 int
gold_detect(sobj)163 gold_detect(sobj)
164 register struct obj *sobj;
165 {
166 register struct obj *obj;
167 register struct monst *mtmp;
168 int uw = u.uinwater;
169 struct obj *temp;
170 boolean stale;
171
172 known = stale = clear_stale_map(COIN_CLASS,
173 (unsigned)(sobj->blessed ? GOLD : 0));
174
175 /* look for gold carried by monsters (might be in a container) */
176 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
177 if (DEADMONSTER(mtmp)) continue; /* probably not needed in this case but... */
178 #ifndef GOLDOBJ
179 if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
180 #else
181 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
182 #endif
183 known = TRUE;
184 goto outgoldmap; /* skip further searching */
185 } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
186 if (sobj->blessed && o_material(obj, GOLD)) {
187 known = TRUE;
188 goto outgoldmap;
189 } else if (o_in(obj, COIN_CLASS)) {
190 known = TRUE;
191 goto outgoldmap; /* skip further searching */
192 }
193 }
194
195 /* look for gold objects */
196 for (obj = fobj; obj; obj = obj->nobj) {
197 if (sobj->blessed && o_material(obj, GOLD)) {
198 known = TRUE;
199 if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
200 } else if (o_in(obj, COIN_CLASS)) {
201 known = TRUE;
202 if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
203 }
204 }
205
206 if (!known) {
207 /* no gold found on floor or monster's inventory.
208 adjust message if you have gold in your inventory */
209 if (sobj) {
210 char buf[BUFSZ];
211 if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
212 Sprintf(buf, "You feel like a million %s!",
213 currency(2L));
214 } else if (hidden_gold() ||
215 #ifndef GOLDOBJ
216 u.ugold)
217 #else
218 money_cnt(invent))
219 #endif
220 Strcpy(buf,
221 "You feel worried about your future financial situation.");
222 else
223 Strcpy(buf, "You feel materially poor.");
224 strange_feeling(sobj, buf);
225 }
226 return(1);
227 }
228 /* only under me - no separate display required */
229 if (stale) docrt();
230 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
231 return(0);
232
233 outgoldmap:
234 cls();
235
236 u.uinwater = 0;
237 /* Discover gold locations. */
238 for (obj = fobj; obj; obj = obj->nobj) {
239 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
240 if (temp != obj) {
241 temp->ox = obj->ox;
242 temp->oy = obj->oy;
243 }
244 map_object(temp,1);
245 } else if ((temp = o_in(obj, COIN_CLASS))) {
246 if (temp != obj) {
247 temp->ox = obj->ox;
248 temp->oy = obj->oy;
249 }
250 map_object(temp,1);
251 }
252 }
253 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
254 if (DEADMONSTER(mtmp)) continue; /* probably overkill here */
255 #ifndef GOLDOBJ
256 if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
257 #else
258 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
259 #endif
260 struct obj gold;
261
262 gold.otyp = GOLD_PIECE;
263 gold.ox = mtmp->mx;
264 gold.oy = mtmp->my;
265 map_object(&gold,1);
266 } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
267 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
268 temp->ox = mtmp->mx;
269 temp->oy = mtmp->my;
270 map_object(temp,1);
271 break;
272 } else if ((temp = o_in(obj, COIN_CLASS))) {
273 temp->ox = mtmp->mx;
274 temp->oy = mtmp->my;
275 map_object(temp,1);
276 break;
277 }
278 }
279
280 newsym(u.ux,u.uy);
281 You_feel("very greedy, and sense gold!");
282 exercise(A_WIS, TRUE);
283 display_nhwindow(WIN_MAP, TRUE);
284 docrt();
285 u.uinwater = uw;
286 if (Underwater) under_water(2);
287 if (u.uburied) under_ground(2);
288 return(0);
289 }
290
291 /* returns 1 if nothing was detected */
292 /* returns 0 if something was detected */
293 int
food_detect(sobj)294 food_detect(sobj)
295 register struct obj *sobj;
296 {
297 register struct obj *obj;
298 register struct monst *mtmp;
299 register int ct = 0, ctu = 0;
300 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
301 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
302 const char *what = confused ? something : "food";
303 int uw = u.uinwater;
304
305 stale = clear_stale_map(oclass, 0);
306
307 for (obj = fobj; obj; obj = obj->nobj)
308 if (o_in(obj, oclass)) {
309 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
310 else ct++;
311 }
312 for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
313 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */
314 for (obj = mtmp->minvent; obj; obj = obj->nobj)
315 if (o_in(obj, oclass)) {
316 ct++;
317 break;
318 }
319 }
320
321 if (!ct && !ctu) {
322 known = stale && !confused;
323 if (stale) {
324 docrt();
325 You("sense a lack of %s nearby.", what);
326 if (sobj && sobj->blessed) {
327 if (!u.uedibility) Your("%s starts to tingle.", body_part(NOSE));
328 u.uedibility = 1;
329 }
330 } else if (sobj) {
331 char buf[BUFSZ];
332 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
333 (sobj->blessed && !u.uedibility) ? " then starts to tingle" : "");
334 if (sobj->blessed && !u.uedibility) {
335 boolean savebeginner = flags.beginner; /* prevent non-delivery of */
336 flags.beginner = FALSE; /* message */
337 strange_feeling(sobj, buf);
338 flags.beginner = savebeginner;
339 u.uedibility = 1;
340 } else
341 strange_feeling(sobj, buf);
342 }
343 return !stale;
344 } else if (!ct) {
345 known = TRUE;
346 You("%s %s nearby.", sobj ? "smell" : "sense", what);
347 if (sobj && sobj->blessed) {
348 if (!u.uedibility) pline("Your %s starts to tingle.", body_part(NOSE));
349 u.uedibility = 1;
350 }
351 } else {
352 struct obj *temp;
353 known = TRUE;
354 cls();
355 u.uinwater = 0;
356 for (obj = fobj; obj; obj = obj->nobj)
357 if ((temp = o_in(obj, oclass)) != 0) {
358 if (temp != obj) {
359 temp->ox = obj->ox;
360 temp->oy = obj->oy;
361 }
362 map_object(temp,1);
363 }
364 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
365 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */
366 for (obj = mtmp->minvent; obj; obj = obj->nobj)
367 if ((temp = o_in(obj, oclass)) != 0) {
368 temp->ox = mtmp->mx;
369 temp->oy = mtmp->my;
370 map_object(temp,1);
371 break; /* skip rest of this monster's inventory */
372 }
373 newsym(u.ux,u.uy);
374 if (sobj) {
375 if (sobj->blessed) {
376 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
377 u.uedibility ? "continues" : "starts", what);
378 u.uedibility = 1;
379 } else
380 Your("%s tingles and you smell %s.", body_part(NOSE), what);
381 }
382 else You("sense %s.", what);
383 display_nhwindow(WIN_MAP, TRUE);
384 exercise(A_WIS, TRUE);
385 docrt();
386 u.uinwater = uw;
387 if (Underwater) under_water(2);
388 if (u.uburied) under_ground(2);
389 }
390 return(0);
391 }
392
393 /*
394 * Used for scrolls, potions, spells, and crystal balls. Returns:
395 *
396 * 1 - nothing was detected
397 * 0 - something was detected
398 */
399 int
object_detect(detector,class,quiet)400 object_detect(detector, class, quiet)
401 struct obj *detector; /* object doing the detecting */
402 int class; /* an object class, 0 for all */
403 boolean quiet; /* don't output any message */
404 {
405 register int x, y;
406 char stuff[BUFSZ];
407 int is_cursed = (detector && detector->cursed);
408 int do_dknown = (detector && (detector->oclass == POTION_CLASS ||
409 detector->oclass == SPBOOK_CLASS) &&
410 detector->blessed);
411 int ct = 0, ctu = 0;
412 register struct obj *obj, *otmp = (struct obj *)0;
413 register struct monst *mtmp;
414 int uw = u.uinwater;
415 int sym, boulder = 0;
416
417 if (class < 0 || class >= MAXOCLASSES) {
418 warning("object_detect: illegal class %d", class);
419 class = 0;
420 }
421
422 /* Special boulder symbol check - does the class symbol happen
423 * to match iflags.bouldersym which is a user-defined?
424 * If so, that means we aren't sure what they really wanted to
425 * detect. Rather than trump anything, show both possibilities.
426 * We can exclude checking the buried obj chain for boulders below.
427 */
428 sym = class ? def_oc_syms[class] : 0;
429 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
430 boulder = ROCK_CLASS;
431
432 if (Hallucination || (Confusion && class == SCROLL_CLASS))
433 Strcpy(stuff, something);
434 else
435 Strcpy(stuff, class ? oclass_names[class] : "objects");
436 if (boulder && class != ROCK_CLASS) Strcat(stuff, " and/or large stones");
437
438 if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj);
439
440 for (obj = fobj; obj; obj = obj->nobj) {
441 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
442 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
443 else ct++;
444 }
445 if (do_dknown) do_dknown_of(obj);
446 }
447
448 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
449 if (!class || o_in(obj, class)) {
450 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
451 else ct++;
452 }
453 if (do_dknown) do_dknown_of(obj);
454 }
455
456 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
457 if (DEADMONSTER(mtmp)) continue;
458 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
459 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) ct++;
460 if (do_dknown) do_dknown_of(obj);
461 }
462 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
463 (!class || class == objects[mtmp->mappearance].oc_class)) ||
464 #ifndef GOLDOBJ
465 (mtmp->mgold && (!class || class == COIN_CLASS))) {
466 #else
467 (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
468 #endif
469 ct++;
470 break;
471 }
472 }
473
474 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
475 if (!ctu) {
476 if (detector)
477 if (!quiet) strange_feeling(detector, "You feel a lack of something.");
478 return 1;
479 }
480
481 if (!quiet) You("sense %s nearby.", stuff);
482 return 0;
483 }
484
485 if (!quiet) cls();
486
487 u.uinwater = 0;
488 /*
489 * Map all buried objects first.
490 */
491 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
492 if (!class || (otmp = o_in(obj, class))) {
493 if (class) {
494 if (otmp != obj) {
495 otmp->ox = obj->ox;
496 otmp->oy = obj->oy;
497 }
498 map_object(otmp, 1);
499 } else
500 map_object(obj, 1);
501 }
502 /*
503 * If we are mapping all objects, map only the top object of a pile or
504 * the first object in a monster's inventory. Otherwise, go looking
505 * for a matching object class and display the first one encountered
506 * at each location.
507 *
508 * Objects on the floor override buried objects.
509 */
510 for (x = 1; x < COLNO; x++)
511 for (y = 0; y < ROWNO; y++)
512 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
513 if ((!class && !boulder) ||
514 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) {
515 if (class || boulder) {
516 if (otmp != obj) {
517 otmp->ox = obj->ox;
518 otmp->oy = obj->oy;
519 }
520 map_object(otmp, 1);
521 } else
522 map_object(obj, 1);
523 break;
524 }
525
526 /* Objects in the monster's inventory override floor objects. */
527 for (mtmp = fmon ; mtmp ; mtmp = mtmp->nmon) {
528 if (DEADMONSTER(mtmp)) continue;
529 for (obj = mtmp->minvent; obj; obj = obj->nobj)
530 if ((!class && !boulder) ||
531 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) {
532 if (!class && !boulder) otmp = obj;
533 otmp->ox = mtmp->mx; /* at monster location */
534 otmp->oy = mtmp->my;
535 map_object(otmp, 1);
536 break;
537 }
538 /* Allow a mimic to override the detected objects it is carrying. */
539 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
540 (!class || class == objects[mtmp->mappearance].oc_class)) {
541 struct obj temp;
542
543 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
544 temp.ox = mtmp->mx;
545 temp.oy = mtmp->my;
546 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
547 map_object(&temp, 1);
548 #ifndef GOLDOBJ
549 } else if (mtmp->mgold && (!class || class == COIN_CLASS)) {
550 #else
551 } else if (findgold(mtmp->minvent) && (!class || class == COIN_CLASS)) {
552 #endif
553 struct obj gold;
554
555 gold.otyp = GOLD_PIECE;
556 gold.ox = mtmp->mx;
557 gold.oy = mtmp->my;
558 map_object(&gold, 1);
559 }
560 }
561
562 newsym(u.ux,u.uy);
563 if (!quiet) {
564 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
565 display_nhwindow(WIN_MAP, TRUE);
566 /*
567 * What are we going to do when the hero does an object detect while blind
568 * and the detected object covers a known pool?
569 */
570 docrt(); /* this will correctly reset vision */
571 }
572
573 u.uinwater = uw;
574 if (Underwater) under_water(2);
575 if (u.uburied) under_ground(2);
576 return 0;
577 }
578
579 /*
580 * Used by: crystal balls, potions, fountains
581 *
582 * Returns 1 if nothing was detected.
583 * Returns 0 if something was detected.
584 */
585 int
monster_detect(otmp,mclass)586 monster_detect(otmp, mclass)
587 register struct obj *otmp; /* detecting object (if any) */
588 int mclass; /* monster class, 0 for all */
589 {
590 register struct monst *mtmp;
591 int mcnt = 0;
592
593
594 /* Note: This used to just check fmon for a non-zero value
595 * but in versions since 3.3.0 fmon can test TRUE due to the
596 * presence of dmons, so we have to find at least one
597 * with positive hit-points to know for sure.
598 */
599 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
600 if (!DEADMONSTER(mtmp)) {
601 mcnt++;
602 break;
603 }
604
605 if (!mcnt) {
606 if (otmp)
607 strange_feeling(otmp, Hallucination ?
608 "You get the heebie jeebies." :
609 "You feel threatened.");
610 return 1;
611 } else {
612 boolean woken = FALSE;
613
614 cls();
615 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
616 if (DEADMONSTER(mtmp)) continue;
617 if (!mclass || mtmp->data->mlet == mclass ||
618 (mtmp->data == &mons[PM_LONG_WORM] && mclass == S_WORM_TAIL))
619 if (mtmp->mx > 0) {
620 if (mclass && def_monsyms[mclass] == ' ')
621 show_glyph(mtmp->mx,mtmp->my,
622 detected_mon_to_glyph(mtmp));
623 else
624 show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp));
625 /* don't be stingy - display entire worm */
626 if (mtmp->data == &mons[PM_LONG_WORM]) detect_wsegs(mtmp,0);
627 }
628 if (otmp && otmp->cursed &&
629 (mtmp->msleeping || !mtmp->mcanmove)) {
630 mtmp->msleeping = mtmp->mfrozen = 0;
631 mtmp->mcanmove = 1;
632 woken = TRUE;
633 }
634 }
635 display_self();
636 You("sense the presence of monsters.");
637 if (woken)
638 pline("Monsters sense the presence of you.");
639 display_nhwindow(WIN_MAP, TRUE);
640 docrt();
641 if (Underwater) under_water(2);
642 if (u.uburied) under_ground(2);
643 }
644 return 0;
645 }
646
647 STATIC_OVL void
sense_trap(trap,x,y,src_cursed)648 sense_trap(trap, x, y, src_cursed)
649 struct trap *trap;
650 xchar x, y;
651 int src_cursed;
652 {
653 if (Hallucination || src_cursed) {
654 struct obj obj; /* fake object */
655 if (trap) {
656 obj.ox = trap->tx;
657 obj.oy = trap->ty;
658 } else {
659 obj.ox = x;
660 obj.oy = y;
661 }
662 obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
663 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
664 map_object(&obj,1);
665 } else if (trap) {
666 map_trap(trap,1);
667 trap->tseen = 1;
668 } else {
669 struct trap temp_trap; /* fake trap */
670 temp_trap.tx = x;
671 temp_trap.ty = y;
672 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
673 map_trap(&temp_trap,1);
674 }
675
676 }
677
678 /* the detections are pulled out so they can */
679 /* also be used in the crystal ball routine */
680 /* returns 1 if nothing was detected */
681 /* returns 0 if something was detected */
682 int
trap_detect(sobj)683 trap_detect(sobj)
684 register struct obj *sobj;
685 /* sobj is null if crystal ball, *scroll if gold detection scroll */
686 {
687 register struct trap *ttmp;
688 register struct obj *obj;
689 register int door;
690 int uw = u.uinwater;
691 boolean found = FALSE;
692 coord cc;
693
694 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
695 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
696 goto outtrapmap;
697 else found = TRUE;
698 }
699 for (obj = fobj; obj; obj = obj->nobj) {
700 if ((obj->otyp == LARGE_BOX || obj->otyp == CHEST || obj->otyp == IRON_SAFE) &&
701 obj->otrapped) {
702 if (obj->ox != u.ux || obj->oy != u.uy)
703 goto outtrapmap;
704 else found = TRUE;
705 }
706 }
707 for (door = 0; door < doorindex; door++) {
708 cc = doors[door];
709 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
710 if (cc.x != u.ux || cc.y != u.uy)
711 goto outtrapmap;
712 else found = TRUE;
713 }
714 }
715 if (!found) {
716 char buf[42];
717 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
718 strange_feeling(sobj,buf);
719 return(1);
720 }
721 /* traps exist, but only under me - no separate display required */
722 Your("%s itch.", makeplural(body_part(TOE)));
723 return(0);
724 outtrapmap:
725 cls();
726
727 u.uinwater = 0;
728 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
729 sense_trap(ttmp, 0, 0, sobj && sobj->cursed);
730
731 for (obj = fobj; obj; obj = obj->nobj)
732 if ((obj->otyp == LARGE_BOX || obj->otyp == CHEST || obj->otyp == IRON_SAFE) &&
733 obj->otrapped)
734 sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed);
735
736 for (door = 0; door < doorindex; door++) {
737 cc = doors[door];
738 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
739 sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed);
740 }
741
742 newsym(u.ux,u.uy);
743 You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped");
744 display_nhwindow(WIN_MAP, TRUE);
745 docrt();
746 u.uinwater = uw;
747 if (Underwater) under_water(2);
748 if (u.uburied) under_ground(2);
749 return(0);
750 }
751
752 const char *
level_distance(where)753 level_distance(where)
754 d_level *where;
755 {
756 register schar ll = depth(&u.uz) - depth(where);
757 register boolean indun = (u.uz.dnum == where->dnum);
758
759 if (ll < 0) {
760 if (ll < (-8 - rn2(3)))
761 if (!indun) return "far away";
762 else return "far below";
763 else if (ll < -1)
764 if (!indun) return "away below you";
765 else return "below you";
766 else
767 if (!indun) return "in the distance";
768 else return "just below";
769 } else if (ll > 0) {
770 if (ll > (8 + rn2(3)))
771 if (!indun) return "far away";
772 else return "far above";
773 else if (ll > 1)
774 if (!indun) return "away above you";
775 else return "above you";
776 else
777 if (!indun) return "in the distance";
778 else return "just above";
779 } else
780 if (!indun) return "in the distance";
781 else return "near you";
782 }
783
784 static const struct {
785 const char *what;
786 d_level *where;
787 } level_detects[] = {
788 { "Delphi", &oracle_level },
789 { "Medusa's lair", &medusa_level },
790 { "a castle", &stronghold_level },
791 { "the Wizard of Yendor's tower", &wiz1_level },
792 };
793
794 void
use_crystal_ball(obj)795 use_crystal_ball(obj)
796 struct obj *obj;
797 {
798 char ch;
799 int oops;
800
801 if (Blind) {
802 pline("Too bad you can't see %s.", the(xname(obj)));
803 return;
804 }
805 oops = (rnd(obj->blessed ? 13 : 20) > ACURR(A_INT) || obj->cursed);
806 if (oops && (obj->spe > 0)) {
807 switch (rnd(obj->oartifact ? 4 : 5)) {
808 case 1 : pline("%s too much to comprehend!", Tobjnam(obj, "are"));
809 break;
810 case 2 : pline("%s you!", Tobjnam(obj, "confuse"));
811 make_confused(HConfusion + rnd(100),FALSE);
812 break;
813 case 3 : if (!resists_blnd(&youmonst)) {
814 pline("%s your vision!", Tobjnam(obj, "damage"));
815 make_blinded(Blinded + rnd(100),FALSE);
816 if (!Blind) Your("%s", vision_clears);
817 } else {
818 pline("%s your vision.", Tobjnam(obj, "assault"));
819 You("are unaffected!");
820 }
821 break;
822 case 4 : pline("%s your mind!", Tobjnam(obj, "zap"));
823 (void) make_hallucinated(HHallucination + rnd(100),FALSE,0L);
824 break;
825 case 5 : pline("%s!", Tobjnam(obj, "explode"));
826 useup(obj);
827 obj = 0; /* it's gone */
828 losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN);
829 break;
830 }
831 if (obj) consume_obj_charge(obj, TRUE);
832 return;
833 }
834
835 if (Hallucination) {
836 if (!obj->spe) {
837 pline("All you see is funky %s haze.", hcolor((char *)0));
838 } else {
839 switch(rnd(6)) {
840 case 1 : You("grok some groovy globs of incandescent lava.");
841 break;
842 case 2 : pline("Whoa! Psychedelic colors, %s!",
843 poly_gender() == 1 ? "babe" : "dude");
844 break;
845 case 3 : pline_The("crystal pulses with sinister %s light!",
846 hcolor((char *)0));
847 break;
848 case 4 : You("see goldfish swimming above fluorescent rocks.");
849 break;
850 case 5 : You("see tiny snowflakes spinning around a miniature farmhouse.");
851 break;
852 default: pline("Oh wow... like a kaleidoscope!");
853 break;
854 }
855 consume_obj_charge(obj, TRUE);
856 }
857 return;
858 }
859
860 /* read a single character */
861 if (flags.verbose) You("may look for an object or monster symbol.");
862 ch = yn_function("What do you look for?", (char *)0, '\0');
863 /* Don't filter out ' ' here; it has a use */
864 if ((ch != def_monsyms[S_GHOST]) && index(quitchars,ch)) {
865 if (flags.verbose) pline("%s", Never_mind);
866 return;
867 }
868 You("peer into %s...", the(xname(obj)));
869 nomul(-rnd(10), "gazing into a crystal ball");
870 nomovemsg = "";
871 if (obj->spe <= 0)
872 pline_The("vision is unclear.");
873 else {
874 int class;
875 int ret = 0;
876
877 makeknown(CRYSTAL_BALL);
878 consume_obj_charge(obj, TRUE);
879
880 /* special case: accept ']' as synonym for mimic
881 * we have to do this before the def_char_to_objclass check
882 */
883 if (ch == DEF_MIMIC_DEF) ch = DEF_MIMIC;
884
885 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
886 ret = object_detect((struct obj *)0, class, FALSE);
887 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
888 ret = monster_detect((struct obj *)0, class);
889 else if (iflags.bouldersym && (ch == iflags.bouldersym))
890 ret = object_detect((struct obj *)0, ROCK_CLASS, FALSE);
891 else switch(ch) {
892 case '^':
893 ret = trap_detect((struct obj *)0);
894 break;
895 default:
896 {
897 int i = rn2(SIZE(level_detects));
898 You("see %s, %s.",
899 level_detects[i].what,
900 level_distance(level_detects[i].where));
901 }
902 ret = 0;
903 break;
904 }
905
906 if (ret) {
907 if (!rn2(100)) /* make them nervous */
908 You("see the Wizard of Yendor gazing out at you.");
909 else pline_The("vision is unclear.");
910 }
911 }
912 return;
913 }
914
915 STATIC_OVL void
show_map_spot(x,y)916 show_map_spot(x, y)
917 register int x, y;
918 {
919 register struct rm *lev;
920
921 if (Confusion && rn2(7)) return;
922 lev = &levl[x][y];
923
924 lev->seenv = SVALL;
925
926 /* Secret corridors are found, but not secret doors. */
927 if (lev->typ == SCORR) {
928 lev->typ = CORR;
929 unblock_point(x,y);
930 }
931
932 /* if we don't remember an object or trap there, map it */
933 if (lev->typ == ROOM ?
934 (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) &&
935 glyph_to_cmap(lev->glyph) != ROOM) :
936 (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) {
937 if (level.flags.hero_memory) {
938 magic_map_background(x,y,0);
939 newsym(x,y); /* show it, if not blocked */
940 } else {
941 magic_map_background(x,y,1); /* display it */
942 }
943 }
944 }
945
946 void
do_mapping()947 do_mapping()
948 {
949 register int zx, zy;
950 int uw = u.uinwater;
951
952 u.uinwater = 0;
953 for (zx = 1; zx < COLNO; zx++)
954 for (zy = 0; zy < ROWNO; zy++)
955 show_map_spot(zx, zy);
956 exercise(A_WIS, TRUE);
957 u.uinwater = uw;
958 if (!level.flags.hero_memory || Underwater) {
959 flush_screen(1); /* flush temp screen */
960 display_nhwindow(WIN_MAP, TRUE); /* wait */
961 docrt();
962 }
963 }
964
965 void
do_vicinity_map()966 do_vicinity_map()
967 {
968 register int zx, zy;
969 int lo_y = (u.uy-5 < 0 ? 0 : u.uy-5),
970 hi_y = (u.uy+6 > ROWNO ? ROWNO : u.uy+6),
971 lo_x = (u.ux-9 < 1 ? 1 : u.ux-9), /* avoid column 0 */
972 hi_x = (u.ux+10 > COLNO ? COLNO : u.ux+10);
973
974 for (zx = lo_x; zx < hi_x; zx++)
975 for (zy = lo_y; zy < hi_y; zy++)
976 show_map_spot(zx, zy);
977
978 if (!level.flags.hero_memory || Underwater) {
979 flush_screen(1); /* flush temp screen */
980 display_nhwindow(WIN_MAP, TRUE); /* wait */
981 docrt();
982 }
983 }
984
985 /* convert a secret door into a normal door */
986 void
cvt_sdoor_to_door(lev)987 cvt_sdoor_to_door(lev)
988 struct rm *lev;
989 {
990 int newmask = lev->doormask & ~WM_MASK;
991
992 #ifdef REINCARNATION
993 if (Is_rogue_level(&u.uz))
994 /* rogue didn't have doors, only doorways */
995 newmask = D_NODOOR;
996 else
997 #endif
998 /* newly exposed door is closed */
999 if (!(newmask & D_LOCKED)) newmask |= D_CLOSED;
1000
1001 lev->typ = DOOR;
1002 lev->doormask = newmask;
1003 }
1004
1005
1006 STATIC_PTR void
findone(zx,zy,num)1007 findone(zx,zy,num)
1008 int zx,zy;
1009 genericptr_t num;
1010 {
1011 register struct trap *ttmp;
1012 register struct monst *mtmp;
1013
1014 if(levl[zx][zy].typ == SDOOR) {
1015 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1016 magic_map_background(zx, zy, 0);
1017 newsym(zx, zy);
1018 (*(int*)num)++;
1019 } else if(levl[zx][zy].typ == SCORR) {
1020 levl[zx][zy].typ = CORR;
1021 unblock_point(zx,zy);
1022 magic_map_background(zx, zy, 0);
1023 newsym(zx, zy);
1024 (*(int*)num)++;
1025 } else if ((ttmp = t_at(zx, zy)) != 0) {
1026 if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1027 ttmp->tseen = 1;
1028 newsym(zx,zy);
1029 (*(int*)num)++;
1030 }
1031 } else if ((mtmp = m_at(zx, zy)) != 0) {
1032 if(mtmp->m_ap_type) {
1033 seemimic(mtmp);
1034 (*(int*)num)++;
1035 }
1036 if (mtmp->mundetected &&
1037 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1038 mtmp->mundetected = 0;
1039 newsym(zx, zy);
1040 (*(int*)num)++;
1041 }
1042 if (!canspotmon(mtmp) &&
1043 !glyph_is_invisible(levl[zx][zy].glyph))
1044 map_invisible(zx, zy);
1045 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1046 unmap_object(zx, zy);
1047 newsym(zx, zy);
1048 (*(int*)num)++;
1049 }
1050 }
1051
1052 STATIC_PTR void
openone(zx,zy,num)1053 openone(zx,zy,num)
1054 int zx,zy;
1055 genericptr_t num;
1056 {
1057 register struct trap *ttmp;
1058 register struct obj *otmp;
1059
1060 if(OBJ_AT(zx, zy)) {
1061 for(otmp = level.objects[zx][zy];
1062 otmp; otmp = otmp->nexthere) {
1063 if(Is_box(otmp) && otmp->olocked) {
1064 otmp->olocked = 0;
1065 (*(int*)num)++;
1066 }
1067 }
1068 /* let it fall to the next cases. could be on trap. */
1069 }
1070 if(levl[zx][zy].typ == SDOOR || (levl[zx][zy].typ == DOOR &&
1071 (levl[zx][zy].doormask & (D_CLOSED|D_LOCKED)))) {
1072 if(levl[zx][zy].typ == SDOOR)
1073 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1074 if(levl[zx][zy].doormask & D_TRAPPED) {
1075 if(distu(zx, zy) < 3) b_trapped("door", 0);
1076 else Norep("You %s an explosion!",
1077 cansee(zx, zy) ? "see" :
1078 (flags.soundok ? "hear" :
1079 "feel the shock of"));
1080 wake_nearto(zx, zy, 11*11);
1081 levl[zx][zy].doormask = D_NODOOR;
1082 } else
1083 levl[zx][zy].doormask = D_ISOPEN;
1084 unblock_point(zx, zy);
1085 newsym(zx, zy);
1086 (*(int*)num)++;
1087 } else if(levl[zx][zy].typ == SCORR) {
1088 levl[zx][zy].typ = CORR;
1089 unblock_point(zx, zy);
1090 newsym(zx, zy);
1091 (*(int*)num)++;
1092 } else if ((ttmp = t_at(zx, zy)) != 0) {
1093 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1094 ttmp->tseen = 1;
1095 newsym(zx,zy);
1096 (*(int*)num)++;
1097 }
1098 } else if (find_drawbridge(&zx, &zy)) {
1099 /* make sure it isn't an open drawbridge */
1100 open_drawbridge(zx, zy);
1101 (*(int*)num)++;
1102 }
1103 }
1104
1105 int
findit()1106 findit() /* returns number of things found */
1107 {
1108 int num = 0;
1109
1110 if(u.uswallow) return(0);
1111 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1112 return(num);
1113 }
1114
1115 int
openit()1116 openit() /* returns number of things found and opened */
1117 {
1118 int num = 0;
1119
1120 if(u.uswallow) {
1121 if (is_animal(u.ustuck->data)) {
1122 if (Blind) pline("Its mouth opens!");
1123 else pline("%s opens its mouth!", Monnam(u.ustuck));
1124 }
1125 expels(u.ustuck, u.ustuck->data, TRUE);
1126 return(-1);
1127 }
1128
1129 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1130 return(num);
1131 }
1132
1133 void
find_trap(trap)1134 find_trap(trap)
1135 struct trap *trap;
1136 {
1137 int tt = what_trap(trap->ttyp);
1138 boolean cleared = FALSE;
1139
1140 trap->tseen = 1;
1141 exercise(A_WIS, TRUE);
1142 if (Blind)
1143 feel_location(trap->tx, trap->ty);
1144 else
1145 newsym(trap->tx, trap->ty);
1146
1147 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1148 /* There's too much clutter to see your find otherwise */
1149 cls();
1150 map_trap(trap, 1);
1151 display_self();
1152 cleared = TRUE;
1153 }
1154
1155 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1156
1157 if (cleared) {
1158 display_nhwindow(WIN_MAP, TRUE); /* wait */
1159 docrt();
1160 }
1161 }
1162
1163 int
dosearch0(aflag)1164 dosearch0(aflag)
1165 register int aflag;
1166 {
1167 #ifdef GCC_BUG
1168 /* some versions of gcc seriously muck up nested loops. if you get strange
1169 crashes while searching in a version compiled with gcc, try putting
1170 #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
1171 makefile).
1172 */
1173 volatile xchar x, y;
1174 #else
1175 register xchar x, y;
1176 #endif
1177 register struct trap *trap;
1178 register struct monst *mtmp;
1179
1180 if(u.uswallow) {
1181 if (!aflag)
1182 pline("What are you looking for? The exit?");
1183 } else {
1184 int fund = (uwep && uwep->oartifact &&
1185 spec_ability(uwep, SPFX_SEARCH)) ?
1186 uwep->spe : 0;
1187 if (ublindf && ublindf->otyp == LENSES && !Blind)
1188 fund += 2; /* JDS: lenses help searching */
1189 if (fund > 5) fund = 5;
1190 for(x = u.ux-1; x < u.ux+2; x++)
1191 for(y = u.uy-1; y < u.uy+2; y++) {
1192 if(!isok(x,y)) continue;
1193 if(x != u.ux || y != u.uy) {
1194 if (Blind && !aflag) feel_location(x,y);
1195 if(levl[x][y].typ == SDOOR) {
1196 if(rnl(7-fund)) continue;
1197 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1198 exercise(A_WIS, TRUE);
1199 nomul(0, 0);
1200 if (Blind && !aflag)
1201 feel_location(x,y); /* make sure it shows up */
1202 else
1203 newsym(x,y);
1204 } else if(levl[x][y].typ == SCORR) {
1205 if(rnl(7-fund)) continue;
1206 levl[x][y].typ = CORR;
1207 unblock_point(x,y); /* vision */
1208 exercise(A_WIS, TRUE);
1209 nomul(0, 0);
1210 newsym(x,y);
1211 } else {
1212 /* Be careful not to find anything in an SCORR or SDOOR */
1213 if((mtmp = m_at(x, y)) && !aflag) {
1214 if(mtmp->m_ap_type) {
1215 seemimic(mtmp);
1216 find: exercise(A_WIS, TRUE);
1217 if (!canspotmon(mtmp)) {
1218 if (glyph_is_invisible(levl[x][y].glyph)) {
1219 /* found invisible monster in a square
1220 * which already has an 'I' in it.
1221 * Logically, this should still take
1222 * time and lead to a return(1), but if
1223 * we did that the player would keep
1224 * finding the same monster every turn.
1225 */
1226 continue;
1227 } else {
1228 You_feel("an unseen monster!");
1229 map_invisible(x, y);
1230 }
1231 } else if (!sensemon(mtmp))
1232 You("find %s.", a_monnam(mtmp));
1233 return(1);
1234 }
1235 if(!canspotmon(mtmp)) {
1236 if (mtmp->mundetected &&
1237 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1238 mtmp->mundetected = 0;
1239 newsym(x,y);
1240 goto find;
1241 }
1242 }
1243
1244 /* see if an invisible monster has moved--if Blind,
1245 * feel_location() already did it
1246 */
1247 if (!aflag && !mtmp && !Blind &&
1248 glyph_is_invisible(levl[x][y].glyph)) {
1249 unmap_object(x,y);
1250 newsym(x,y);
1251 }
1252
1253 if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) {
1254 nomul(0, 0);
1255
1256 if (trap->ttyp == STATUE_TRAP) {
1257 if (activate_statue_trap(trap, x, y, FALSE))
1258 exercise(A_WIS, TRUE);
1259 return(1);
1260 } else {
1261 find_trap(trap);
1262 }
1263 }
1264 }
1265 }
1266 }
1267 }
1268 return(1);
1269 }
1270
1271 int
dosearch()1272 dosearch()
1273 {
1274 return(dosearch0(0));
1275 }
1276
1277 /* Pre-map the sokoban levels */
1278 void
sokoban_detect()1279 sokoban_detect()
1280 {
1281 register int x, y;
1282 register struct trap *ttmp;
1283 register struct obj *obj;
1284
1285 /* Map the background and boulders */
1286 for (x = 1; x < COLNO; x++)
1287 for (y = 0; y < ROWNO; y++) {
1288 if (IS_WALL(levl[x][y].typ))
1289 levl[x][y].seenv = SVALL;
1290 else if (levl[x][y].typ == SDOOR)
1291 levl[x][y].typ = DOOR;
1292 else if (levl[x][y].typ == SCORR)
1293 levl[x][y].typ = CORR;
1294
1295 /* all Sokoban floors only shown lit when dark_room is deactivated */
1296 levl[x][y].waslit = (levl[x][y].typ != CORR) ? (!iflags.dark_room) : levl[x][y].lit;
1297 map_background(x, y, 1);
1298 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
1299 if (obj->otyp == BOULDER)
1300 map_object(obj, 1);
1301 }
1302
1303 /* Map the traps */
1304 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1305 ttmp->tseen = 1;
1306 map_trap(ttmp, 1);
1307 }
1308 }
1309
1310
1311 /*detect.c*/
1312