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)400 object_detect(detector, class)
401 struct obj *detector; /* object doing the detecting */
402 int class; /* an object class, 0 for all */
403 {
404 register int x, y;
405 char stuff[BUFSZ];
406 int is_cursed = (detector && detector->cursed);
407 int do_dknown = (detector && (detector->oclass == POTION_CLASS ||
408 detector->oclass == SPBOOK_CLASS) &&
409 detector->blessed);
410 int ct = 0, ctu = 0;
411 register struct obj *obj, *otmp = (struct obj *)0;
412 register struct monst *mtmp;
413 int uw = u.uinwater;
414 int sym, boulder = 0;
415
416 if (class < 0 || class >= MAXOCLASSES) {
417 warning("object_detect: illegal class %d", class);
418 class = 0;
419 }
420
421 /* Special boulder symbol check - does the class symbol happen
422 * to match iflags.bouldersym which is a user-defined?
423 * If so, that means we aren't sure what they really wanted to
424 * detect. Rather than trump anything, show both possibilities.
425 * We can exclude checking the buried obj chain for boulders below.
426 */
427 sym = class ? def_oc_syms[class] : 0;
428 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
429 boulder = ROCK_CLASS;
430
431 if (Hallucination || (Confusion && class == SCROLL_CLASS))
432 Strcpy(stuff, something);
433 else
434 Strcpy(stuff, class ? oclass_names[class] : "objects");
435 if (boulder && class != ROCK_CLASS) Strcat(stuff, " and/or large stones");
436
437 if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj);
438
439 for (obj = fobj; obj; obj = obj->nobj) {
440 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
441 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
442 else ct++;
443 }
444 if (do_dknown) do_dknown_of(obj);
445 }
446
447 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
448 if (!class || o_in(obj, class)) {
449 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
450 else ct++;
451 }
452 if (do_dknown) do_dknown_of(obj);
453 }
454
455 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
456 if (DEADMONSTER(mtmp)) continue;
457 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
458 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) ct++;
459 if (do_dknown) do_dknown_of(obj);
460 }
461 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
462 (!class || class == objects[mtmp->mappearance].oc_class)) ||
463 #ifndef GOLDOBJ
464 (mtmp->mgold && (!class || class == COIN_CLASS))) {
465 #else
466 (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
467 #endif
468 ct++;
469 break;
470 }
471 }
472
473 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
474 if (!ctu) {
475 if (detector)
476 strange_feeling(detector, "You feel a lack of something.");
477 return 1;
478 }
479
480 You("sense %s nearby.", stuff);
481 return 0;
482 }
483
484 cls();
485
486 u.uinwater = 0;
487 /*
488 * Map all buried objects first.
489 */
490 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
491 if (!class || (otmp = o_in(obj, class))) {
492 if (class) {
493 if (otmp != obj) {
494 otmp->ox = obj->ox;
495 otmp->oy = obj->oy;
496 }
497 map_object(otmp, 1);
498 } else
499 map_object(obj, 1);
500 }
501 /*
502 * If we are mapping all objects, map only the top object of a pile or
503 * the first object in a monster's inventory. Otherwise, go looking
504 * for a matching object class and display the first one encountered
505 * at each location.
506 *
507 * Objects on the floor override buried objects.
508 */
509 for (x = 1; x < COLNO; x++)
510 for (y = 0; y < ROWNO; y++)
511 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
512 if ((!class && !boulder) ||
513 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) {
514 if (class || boulder) {
515 if (otmp != obj) {
516 otmp->ox = obj->ox;
517 otmp->oy = obj->oy;
518 }
519 map_object(otmp, 1);
520 } else
521 map_object(obj, 1);
522 break;
523 }
524
525 /* Objects in the monster's inventory override floor objects. */
526 for (mtmp = fmon ; mtmp ; mtmp = mtmp->nmon) {
527 if (DEADMONSTER(mtmp)) continue;
528 for (obj = mtmp->minvent; obj; obj = obj->nobj)
529 if ((!class && !boulder) ||
530 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) {
531 if (!class && !boulder) otmp = obj;
532 otmp->ox = mtmp->mx; /* at monster location */
533 otmp->oy = mtmp->my;
534 map_object(otmp, 1);
535 break;
536 }
537 /* Allow a mimic to override the detected objects it is carrying. */
538 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
539 (!class || class == objects[mtmp->mappearance].oc_class)) {
540 struct obj temp;
541
542 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
543 temp.ox = mtmp->mx;
544 temp.oy = mtmp->my;
545 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
546 map_object(&temp, 1);
547 #ifndef GOLDOBJ
548 } else if (mtmp->mgold && (!class || class == COIN_CLASS)) {
549 #else
550 } else if (findgold(mtmp->minvent) && (!class || class == COIN_CLASS)) {
551 #endif
552 struct obj gold;
553
554 gold.otyp = GOLD_PIECE;
555 gold.ox = mtmp->mx;
556 gold.oy = mtmp->my;
557 map_object(&gold, 1);
558 }
559 }
560
561 newsym(u.ux,u.uy);
562 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
563 display_nhwindow(WIN_MAP, TRUE);
564 /*
565 * What are we going to do when the hero does an object detect while blind
566 * and the detected object covers a known pool?
567 */
568 docrt(); /* this will correctly reset vision */
569
570 u.uinwater = uw;
571 if (Underwater) under_water(2);
572 if (u.uburied) under_ground(2);
573 return 0;
574 }
575
576 /*
577 * Used by: crystal balls, potions, fountains
578 *
579 * Returns 1 if nothing was detected.
580 * Returns 0 if something was detected.
581 */
582 int
monster_detect(otmp,mclass)583 monster_detect(otmp, mclass)
584 register struct obj *otmp; /* detecting object (if any) */
585 int mclass; /* monster class, 0 for all */
586 {
587 register struct monst *mtmp;
588 int mcnt = 0;
589
590
591 /* Note: This used to just check fmon for a non-zero value
592 * but in versions since 3.3.0 fmon can test TRUE due to the
593 * presence of dmons, so we have to find at least one
594 * with positive hit-points to know for sure.
595 */
596 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
597 if (!DEADMONSTER(mtmp)) {
598 mcnt++;
599 break;
600 }
601
602 if (!mcnt) {
603 if (otmp)
604 strange_feeling(otmp, Hallucination ?
605 "You get the heebie jeebies." :
606 "You feel threatened.");
607 return 1;
608 } else {
609 boolean woken = FALSE;
610
611 cls();
612 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
613 if (DEADMONSTER(mtmp)) continue;
614 if (!mclass || mtmp->data->mlet == mclass ||
615 (mtmp->data == &mons[PM_LONG_WORM] && mclass == S_WORM_TAIL))
616 if (mtmp->mx > 0) {
617 if (mclass && def_monsyms[mclass] == ' ')
618 show_glyph(mtmp->mx,mtmp->my,
619 detected_mon_to_glyph(mtmp));
620 else
621 show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp));
622 /* don't be stingy - display entire worm */
623 if (mtmp->data == &mons[PM_LONG_WORM]) detect_wsegs(mtmp,0);
624 }
625 if (otmp && otmp->cursed &&
626 (mtmp->msleeping || !mtmp->mcanmove)) {
627 mtmp->msleeping = mtmp->mfrozen = 0;
628 mtmp->mcanmove = 1;
629 woken = TRUE;
630 }
631 }
632 display_self();
633 You("sense the presence of monsters.");
634 if (woken)
635 pline("Monsters sense the presence of you.");
636 display_nhwindow(WIN_MAP, TRUE);
637 docrt();
638 if (Underwater) under_water(2);
639 if (u.uburied) under_ground(2);
640 }
641 return 0;
642 }
643
644 STATIC_OVL void
sense_trap(trap,x,y,src_cursed)645 sense_trap(trap, x, y, src_cursed)
646 struct trap *trap;
647 xchar x, y;
648 int src_cursed;
649 {
650 if (Hallucination || src_cursed) {
651 struct obj obj; /* fake object */
652 if (trap) {
653 obj.ox = trap->tx;
654 obj.oy = trap->ty;
655 } else {
656 obj.ox = x;
657 obj.oy = y;
658 }
659 obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
660 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
661 map_object(&obj,1);
662 } else if (trap) {
663 map_trap(trap,1);
664 trap->tseen = 1;
665 } else {
666 struct trap temp_trap; /* fake trap */
667 temp_trap.tx = x;
668 temp_trap.ty = y;
669 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
670 map_trap(&temp_trap,1);
671 }
672
673 }
674
675 /* the detections are pulled out so they can */
676 /* also be used in the crystal ball routine */
677 /* returns 1 if nothing was detected */
678 /* returns 0 if something was detected */
679 int
trap_detect(sobj)680 trap_detect(sobj)
681 register struct obj *sobj;
682 /* sobj is null if crystal ball, *scroll if gold detection scroll */
683 {
684 register struct trap *ttmp;
685 register struct obj *obj;
686 register int door;
687 int uw = u.uinwater;
688 boolean found = FALSE;
689 coord cc;
690
691 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
692 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
693 goto outtrapmap;
694 else found = TRUE;
695 }
696 for (obj = fobj; obj; obj = obj->nobj) {
697 if ((obj->otyp == LARGE_BOX || obj->otyp == CHEST || obj->otyp == IRON_SAFE) &&
698 obj->otrapped) {
699 if (obj->ox != u.ux || obj->oy != u.uy)
700 goto outtrapmap;
701 else found = TRUE;
702 }
703 }
704 for (door = 0; door < doorindex; door++) {
705 cc = doors[door];
706 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
707 if (cc.x != u.ux || cc.y != u.uy)
708 goto outtrapmap;
709 else found = TRUE;
710 }
711 }
712 if (!found) {
713 char buf[42];
714 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
715 strange_feeling(sobj,buf);
716 return(1);
717 }
718 /* traps exist, but only under me - no separate display required */
719 Your("%s itch.", makeplural(body_part(TOE)));
720 return(0);
721 outtrapmap:
722 cls();
723
724 u.uinwater = 0;
725 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
726 sense_trap(ttmp, 0, 0, sobj && sobj->cursed);
727
728 for (obj = fobj; obj; obj = obj->nobj)
729 if ((obj->otyp == LARGE_BOX || obj->otyp == CHEST || obj->otyp == IRON_SAFE) &&
730 obj->otrapped)
731 sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed);
732
733 for (door = 0; door < doorindex; door++) {
734 cc = doors[door];
735 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
736 sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed);
737 }
738
739 newsym(u.ux,u.uy);
740 You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped");
741 display_nhwindow(WIN_MAP, TRUE);
742 docrt();
743 u.uinwater = uw;
744 if (Underwater) under_water(2);
745 if (u.uburied) under_ground(2);
746 return(0);
747 }
748
749 const char *
level_distance(where)750 level_distance(where)
751 d_level *where;
752 {
753 register schar ll = depth(&u.uz) - depth(where);
754 register boolean indun = (u.uz.dnum == where->dnum);
755
756 if (ll < 0) {
757 if (ll < (-8 - rn2(3)))
758 if (!indun) return "far away";
759 else return "far below";
760 else if (ll < -1)
761 if (!indun) return "away below you";
762 else return "below you";
763 else
764 if (!indun) return "in the distance";
765 else return "just below";
766 } else if (ll > 0) {
767 if (ll > (8 + rn2(3)))
768 if (!indun) return "far away";
769 else return "far above";
770 else if (ll > 1)
771 if (!indun) return "away above you";
772 else return "above you";
773 else
774 if (!indun) return "in the distance";
775 else return "just above";
776 } else
777 if (!indun) return "in the distance";
778 else return "near you";
779 }
780
781 static const struct {
782 const char *what;
783 d_level *where;
784 } level_detects[] = {
785 { "Delphi", &oracle_level },
786 { "Medusa's lair", &medusa_level },
787 { "a castle", &stronghold_level },
788 { "the Wizard of Yendor's tower", &wiz1_level },
789 };
790
791 void
use_crystal_ball(obj)792 use_crystal_ball(obj)
793 struct obj *obj;
794 {
795 char ch;
796 int oops;
797
798 if (Blind) {
799 pline("Too bad you can't see %s.", the(xname(obj)));
800 return;
801 }
802 oops = (rnd(obj->blessed ? 13 : 20) > ACURR(A_INT) || obj->cursed);
803 if (oops && (obj->spe > 0)) {
804 switch (rnd(obj->oartifact ? 4 : 5)) {
805 case 1 : pline("%s too much to comprehend!", Tobjnam(obj, "are"));
806 break;
807 case 2 : pline("%s you!", Tobjnam(obj, "confuse"));
808 make_confused(HConfusion + rnd(100),FALSE);
809 break;
810 case 3 : if (!resists_blnd(&youmonst)) {
811 pline("%s your vision!", Tobjnam(obj, "damage"));
812 make_blinded(Blinded + rnd(100),FALSE);
813 if (!Blind) Your(vision_clears);
814 } else {
815 pline("%s your vision.", Tobjnam(obj, "assault"));
816 You("are unaffected!");
817 }
818 break;
819 case 4 : pline("%s your mind!", Tobjnam(obj, "zap"));
820 (void) make_hallucinated(HHallucination + rnd(100),FALSE,0L);
821 break;
822 case 5 : pline("%s!", Tobjnam(obj, "explode"));
823 useup(obj);
824 obj = 0; /* it's gone */
825 losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN);
826 break;
827 }
828 if (obj) consume_obj_charge(obj, TRUE);
829 return;
830 }
831
832 if (Hallucination) {
833 if (!obj->spe) {
834 pline("All you see is funky %s haze.", hcolor((char *)0));
835 } else {
836 switch(rnd(6)) {
837 case 1 : You("grok some groovy globs of incandescent lava.");
838 break;
839 case 2 : pline("Whoa! Psychedelic colors, %s!",
840 poly_gender() == 1 ? "babe" : "dude");
841 break;
842 case 3 : pline_The("crystal pulses with sinister %s light!",
843 hcolor((char *)0));
844 break;
845 case 4 : You("see goldfish swimming above fluorescent rocks.");
846 break;
847 case 5 : You("see tiny snowflakes spinning around a miniature farmhouse.");
848 break;
849 default: pline("Oh wow... like a kaleidoscope!");
850 break;
851 }
852 consume_obj_charge(obj, TRUE);
853 }
854 return;
855 }
856
857 /* read a single character */
858 if (flags.verbose) You("may look for an object or monster symbol.");
859 ch = yn_function("What do you look for?", (char *)0, '\0');
860 /* Don't filter out ' ' here; it has a use */
861 if ((ch != def_monsyms[S_GHOST]) && index(quitchars,ch)) {
862 if (flags.verbose) pline(Never_mind);
863 return;
864 }
865 You("peer into %s...", the(xname(obj)));
866 nomul(-rnd(10), "gazing into a crystal ball");
867 nomovemsg = "";
868 if (obj->spe <= 0)
869 pline_The("vision is unclear.");
870 else {
871 int class;
872 int ret = 0;
873
874 makeknown(CRYSTAL_BALL);
875 consume_obj_charge(obj, TRUE);
876
877 /* special case: accept ']' as synonym for mimic
878 * we have to do this before the def_char_to_objclass check
879 */
880 if (ch == DEF_MIMIC_DEF) ch = DEF_MIMIC;
881
882 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
883 ret = object_detect((struct obj *)0, class);
884 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
885 ret = monster_detect((struct obj *)0, class);
886 else if (iflags.bouldersym && (ch == iflags.bouldersym))
887 ret = object_detect((struct obj *)0, ROCK_CLASS);
888 else switch(ch) {
889 case '^':
890 ret = trap_detect((struct obj *)0);
891 break;
892 default:
893 {
894 int i = rn2(SIZE(level_detects));
895 You("see %s, %s.",
896 level_detects[i].what,
897 level_distance(level_detects[i].where));
898 }
899 ret = 0;
900 break;
901 }
902
903 if (ret) {
904 if (!rn2(100)) /* make them nervous */
905 You("see the Wizard of Yendor gazing out at you.");
906 else pline_The("vision is unclear.");
907 }
908 }
909 return;
910 }
911
912 STATIC_OVL void
show_map_spot(x,y)913 show_map_spot(x, y)
914 register int x, y;
915 {
916 register struct rm *lev;
917
918 if (Confusion && rn2(7)) return;
919 lev = &levl[x][y];
920
921 lev->seenv = SVALL;
922
923 /* Secret corridors are found, but not secret doors. */
924 if (lev->typ == SCORR) {
925 lev->typ = CORR;
926 unblock_point(x,y);
927 }
928
929 /* if we don't remember an object or trap there, map it */
930 if (lev->typ == ROOM ?
931 (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) &&
932 glyph_to_cmap(lev->glyph) != ROOM) :
933 (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) {
934 if (level.flags.hero_memory) {
935 magic_map_background(x,y,0);
936 newsym(x,y); /* show it, if not blocked */
937 } else {
938 magic_map_background(x,y,1); /* display it */
939 }
940 }
941 }
942
943 void
do_mapping()944 do_mapping()
945 {
946 register int zx, zy;
947 int uw = u.uinwater;
948
949 u.uinwater = 0;
950 for (zx = 1; zx < COLNO; zx++)
951 for (zy = 0; zy < ROWNO; zy++)
952 show_map_spot(zx, zy);
953 exercise(A_WIS, TRUE);
954 u.uinwater = uw;
955 if (!level.flags.hero_memory || Underwater) {
956 flush_screen(1); /* flush temp screen */
957 display_nhwindow(WIN_MAP, TRUE); /* wait */
958 docrt();
959 }
960 }
961
962 void
do_vicinity_map()963 do_vicinity_map()
964 {
965 register int zx, zy;
966 int lo_y = (u.uy-5 < 0 ? 0 : u.uy-5),
967 hi_y = (u.uy+6 > ROWNO ? ROWNO : u.uy+6),
968 lo_x = (u.ux-9 < 1 ? 1 : u.ux-9), /* avoid column 0 */
969 hi_x = (u.ux+10 > COLNO ? COLNO : u.ux+10);
970
971 for (zx = lo_x; zx < hi_x; zx++)
972 for (zy = lo_y; zy < hi_y; zy++)
973 show_map_spot(zx, zy);
974
975 if (!level.flags.hero_memory || Underwater) {
976 flush_screen(1); /* flush temp screen */
977 display_nhwindow(WIN_MAP, TRUE); /* wait */
978 docrt();
979 }
980 }
981
982 /* convert a secret door into a normal door */
983 void
cvt_sdoor_to_door(lev)984 cvt_sdoor_to_door(lev)
985 struct rm *lev;
986 {
987 int newmask = lev->doormask & ~WM_MASK;
988
989 #ifdef REINCARNATION
990 if (Is_rogue_level(&u.uz))
991 /* rogue didn't have doors, only doorways */
992 newmask = D_NODOOR;
993 else
994 #endif
995 /* newly exposed door is closed */
996 if (!(newmask & D_LOCKED)) newmask |= D_CLOSED;
997
998 lev->typ = DOOR;
999 lev->doormask = newmask;
1000 }
1001
1002
1003 STATIC_PTR void
findone(zx,zy,num)1004 findone(zx,zy,num)
1005 int zx,zy;
1006 genericptr_t num;
1007 {
1008 register struct trap *ttmp;
1009 register struct monst *mtmp;
1010
1011 if(levl[zx][zy].typ == SDOOR) {
1012 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1013 magic_map_background(zx, zy, 0);
1014 newsym(zx, zy);
1015 (*(int*)num)++;
1016 } else if(levl[zx][zy].typ == SCORR) {
1017 levl[zx][zy].typ = CORR;
1018 unblock_point(zx,zy);
1019 magic_map_background(zx, zy, 0);
1020 newsym(zx, zy);
1021 (*(int*)num)++;
1022 } else if ((ttmp = t_at(zx, zy)) != 0) {
1023 if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1024 ttmp->tseen = 1;
1025 newsym(zx,zy);
1026 (*(int*)num)++;
1027 }
1028 } else if ((mtmp = m_at(zx, zy)) != 0) {
1029 if(mtmp->m_ap_type) {
1030 seemimic(mtmp);
1031 (*(int*)num)++;
1032 }
1033 if (mtmp->mundetected &&
1034 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1035 mtmp->mundetected = 0;
1036 newsym(zx, zy);
1037 (*(int*)num)++;
1038 }
1039 if (!canspotmon(mtmp) &&
1040 !glyph_is_invisible(levl[zx][zy].glyph))
1041 map_invisible(zx, zy);
1042 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1043 unmap_object(zx, zy);
1044 newsym(zx, zy);
1045 (*(int*)num)++;
1046 }
1047 }
1048
1049 STATIC_PTR void
openone(zx,zy,num)1050 openone(zx,zy,num)
1051 int zx,zy;
1052 genericptr_t num;
1053 {
1054 register struct trap *ttmp;
1055 register struct obj *otmp;
1056
1057 if(OBJ_AT(zx, zy)) {
1058 for(otmp = level.objects[zx][zy];
1059 otmp; otmp = otmp->nexthere) {
1060 if(Is_box(otmp) && otmp->olocked) {
1061 otmp->olocked = 0;
1062 (*(int*)num)++;
1063 }
1064 }
1065 /* let it fall to the next cases. could be on trap. */
1066 }
1067 if(levl[zx][zy].typ == SDOOR || (levl[zx][zy].typ == DOOR &&
1068 (levl[zx][zy].doormask & (D_CLOSED|D_LOCKED)))) {
1069 if(levl[zx][zy].typ == SDOOR)
1070 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1071 if(levl[zx][zy].doormask & D_TRAPPED) {
1072 if(distu(zx, zy) < 3) b_trapped("door", 0);
1073 else Norep("You %s an explosion!",
1074 cansee(zx, zy) ? "see" :
1075 (flags.soundok ? "hear" :
1076 "feel the shock of"));
1077 wake_nearto(zx, zy, 11*11);
1078 levl[zx][zy].doormask = D_NODOOR;
1079 } else
1080 levl[zx][zy].doormask = D_ISOPEN;
1081 unblock_point(zx, zy);
1082 newsym(zx, zy);
1083 (*(int*)num)++;
1084 } else if(levl[zx][zy].typ == SCORR) {
1085 levl[zx][zy].typ = CORR;
1086 unblock_point(zx, zy);
1087 newsym(zx, zy);
1088 (*(int*)num)++;
1089 } else if ((ttmp = t_at(zx, zy)) != 0) {
1090 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1091 ttmp->tseen = 1;
1092 newsym(zx,zy);
1093 (*(int*)num)++;
1094 }
1095 } else if (find_drawbridge(&zx, &zy)) {
1096 /* make sure it isn't an open drawbridge */
1097 open_drawbridge(zx, zy);
1098 (*(int*)num)++;
1099 }
1100 }
1101
1102 int
findit()1103 findit() /* returns number of things found */
1104 {
1105 int num = 0;
1106
1107 if(u.uswallow) return(0);
1108 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1109 return(num);
1110 }
1111
1112 int
openit()1113 openit() /* returns number of things found and opened */
1114 {
1115 int num = 0;
1116
1117 if(u.uswallow) {
1118 if (is_animal(u.ustuck->data)) {
1119 if (Blind) pline("Its mouth opens!");
1120 else pline("%s opens its mouth!", Monnam(u.ustuck));
1121 }
1122 expels(u.ustuck, u.ustuck->data, TRUE);
1123 return(-1);
1124 }
1125
1126 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1127 return(num);
1128 }
1129
1130 void
find_trap(trap)1131 find_trap(trap)
1132 struct trap *trap;
1133 {
1134 int tt = what_trap(trap->ttyp);
1135 boolean cleared = FALSE;
1136
1137 trap->tseen = 1;
1138 exercise(A_WIS, TRUE);
1139 if (Blind)
1140 feel_location(trap->tx, trap->ty);
1141 else
1142 newsym(trap->tx, trap->ty);
1143
1144 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1145 /* There's too much clutter to see your find otherwise */
1146 cls();
1147 map_trap(trap, 1);
1148 display_self();
1149 cleared = TRUE;
1150 }
1151
1152 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1153
1154 if (cleared) {
1155 display_nhwindow(WIN_MAP, TRUE); /* wait */
1156 docrt();
1157 }
1158 }
1159
1160 int
dosearch0(aflag)1161 dosearch0(aflag)
1162 register int aflag;
1163 {
1164 #ifdef GCC_BUG
1165 /* some versions of gcc seriously muck up nested loops. if you get strange
1166 crashes while searching in a version compiled with gcc, try putting
1167 #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
1168 makefile).
1169 */
1170 volatile xchar x, y;
1171 #else
1172 register xchar x, y;
1173 #endif
1174 register struct trap *trap;
1175 register struct monst *mtmp;
1176
1177 if(u.uswallow) {
1178 if (!aflag)
1179 pline("What are you looking for? The exit?");
1180 } else {
1181 int fund = (uwep && uwep->oartifact &&
1182 spec_ability(uwep, SPFX_SEARCH)) ?
1183 uwep->spe : 0;
1184 if (ublindf && ublindf->otyp == LENSES && !Blind)
1185 fund += 2; /* JDS: lenses help searching */
1186 if (fund > 5) fund = 5;
1187 for(x = u.ux-1; x < u.ux+2; x++)
1188 for(y = u.uy-1; y < u.uy+2; y++) {
1189 if(!isok(x,y)) continue;
1190 if(x != u.ux || y != u.uy) {
1191 if (Blind && !aflag) feel_location(x,y);
1192 if(levl[x][y].typ == SDOOR) {
1193 if(rnl(7-fund)) continue;
1194 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1195 exercise(A_WIS, TRUE);
1196 nomul(0, 0);
1197 if (Blind && !aflag)
1198 feel_location(x,y); /* make sure it shows up */
1199 else
1200 newsym(x,y);
1201 } else if(levl[x][y].typ == SCORR) {
1202 if(rnl(7-fund)) continue;
1203 levl[x][y].typ = CORR;
1204 unblock_point(x,y); /* vision */
1205 exercise(A_WIS, TRUE);
1206 nomul(0, 0);
1207 newsym(x,y);
1208 } else {
1209 /* Be careful not to find anything in an SCORR or SDOOR */
1210 if((mtmp = m_at(x, y)) && !aflag) {
1211 if(mtmp->m_ap_type) {
1212 seemimic(mtmp);
1213 find: exercise(A_WIS, TRUE);
1214 if (!canspotmon(mtmp)) {
1215 if (glyph_is_invisible(levl[x][y].glyph)) {
1216 /* found invisible monster in a square
1217 * which already has an 'I' in it.
1218 * Logically, this should still take
1219 * time and lead to a return(1), but if
1220 * we did that the player would keep
1221 * finding the same monster every turn.
1222 */
1223 continue;
1224 } else {
1225 You_feel("an unseen monster!");
1226 map_invisible(x, y);
1227 }
1228 } else if (!sensemon(mtmp))
1229 You("find %s.", a_monnam(mtmp));
1230 return(1);
1231 }
1232 if(!canspotmon(mtmp)) {
1233 if (mtmp->mundetected &&
1234 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1235 mtmp->mundetected = 0;
1236 newsym(x,y);
1237 goto find;
1238 }
1239 }
1240
1241 /* see if an invisible monster has moved--if Blind,
1242 * feel_location() already did it
1243 */
1244 if (!aflag && !mtmp && !Blind &&
1245 glyph_is_invisible(levl[x][y].glyph)) {
1246 unmap_object(x,y);
1247 newsym(x,y);
1248 }
1249
1250 if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) {
1251 nomul(0, 0);
1252
1253 if (trap->ttyp == STATUE_TRAP) {
1254 if (activate_statue_trap(trap, x, y, FALSE))
1255 exercise(A_WIS, TRUE);
1256 return(1);
1257 } else {
1258 find_trap(trap);
1259 }
1260 }
1261 }
1262 }
1263 }
1264 }
1265 return(1);
1266 }
1267
1268 int
dosearch()1269 dosearch()
1270 {
1271 return(dosearch0(0));
1272 }
1273
1274 /* Pre-map the sokoban levels */
1275 void
sokoban_detect()1276 sokoban_detect()
1277 {
1278 register int x, y;
1279 register struct trap *ttmp;
1280 register struct obj *obj;
1281
1282 /* Map the background and boulders */
1283 for (x = 1; x < COLNO; x++)
1284 for (y = 0; y < ROWNO; y++) {
1285 if (IS_WALL(levl[x][y].typ))
1286 levl[x][y].seenv = SVALL;
1287 else if (levl[x][y].typ == SDOOR)
1288 levl[x][y].typ = DOOR;
1289 else if (levl[x][y].typ == SCORR)
1290 levl[x][y].typ = CORR;
1291
1292 /* all Sokoban floors only shown lit when dark_room is deactivated */
1293 levl[x][y].waslit = (levl[x][y].typ != CORR) ? (!iflags.dark_room) : levl[x][y].lit;
1294 map_background(x, y, 1);
1295 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
1296 if (obj->otyp == BOULDER)
1297 map_object(obj, 1);
1298 }
1299
1300 /* Map the traps */
1301 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1302 ttmp->tseen = 1;
1303 map_trap(ttmp, 1);
1304 }
1305 }
1306
1307
1308 /*detect.c*/
1309