fixes entry
[aNetHack.git] / src / detect.c
blob7668d77bb82eae5600e071f09343dcdb057f967b
1 /* NetHack 3.6 detect.c $NHDT-Date: 1463191981 2016/05/14 02:13:01 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.70 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /*
6 * Detection routines, including crystal ball, magic mapping, and search
7 * command.
8 */
10 #include "hack.h"
11 #include "artifact.h"
13 extern boolean known; /* from read.c */
15 STATIC_DCL boolean NDECL(unconstrain_map);
16 STATIC_DCL void NDECL(reconstrain_map);
17 STATIC_DCL void FDECL(browse_map, (int, const char *));
18 STATIC_DCL void FDECL(map_monst, (struct monst *, BOOLEAN_P));
19 STATIC_DCL void FDECL(do_dknown_of, (struct obj *));
20 STATIC_DCL boolean FDECL(check_map_spot, (int, int, CHAR_P, unsigned));
21 STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P, unsigned));
22 STATIC_DCL void FDECL(sense_trap, (struct trap *, XCHAR_P, XCHAR_P, int));
23 STATIC_DCL int FDECL(detect_obj_traps, (struct obj *, BOOLEAN_P, int));
24 STATIC_DCL void FDECL(show_map_spot, (int, int));
25 STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
26 STATIC_PTR void FDECL(openone, (int, int, genericptr_t));
27 STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P));
29 /* bring hero out from underwater or underground or being engulfed;
30 return True iff any change occurred */
31 STATIC_OVL boolean
32 unconstrain_map()
34 boolean res = u.uinwater || u.uburied || u.uswallow;
36 /* bring Underwater, buried, or swallowed hero to normal map */
37 iflags.save_uinwater = u.uinwater, u.uinwater = 0;
38 iflags.save_uburied = u.uburied, u.uburied = 0;
39 iflags.save_uswallow = u.uswallow, u.uswallow = 0;
41 return res;
44 /* put hero back underwater or underground or engulfed */
45 STATIC_OVL void
46 reconstrain_map()
48 u.uinwater = iflags.save_uinwater, iflags.save_uinwater = 0;
49 u.uburied = iflags.save_uburied, iflags.save_uburied = 0;
50 u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0;
53 /* use getpos()'s 'autodescribe' to view whatever is currently shown on map */
54 STATIC_DCL void
55 browse_map(ter_typ, ter_explain)
56 int ter_typ;
57 const char *ter_explain;
59 coord dummy_pos; /* don't care whether player actually picks a spot */
61 dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */
62 iflags.autodescribe = TRUE;
63 iflags.terrainmode = ter_typ;
64 getpos(&dummy_pos, FALSE, ter_explain);
65 iflags.terrainmode = 0;
66 /* leave iflags.autodescribe 'on' even if previously 'off' */
69 /* extracted from monster_detection() so can be shared by do_vicinity_map() */
70 STATIC_DCL void
71 map_monst(mtmp, showtail)
72 struct monst *mtmp;
73 boolean showtail;
75 if (def_monsyms[(int) mtmp->data->mlet].sym == ' ')
76 show_glyph(mtmp->mx, mtmp->my, detected_mon_to_glyph(mtmp));
77 else
78 show_glyph(mtmp->mx, mtmp->my,
79 mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
81 if (showtail && mtmp->data == &mons[PM_LONG_WORM])
82 detect_wsegs(mtmp, 0);
85 /* this is checking whether a trap symbol represents a trapped chest,
86 not whether a trapped chest is actually present */
87 boolean
88 trapped_chest_at(ttyp, x, y)
89 int ttyp;
90 int x, y;
92 struct monst *mtmp;
93 struct obj *otmp;
95 if (!glyph_is_trap(glyph_at(x, y)))
96 return FALSE;
97 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
98 return FALSE;
101 * TODO? We should check containers recursively like the trap
102 * detecting routine does. Chests and large boxes do not nest in
103 * themselves or each other, but could be contained inside statues.
105 * For farlook, we should also check for buried containers, but
106 * for '^' command to examine adjacent trap glyph, we shouldn't.
109 /* on map, presence of any trappable container will do */
110 if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y))
111 return TRUE;
112 /* in inventory, we need to find one which is actually trapped */
113 if (x == u.ux && y == u.uy) {
114 for (otmp = invent; otmp; otmp = otmp->nobj)
115 if (Is_box(otmp) && otmp->otrapped)
116 return TRUE;
117 if (u.usteed) { /* steed isn't on map so won't be found by m_at() */
118 for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj)
119 if (Is_box(otmp) && otmp->otrapped)
120 return TRUE;
123 if ((mtmp = m_at(x, y)) != 0)
124 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
125 if (Is_box(otmp) && otmp->otrapped)
126 return TRUE;
127 return FALSE;
130 /* this is checking whether a trap symbol represents a trapped door,
131 not whether the door here is actually trapped */
132 boolean
133 trapped_door_at(ttyp, x, y)
134 int ttyp;
135 int x, y;
137 struct rm *lev;
139 if (!glyph_is_trap(glyph_at(x, y)))
140 return FALSE;
141 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
142 return FALSE;
143 lev = &levl[x][y];
144 if (!IS_DOOR(lev->typ))
145 return FALSE;
146 if ((lev->doormask & (D_NODOOR | D_BROKEN | D_ISOPEN)) != 0
147 && trapped_chest_at(ttyp, x, y))
148 return FALSE;
149 return TRUE;
152 /* recursively search obj for an object in class oclass, return 1st found */
153 struct obj *
154 o_in(obj, oclass)
155 struct obj *obj;
156 char oclass;
158 register struct obj *otmp;
159 struct obj *temp;
161 if (obj->oclass == oclass)
162 return obj;
164 if (Has_contents(obj)) {
165 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
166 if (otmp->oclass == oclass)
167 return otmp;
168 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)) != 0)
169 return temp;
171 return (struct obj *) 0;
174 /* Recursively search obj for an object made of specified material.
175 * Return first found.
177 struct obj *
178 o_material(obj, material)
179 struct obj *obj;
180 unsigned material;
182 register struct obj *otmp;
183 struct obj *temp;
185 if (objects[obj->otyp].oc_material == material)
186 return obj;
188 if (Has_contents(obj)) {
189 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
190 if (objects[otmp->otyp].oc_material == material)
191 return otmp;
192 else if (Has_contents(otmp)
193 && (temp = o_material(otmp, material)) != 0)
194 return temp;
196 return (struct obj *) 0;
199 STATIC_OVL void
200 do_dknown_of(obj)
201 struct obj *obj;
203 struct obj *otmp;
205 obj->dknown = 1;
206 if (Has_contents(obj)) {
207 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
208 do_dknown_of(otmp);
212 /* Check whether the location has an outdated object displayed on it. */
213 STATIC_OVL boolean
214 check_map_spot(x, y, oclass, material)
215 int x, y;
216 char oclass;
217 unsigned material;
219 int glyph;
220 register struct obj *otmp;
221 register struct monst *mtmp;
223 glyph = glyph_at(x, y);
224 if (glyph_is_object(glyph)) {
225 /* there's some object shown here */
226 if (oclass == ALL_CLASSES) {
227 return (boolean) !(level.objects[x][y] /* stale if nothing here */
228 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
229 } else {
230 if (material
231 && objects[glyph_to_obj(glyph)].oc_material == material) {
232 /* object shown here is of interest because material matches */
233 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
234 if (o_material(otmp, GOLD))
235 return FALSE;
236 /* didn't find it; perhaps a monster is carrying it */
237 if ((mtmp = m_at(x, y)) != 0) {
238 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
239 if (o_material(otmp, GOLD))
240 return FALSE;
242 /* detection indicates removal of this object from the map */
243 return TRUE;
245 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
246 /* obj shown here is of interest because its class matches */
247 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
248 if (o_in(otmp, oclass))
249 return FALSE;
250 /* didn't find it; perhaps a monster is carrying it */
251 if ((mtmp = m_at(x, y)) != 0) {
252 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
253 if (o_in(otmp, oclass))
254 return FALSE;
256 /* detection indicates removal of this object from the map */
257 return TRUE;
261 return FALSE;
265 * When doing detection, remove stale data from the map display (corpses
266 * rotted away, objects carried away by monsters, etc) so that it won't
267 * reappear after the detection has completed. Return true if noticeable
268 * change occurs.
270 STATIC_OVL boolean
271 clear_stale_map(oclass, material)
272 char oclass;
273 unsigned material;
275 register int zx, zy;
276 boolean change_made = FALSE;
278 for (zx = 1; zx < COLNO; zx++)
279 for (zy = 0; zy < ROWNO; zy++)
280 if (check_map_spot(zx, zy, oclass, material)) {
281 unmap_object(zx, zy);
282 change_made = TRUE;
285 return change_made;
288 /* look for gold, on the floor or in monsters' possession */
290 gold_detect(sobj)
291 register struct obj *sobj;
293 register struct obj *obj;
294 register struct monst *mtmp;
295 struct obj gold, *temp = 0;
296 boolean stale, ugold = FALSE, steedgold = FALSE;
297 int ter_typ = TER_DETECT | TER_OBJ;
299 known = stale = clear_stale_map(COIN_CLASS,
300 (unsigned) (sobj->blessed ? GOLD : 0));
302 /* look for gold carried by monsters (might be in a container) */
303 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
304 if (DEADMONSTER(mtmp))
305 continue; /* probably not needed in this case but... */
306 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
307 if (mtmp == u.usteed) {
308 steedgold = TRUE;
309 } else {
310 known = TRUE;
311 goto outgoldmap; /* skip further searching */
313 } else {
314 for (obj = mtmp->minvent; obj; obj = obj->nobj)
315 if ((sobj->blessed && o_material(obj, GOLD))
316 || o_in(obj, COIN_CLASS)) {
317 if (mtmp == u.usteed) {
318 steedgold = TRUE;
319 } else {
320 known = TRUE;
321 goto outgoldmap; /* skip further searching */
327 /* look for gold objects */
328 for (obj = fobj; obj; obj = obj->nobj) {
329 if (sobj->blessed && o_material(obj, GOLD)) {
330 known = TRUE;
331 if (obj->ox != u.ux || obj->oy != u.uy)
332 goto outgoldmap;
333 } else if (o_in(obj, COIN_CLASS)) {
334 known = TRUE;
335 if (obj->ox != u.ux || obj->oy != u.uy)
336 goto outgoldmap;
340 if (!known) {
341 /* no gold found on floor or monster's inventory.
342 adjust message if you have gold in your inventory */
343 if (sobj) {
344 char buf[BUFSZ];
346 if (youmonst.data == &mons[PM_GOLD_GOLEM])
347 Sprintf(buf, "You feel like a million %s!", currency(2L));
348 else if (money_cnt(invent) || hidden_gold())
349 Strcpy(buf,
350 "You feel worried about your future financial situation.");
351 else if (steedgold)
352 Sprintf(buf, "You feel interested in %s financial situation.",
353 s_suffix(x_monnam(u.usteed,
354 u.usteed->mtame ? ARTICLE_YOUR
355 : ARTICLE_THE,
356 (char *) 0,
357 SUPPRESS_SADDLE, FALSE)));
358 else
359 Strcpy(buf, "You feel materially poor.");
361 strange_feeling(sobj, buf);
363 return 1;
365 /* only under me - no separate display required */
366 if (stale)
367 docrt();
368 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
369 return 0;
371 outgoldmap:
372 cls();
374 (void) unconstrain_map();
375 /* Discover gold locations. */
376 for (obj = fobj; obj; obj = obj->nobj) {
377 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
378 if (temp != obj) {
379 temp->ox = obj->ox;
380 temp->oy = obj->oy;
382 map_object(temp, 1);
383 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
384 if (temp != obj) {
385 temp->ox = obj->ox;
386 temp->oy = obj->oy;
388 map_object(temp, 1);
390 if (temp && temp->ox == u.ux && temp->oy == u.uy)
391 ugold = TRUE;
393 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
394 if (DEADMONSTER(mtmp))
395 continue; /* probably overkill here */
396 temp = 0;
397 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
398 gold = zeroobj; /* ensure oextra is cleared too */
399 gold.otyp = GOLD_PIECE;
400 gold.quan = (long) rnd(10); /* usually more than 1 */
401 gold.ox = mtmp->mx;
402 gold.oy = mtmp->my;
403 map_object(&gold, 1);
404 temp = &gold;
405 } else {
406 for (obj = mtmp->minvent; obj; obj = obj->nobj)
407 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
408 temp->ox = mtmp->mx;
409 temp->oy = mtmp->my;
410 map_object(temp, 1);
411 break;
412 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
413 temp->ox = mtmp->mx;
414 temp->oy = mtmp->my;
415 map_object(temp, 1);
416 break;
419 if (temp && temp->ox == u.ux && temp->oy == u.uy)
420 ugold = TRUE;
422 if (!ugold) {
423 newsym(u.ux, u.uy);
424 ter_typ |= TER_MON; /* so autodescribe will recognize hero */
426 You_feel("very greedy, and sense gold!");
427 exercise(A_WIS, TRUE);
429 browse_map(ter_typ, "gold");
431 reconstrain_map();
432 docrt();
433 if (Underwater)
434 under_water(2);
435 if (u.uburied)
436 under_ground(2);
437 return 0;
440 /* returns 1 if nothing was detected */
441 /* returns 0 if something was detected */
443 food_detect(sobj)
444 register struct obj *sobj;
446 register struct obj *obj;
447 register struct monst *mtmp;
448 register int ct = 0, ctu = 0;
449 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
450 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
451 const char *what = confused ? something : "food";
453 stale = clear_stale_map(oclass, 0);
454 if (u.usteed) /* some situations leave steed with stale coordinates */
455 u.usteed->mx = u.ux, u.usteed->my = u.uy;
457 for (obj = fobj; obj; obj = obj->nobj)
458 if (o_in(obj, oclass)) {
459 if (obj->ox == u.ux && obj->oy == u.uy)
460 ctu++;
461 else
462 ct++;
464 for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) {
465 /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */
466 for (obj = mtmp->minvent; obj; obj = obj->nobj)
467 if (o_in(obj, oclass)) {
468 if (mtmp->mx == u.ux && mtmp->my == u.uy)
469 ctu++; /* steed or an engulfer with inventory */
470 else
471 ct++;
472 break;
476 if (!ct && !ctu) {
477 known = stale && !confused;
478 if (stale) {
479 docrt();
480 You("sense a lack of %s nearby.", what);
481 if (sobj && sobj->blessed) {
482 if (!u.uedibility)
483 Your("%s starts to tingle.", body_part(NOSE));
484 u.uedibility = 1;
486 } else if (sobj) {
487 char buf[BUFSZ];
489 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
490 (sobj->blessed && !u.uedibility)
491 ? " then starts to tingle"
492 : "");
493 if (sobj->blessed && !u.uedibility) {
494 boolean savebeginner = flags.beginner;
496 flags.beginner = FALSE; /* prevent non-delivery of message */
497 strange_feeling(sobj, buf);
498 flags.beginner = savebeginner;
499 u.uedibility = 1;
500 } else
501 strange_feeling(sobj, buf);
503 return !stale;
504 } else if (!ct) {
505 known = TRUE;
506 You("%s %s nearby.", sobj ? "smell" : "sense", what);
507 if (sobj && sobj->blessed) {
508 if (!u.uedibility)
509 pline("Your %s starts to tingle.", body_part(NOSE));
510 u.uedibility = 1;
512 } else {
513 struct obj *temp;
514 int ter_typ = TER_DETECT | TER_OBJ;
516 known = TRUE;
517 cls();
518 (void) unconstrain_map();
519 for (obj = fobj; obj; obj = obj->nobj)
520 if ((temp = o_in(obj, oclass)) != 0) {
521 if (temp != obj) {
522 temp->ox = obj->ox;
523 temp->oy = obj->oy;
525 map_object(temp, 1);
527 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
528 /* no DEADMONSTER() check needed -- dmons never have inventory */
529 for (obj = mtmp->minvent; obj; obj = obj->nobj)
530 if ((temp = o_in(obj, oclass)) != 0) {
531 temp->ox = mtmp->mx;
532 temp->oy = mtmp->my;
533 map_object(temp, 1);
534 break; /* skip rest of this monster's inventory */
536 if (!ctu) {
537 newsym(u.ux, u.uy);
538 ter_typ |= TER_MON; /* for autodescribe of self */
540 if (sobj) {
541 if (sobj->blessed) {
542 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
543 u.uedibility ? "continues" : "starts", what);
544 u.uedibility = 1;
545 } else
546 Your("%s tingles and you smell %s.", body_part(NOSE), what);
547 } else
548 You("sense %s.", what);
549 exercise(A_WIS, TRUE);
551 browse_map(ter_typ, "food");
553 reconstrain_map();
554 docrt();
555 if (Underwater)
556 under_water(2);
557 if (u.uburied)
558 under_ground(2);
560 return 0;
564 * Used for scrolls, potions, spells, and crystal balls. Returns:
566 * 1 - nothing was detected
567 * 0 - something was detected
570 object_detect(detector, class)
571 struct obj *detector; /* object doing the detecting */
572 int class; /* an object class, 0 for all */
574 register int x, y;
575 char stuff[BUFSZ];
576 int is_cursed = (detector && detector->cursed);
577 int do_dknown = (detector && (detector->oclass == POTION_CLASS
578 || detector->oclass == SPBOOK_CLASS)
579 && detector->blessed);
580 int ct = 0, ctu = 0;
581 register struct obj *obj, *otmp = (struct obj *) 0;
582 register struct monst *mtmp;
583 int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ;
585 if (class < 0 || class >= MAXOCLASSES) {
586 impossible("object_detect: illegal class %d", class);
587 class = 0;
590 /* Special boulder symbol check - does the class symbol happen
591 * to match iflags.bouldersym which is a user-defined?
592 * If so, that means we aren't sure what they really wanted to
593 * detect. Rather than trump anything, show both possibilities.
594 * We can exclude checking the buried obj chain for boulders below.
596 sym = class ? def_oc_syms[class].sym : 0;
597 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
598 boulder = ROCK_CLASS;
600 if (Hallucination || (Confusion && class == SCROLL_CLASS))
601 Strcpy(stuff, something);
602 else
603 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
604 if (boulder && class != ROCK_CLASS)
605 Strcat(stuff, " and/or large stones");
607 if (do_dknown)
608 for (obj = invent; obj; obj = obj->nobj)
609 do_dknown_of(obj);
611 for (obj = fobj; obj; obj = obj->nobj) {
612 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
613 if (obj->ox == u.ux && obj->oy == u.uy)
614 ctu++;
615 else
616 ct++;
618 if (do_dknown)
619 do_dknown_of(obj);
622 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
623 if (!class || o_in(obj, class)) {
624 if (obj->ox == u.ux && obj->oy == u.uy)
625 ctu++;
626 else
627 ct++;
629 if (do_dknown)
630 do_dknown_of(obj);
633 if (u.usteed)
634 u.usteed->mx = u.ux, u.usteed->my = u.uy;
636 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
637 if (DEADMONSTER(mtmp))
638 continue;
639 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
640 if ((!class && !boulder) || o_in(obj, class)
641 || o_in(obj, boulder))
642 ct++;
643 if (do_dknown)
644 do_dknown_of(obj);
646 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
647 && (!class || class == objects[mtmp->mappearance].oc_class))
648 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
649 ct++;
650 break;
654 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
655 if (!ctu) {
656 if (detector)
657 strange_feeling(detector, "You feel a lack of something.");
658 return 1;
661 You("sense %s nearby.", stuff);
662 return 0;
665 cls();
667 (void) unconstrain_map();
669 * Map all buried objects first.
671 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
672 if (!class || (otmp = o_in(obj, class)) != 0) {
673 if (class) {
674 if (otmp != obj) {
675 otmp->ox = obj->ox;
676 otmp->oy = obj->oy;
678 map_object(otmp, 1);
679 } else
680 map_object(obj, 1);
683 * If we are mapping all objects, map only the top object of a pile or
684 * the first object in a monster's inventory. Otherwise, go looking
685 * for a matching object class and display the first one encountered
686 * at each location.
688 * Objects on the floor override buried objects.
690 for (x = 1; x < COLNO; x++)
691 for (y = 0; y < ROWNO; y++)
692 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
693 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
694 || (otmp = o_in(obj, boulder)) != 0) {
695 if (class || boulder) {
696 if (otmp != obj) {
697 otmp->ox = obj->ox;
698 otmp->oy = obj->oy;
700 map_object(otmp, 1);
701 } else
702 map_object(obj, 1);
703 break;
706 /* Objects in the monster's inventory override floor objects. */
707 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
708 if (DEADMONSTER(mtmp))
709 continue;
710 for (obj = mtmp->minvent; obj; obj = obj->nobj)
711 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
712 || (otmp = o_in(obj, boulder)) != 0) {
713 if (!class && !boulder)
714 otmp = obj;
715 otmp->ox = mtmp->mx; /* at monster location */
716 otmp->oy = mtmp->my;
717 map_object(otmp, 1);
718 break;
720 /* Allow a mimic to override the detected objects it is carrying. */
721 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
722 && (!class || class == objects[mtmp->mappearance].oc_class)) {
723 struct obj temp;
725 temp = zeroobj;
726 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
727 temp.quan = 1L;
728 temp.ox = mtmp->mx;
729 temp.oy = mtmp->my;
730 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
731 map_object(&temp, 1);
732 } else if (findgold(mtmp->minvent)
733 && (!class || class == COIN_CLASS)) {
734 struct obj gold;
736 gold = zeroobj; /* ensure oextra is cleared too */
737 gold.otyp = GOLD_PIECE;
738 gold.quan = (long) rnd(10); /* usually more than 1 */
739 gold.ox = mtmp->mx;
740 gold.oy = mtmp->my;
741 map_object(&gold, 1);
744 if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
745 newsym(u.ux, u.uy);
746 ter_typ |= TER_MON;
748 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
750 if (!ct)
751 display_nhwindow(WIN_MAP, TRUE);
752 else
753 browse_map(ter_typ, "object");
755 reconstrain_map();
756 docrt(); /* this will correctly reset vision */
757 if (Underwater)
758 under_water(2);
759 if (u.uburied)
760 under_ground(2);
761 return 0;
765 * Used by: crystal balls, potions, fountains
767 * Returns 1 if nothing was detected.
768 * Returns 0 if something was detected.
771 monster_detect(otmp, mclass)
772 register struct obj *otmp; /* detecting object (if any) */
773 int mclass; /* monster class, 0 for all */
775 register struct monst *mtmp;
776 int mcnt = 0;
778 /* Note: This used to just check fmon for a non-zero value
779 * but in versions since 3.3.0 fmon can test TRUE due to the
780 * presence of dmons, so we have to find at least one
781 * with positive hit-points to know for sure.
783 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
784 if (!DEADMONSTER(mtmp)) {
785 mcnt++;
786 break;
789 if (!mcnt) {
790 if (otmp)
791 strange_feeling(otmp, Hallucination
792 ? "You get the heebie jeebies."
793 : "You feel threatened.");
794 return 1;
795 } else {
796 boolean unconstrained, woken = FALSE;
797 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
799 cls();
800 unconstrained = unconstrain_map();
801 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
802 if (DEADMONSTER(mtmp))
803 continue;
804 if (!mclass || mtmp->data->mlet == mclass
805 || (mtmp->data == &mons[PM_LONG_WORM]
806 && mclass == S_WORM_TAIL))
807 map_monst(mtmp, TRUE);
809 if (otmp && otmp->cursed
810 && (mtmp->msleeping || !mtmp->mcanmove)) {
811 mtmp->msleeping = mtmp->mfrozen = 0;
812 mtmp->mcanmove = 1;
813 woken = TRUE;
816 if (!swallowed)
817 display_self();
818 You("sense the presence of monsters.");
819 if (woken)
820 pline("Monsters sense the presence of you.");
822 if ((otmp && otmp->blessed) && !unconstrained) {
823 /* persistent detection--just show updated map */
824 display_nhwindow(WIN_MAP, TRUE);
825 } else {
826 /* one-shot detection--allow player to move cursor around and
827 get autodescribe feedback */
828 EDetect_monsters |= I_SPECIAL;
829 browse_map(TER_DETECT | TER_MON, "monster of interest");
830 EDetect_monsters &= ~I_SPECIAL;
833 reconstrain_map();
834 docrt(); /* redraw the screen to remove unseen monsters from map */
835 if (Underwater)
836 under_water(2);
837 if (u.uburied)
838 under_ground(2);
840 return 0;
843 STATIC_OVL void
844 sense_trap(trap, x, y, src_cursed)
845 struct trap *trap;
846 xchar x, y;
847 int src_cursed;
849 if (Hallucination || src_cursed) {
850 struct obj obj; /* fake object */
852 obj = zeroobj;
853 if (trap) {
854 obj.ox = trap->tx;
855 obj.oy = trap->ty;
856 } else {
857 obj.ox = x;
858 obj.oy = y;
860 obj.otyp = !Hallucination ? GOLD_PIECE : random_object();
861 obj.quan = (long) ((obj.otyp == GOLD_PIECE) ? rnd(10)
862 : objects[obj.otyp].oc_merge ? rnd(2) : 1);
863 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
864 map_object(&obj, 1);
865 } else if (trap) {
866 map_trap(trap, 1);
867 trap->tseen = 1;
868 } else { /* trapped door or trapped chest */
869 struct trap temp_trap; /* fake trap */
871 (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap);
872 temp_trap.tx = x;
873 temp_trap.ty = y;
874 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
875 map_trap(&temp_trap, 1);
879 #define OTRAP_NONE 0 /* nothing found */
880 #define OTRAP_HERE 1 /* found at hero's location */
881 #define OTRAP_THERE 2 /* found at any other location */
883 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
884 2 if found at some other spot, 3 if both, 0 otherwise; optionally
885 update the map to show where such traps were found */
886 STATIC_OVL int
887 detect_obj_traps(objlist, show_them, how)
888 struct obj *objlist;
889 boolean show_them;
890 int how; /* 1 for misleading map feedback */
892 struct obj *otmp;
893 xchar x, y;
894 int result = OTRAP_NONE;
897 * TODO? Display locations of unarmed land mine and beartrap objects.
898 * If so, should they be displayed as objects or as traps?
901 for (otmp = objlist; otmp; otmp = otmp->nobj) {
902 if (Is_box(otmp) && otmp->otrapped
903 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
904 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
905 if (show_them)
906 sense_trap((struct trap *) 0, x, y, how);
908 if (Has_contents(otmp))
909 result |= detect_obj_traps(otmp->cobj, show_them, how);
911 return result;
914 /* the detections are pulled out so they can
915 * also be used in the crystal ball routine
916 * returns 1 if nothing was detected
917 * returns 0 if something was detected
920 trap_detect(sobj)
921 struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
923 register struct trap *ttmp;
924 struct monst *mon;
925 int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP;
926 int cursed_src = sobj && sobj->cursed;
927 boolean found = FALSE;
928 coord cc;
930 if (u.usteed)
931 u.usteed->mx = u.ux, u.usteed->my = u.uy;
933 /* floor/ceiling traps */
934 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
935 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
936 goto outtrapmap;
937 else
938 found = TRUE;
940 /* chest traps (might be buried or carried) */
941 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
942 if (tr & OTRAP_THERE)
943 goto outtrapmap;
944 else
945 found = TRUE;
947 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0)) != OTRAP_NONE) {
948 if (tr & OTRAP_THERE)
949 goto outtrapmap;
950 else
951 found = TRUE;
953 for (mon = fmon; mon; mon = mon->nmon) {
954 if (DEADMONSTER(mon))
955 continue;
956 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
957 if (tr & OTRAP_THERE)
958 goto outtrapmap;
959 else
960 found = TRUE;
963 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
964 found = TRUE;
965 /* door traps */
966 for (door = 0; door < doorindex; door++) {
967 cc = doors[door];
968 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
969 if (cc.x != u.ux || cc.y != u.uy)
970 goto outtrapmap;
971 else
972 found = TRUE;
975 if (!found) {
976 char buf[BUFSZ];
978 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
979 strange_feeling(sobj, buf);
980 return 1;
982 /* traps exist, but only under me - no separate display required */
983 Your("%s itch.", makeplural(body_part(TOE)));
984 return 0;
986 outtrapmap:
987 cls();
989 (void) unconstrain_map();
990 /* show chest traps first, so that subsequent floor trap display
991 will override if both types are present at the same location */
992 (void) detect_obj_traps(fobj, TRUE, cursed_src);
993 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
994 for (mon = fmon; mon; mon = mon->nmon) {
995 if (DEADMONSTER(mon))
996 continue;
997 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
999 (void) detect_obj_traps(invent, TRUE, cursed_src);
1001 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
1002 sense_trap(ttmp, 0, 0, cursed_src);
1004 for (door = 0; door < doorindex; door++) {
1005 cc = doors[door];
1006 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
1007 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
1010 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
1011 glyph = glyph_at(u.ux, u.uy);
1012 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) {
1013 newsym(u.ux, u.uy);
1014 ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
1016 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
1018 browse_map(ter_typ, "trap of interest");
1020 reconstrain_map();
1021 docrt(); /* redraw the screen to remove unseen traps from the map */
1022 if (Underwater)
1023 under_water(2);
1024 if (u.uburied)
1025 under_ground(2);
1026 return 0;
1029 const char *
1030 level_distance(where)
1031 d_level *where;
1033 register schar ll = depth(&u.uz) - depth(where);
1034 register boolean indun = (u.uz.dnum == where->dnum);
1036 if (ll < 0) {
1037 if (ll < (-8 - rn2(3)))
1038 if (!indun)
1039 return "far away";
1040 else
1041 return "far below";
1042 else if (ll < -1)
1043 if (!indun)
1044 return "away below you";
1045 else
1046 return "below you";
1047 else if (!indun)
1048 return "in the distance";
1049 else
1050 return "just below";
1051 } else if (ll > 0) {
1052 if (ll > (8 + rn2(3)))
1053 if (!indun)
1054 return "far away";
1055 else
1056 return "far above";
1057 else if (ll > 1)
1058 if (!indun)
1059 return "away above you";
1060 else
1061 return "above you";
1062 else if (!indun)
1063 return "in the distance";
1064 else
1065 return "just above";
1066 } else if (!indun)
1067 return "in the distance";
1068 else
1069 return "near you";
1072 static const struct {
1073 const char *what;
1074 d_level *where;
1075 } level_detects[] = {
1076 { "Delphi", &oracle_level },
1077 { "Medusa's lair", &medusa_level },
1078 { "a castle", &stronghold_level },
1079 { "the Wizard of Yendor's tower", &wiz1_level },
1082 void
1083 use_crystal_ball(optr)
1084 struct obj **optr;
1086 char ch;
1087 int oops;
1088 struct obj *obj = *optr;
1090 if (Blind) {
1091 pline("Too bad you can't see %s.", the(xname(obj)));
1092 return;
1094 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
1095 if (oops && (obj->spe > 0)) {
1096 switch (rnd(obj->oartifact ? 4 : 5)) {
1097 case 1:
1098 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
1099 break;
1100 case 2:
1101 pline("%s you!", Tobjnam(obj, "confuse"));
1102 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
1103 break;
1104 case 3:
1105 if (!resists_blnd(&youmonst)) {
1106 pline("%s your vision!", Tobjnam(obj, "damage"));
1107 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
1108 if (!Blind)
1109 Your1(vision_clears);
1110 } else {
1111 pline("%s your vision.", Tobjnam(obj, "assault"));
1112 You("are unaffected!");
1114 break;
1115 case 4:
1116 pline("%s your mind!", Tobjnam(obj, "zap"));
1117 (void) make_hallucinated(
1118 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
1119 break;
1120 case 5:
1121 pline("%s!", Tobjnam(obj, "explode"));
1122 useup(obj);
1123 *optr = obj = 0; /* it's gone */
1124 /* physical damage cause by the shards and force */
1125 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
1126 KILLED_BY_AN);
1127 break;
1129 if (obj)
1130 consume_obj_charge(obj, TRUE);
1131 return;
1134 if (Hallucination) {
1135 if (!obj->spe) {
1136 pline("All you see is funky %s haze.", hcolor((char *) 0));
1137 } else {
1138 switch (rnd(6)) {
1139 case 1:
1140 You("grok some groovy globs of incandescent lava.");
1141 break;
1142 case 2:
1143 pline("Whoa! Psychedelic colors, %s!",
1144 poly_gender() == 1 ? "babe" : "dude");
1145 break;
1146 case 3:
1147 pline_The("crystal pulses with sinister %s light!",
1148 hcolor((char *) 0));
1149 break;
1150 case 4:
1151 You_see("goldfish swimming above fluorescent rocks.");
1152 break;
1153 case 5:
1154 You_see(
1155 "tiny snowflakes spinning around a miniature farmhouse.");
1156 break;
1157 default:
1158 pline("Oh wow... like a kaleidoscope!");
1159 break;
1161 consume_obj_charge(obj, TRUE);
1163 return;
1166 /* read a single character */
1167 if (flags.verbose)
1168 You("may look for an object or monster symbol.");
1169 ch = yn_function("What do you look for?", (char *) 0, '\0');
1170 /* Don't filter out ' ' here; it has a use */
1171 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
1172 if (flags.verbose)
1173 pline1(Never_mind);
1174 return;
1176 You("peer into %s...", the(xname(obj)));
1177 nomul(-rnd(10));
1178 multi_reason = "gazing into a crystal ball";
1179 nomovemsg = "";
1180 if (obj->spe <= 0) {
1181 pline_The("vision is unclear.");
1182 } else {
1183 int class, i;
1184 int ret = 0;
1186 makeknown(CRYSTAL_BALL);
1187 consume_obj_charge(obj, TRUE);
1189 /* special case: accept ']' as synonym for mimic
1190 * we have to do this before the def_char_to_objclass check
1192 if (ch == DEF_MIMIC_DEF)
1193 ch = DEF_MIMIC;
1195 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1196 ret = object_detect((struct obj *) 0, class);
1197 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1198 ret = monster_detect((struct obj *) 0, class);
1199 else if (iflags.bouldersym && (ch == iflags.bouldersym))
1200 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1201 else
1202 switch (ch) {
1203 case '^':
1204 ret = trap_detect((struct obj *) 0);
1205 break;
1206 default:
1207 i = rn2(SIZE(level_detects));
1208 You_see("%s, %s.", level_detects[i].what,
1209 level_distance(level_detects[i].where));
1210 ret = 0;
1211 break;
1214 if (ret) {
1215 if (!rn2(100)) /* make them nervous */
1216 You_see("the Wizard of Yendor gazing out at you.");
1217 else
1218 pline_The("vision is unclear.");
1221 return;
1224 STATIC_OVL void
1225 show_map_spot(x, y)
1226 register int x, y;
1228 struct rm *lev;
1229 struct trap *t;
1230 int oldglyph;
1232 if (Confusion && rn2(7))
1233 return;
1234 lev = &levl[x][y];
1236 lev->seenv = SVALL;
1238 /* Secret corridors are found, but not secret doors. */
1239 if (lev->typ == SCORR) {
1240 lev->typ = CORR;
1241 unblock_point(x, y);
1245 * Force the real background, then if it's not furniture and there's
1246 * a known trap there, display the trap, else if there was an object
1247 * shown there, redisplay the object. So during mapping, furniture
1248 * takes precedence over traps, which take precedence over objects,
1249 * opposite to how normal vision behaves.
1251 oldglyph = glyph_at(x, y);
1252 if (level.flags.hero_memory) {
1253 magic_map_background(x, y, 0);
1254 newsym(x, y); /* show it, if not blocked */
1255 } else {
1256 magic_map_background(x, y, 1); /* display it */
1258 if (!IS_FURNITURE(lev->typ)) {
1259 if ((t = t_at(x, y)) != 0 && t->tseen) {
1260 map_trap(t, 1);
1261 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1262 show_glyph(x, y, oldglyph);
1263 if (level.flags.hero_memory)
1264 lev->glyph = oldglyph;
1269 void
1270 do_mapping()
1272 register int zx, zy;
1273 boolean unconstrained;
1275 unconstrained = unconstrain_map();
1276 for (zx = 1; zx < COLNO; zx++)
1277 for (zy = 0; zy < ROWNO; zy++)
1278 show_map_spot(zx, zy);
1280 if (!level.flags.hero_memory || unconstrained) {
1281 flush_screen(1); /* flush temp screen */
1282 /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
1283 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1284 "anything of interest");
1285 docrt();
1287 reconstrain_map();
1288 exercise(A_WIS, TRUE);
1291 /* clairvoyance */
1292 void
1293 do_vicinity_map(sobj)
1294 struct obj *sobj; /* scroll--actually fake spellbook--object */
1296 register int zx, zy;
1297 struct monst *mtmp;
1298 boolean unconstrained, refresh = FALSE, mdetected = FALSE,
1299 extended = (sobj && sobj->blessed);
1300 int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
1301 hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
1302 lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
1303 hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
1304 ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
1306 unconstrained = unconstrain_map();
1307 for (zx = lo_x; zx <= hi_x; zx++)
1308 for (zy = lo_y; zy <= hi_y; zy++) {
1309 show_map_spot(zx, zy);
1311 if (extended && (mtmp = m_at(zx, zy)) != 0
1312 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
1313 int oldglyph = glyph_at(zx, zy);
1315 map_monst(mtmp, FALSE);
1316 if (glyph_at(zx, zy) != oldglyph)
1317 mdetected = TRUE;
1321 if (!level.flags.hero_memory || unconstrained || mdetected) {
1322 flush_screen(1); /* flush temp screen */
1323 if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
1324 ter_typ |= TER_MON;
1325 if (extended)
1326 EDetect_monsters |= I_SPECIAL;
1327 browse_map(ter_typ, "anything of interest");
1328 EDetect_monsters &= ~I_SPECIAL;
1329 refresh = TRUE;
1331 reconstrain_map();
1332 if (refresh)
1333 docrt();
1336 /* convert a secret door into a normal door */
1337 void
1338 cvt_sdoor_to_door(lev)
1339 struct rm *lev;
1341 int newmask = lev->doormask & ~WM_MASK;
1343 if (Is_rogue_level(&u.uz))
1344 /* rogue didn't have doors, only doorways */
1345 newmask = D_NODOOR;
1346 else
1347 /* newly exposed door is closed */
1348 if (!(newmask & D_LOCKED))
1349 newmask |= D_CLOSED;
1351 lev->typ = DOOR;
1352 lev->doormask = newmask;
1355 STATIC_PTR void
1356 findone(zx, zy, num)
1357 int zx, zy;
1358 genericptr_t num;
1360 register struct trap *ttmp;
1361 register struct monst *mtmp;
1363 if (levl[zx][zy].typ == SDOOR) {
1364 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1365 magic_map_background(zx, zy, 0);
1366 newsym(zx, zy);
1367 (*(int *) num)++;
1368 } else if (levl[zx][zy].typ == SCORR) {
1369 levl[zx][zy].typ = CORR;
1370 unblock_point(zx, zy);
1371 magic_map_background(zx, zy, 0);
1372 newsym(zx, zy);
1373 (*(int *) num)++;
1374 } else if ((ttmp = t_at(zx, zy)) != 0) {
1375 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1376 ttmp->tseen = 1;
1377 newsym(zx, zy);
1378 (*(int *) num)++;
1380 } else if ((mtmp = m_at(zx, zy)) != 0) {
1381 if (mtmp->m_ap_type) {
1382 seemimic(mtmp);
1383 (*(int *) num)++;
1385 if (mtmp->mundetected
1386 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1387 mtmp->mundetected = 0;
1388 newsym(zx, zy);
1389 (*(int *) num)++;
1391 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1392 map_invisible(zx, zy);
1393 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1394 unmap_object(zx, zy);
1395 newsym(zx, zy);
1396 (*(int *) num)++;
1400 STATIC_PTR void
1401 openone(zx, zy, num)
1402 int zx, zy;
1403 genericptr_t num;
1405 register struct trap *ttmp;
1406 register struct obj *otmp;
1407 int *num_p = (int *) num;
1409 if (OBJ_AT(zx, zy)) {
1410 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1411 if (Is_box(otmp) && otmp->olocked) {
1412 otmp->olocked = 0;
1413 (*num_p)++;
1416 /* let it fall to the next cases. could be on trap. */
1418 if (levl[zx][zy].typ == SDOOR
1419 || (levl[zx][zy].typ == DOOR
1420 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1421 if (levl[zx][zy].typ == SDOOR)
1422 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1423 if (levl[zx][zy].doormask & D_TRAPPED) {
1424 if (distu(zx, zy) < 3)
1425 b_trapped("door", 0);
1426 else
1427 Norep("You %s an explosion!",
1428 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1429 : "feel the shock of"));
1430 wake_nearto(zx, zy, 11 * 11);
1431 levl[zx][zy].doormask = D_NODOOR;
1432 } else
1433 levl[zx][zy].doormask = D_ISOPEN;
1434 unblock_point(zx, zy);
1435 newsym(zx, zy);
1436 (*num_p)++;
1437 } else if (levl[zx][zy].typ == SCORR) {
1438 levl[zx][zy].typ = CORR;
1439 unblock_point(zx, zy);
1440 newsym(zx, zy);
1441 (*num_p)++;
1442 } else if ((ttmp = t_at(zx, zy)) != 0) {
1443 struct monst *mon;
1444 boolean dummy; /* unneeded "you notice it arg" */
1446 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1447 ttmp->tseen = 1;
1448 newsym(zx, zy);
1449 (*num_p)++;
1451 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1452 if (openholdingtrap(mon, &dummy)
1453 || openfallingtrap(mon, TRUE, &dummy))
1454 (*num_p)++;
1455 } else if (find_drawbridge(&zx, &zy)) {
1456 /* make sure it isn't an open drawbridge */
1457 open_drawbridge(zx, zy);
1458 (*num_p)++;
1462 /* returns number of things found */
1464 findit()
1466 int num = 0;
1468 if (u.uswallow)
1469 return 0;
1470 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1471 return num;
1474 /* returns number of things found and opened */
1476 openit()
1478 int num = 0;
1480 if (u.uswallow) {
1481 if (is_animal(u.ustuck->data)) {
1482 if (Blind)
1483 pline("Its mouth opens!");
1484 else
1485 pline("%s opens its mouth!", Monnam(u.ustuck));
1487 expels(u.ustuck, u.ustuck->data, TRUE);
1488 return -1;
1491 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1492 return num;
1495 /* callback hack for overriding vision in do_clear_area() */
1496 boolean
1497 detecting(func)
1498 void FDECL((*func), (int, int, genericptr_t));
1500 return (func == findone || func == openone);
1503 void
1504 find_trap(trap)
1505 struct trap *trap;
1507 int tt = what_trap(trap->ttyp);
1508 boolean cleared = FALSE;
1510 trap->tseen = 1;
1511 exercise(A_WIS, TRUE);
1512 feel_newsym(trap->tx, trap->ty);
1514 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1515 /* There's too much clutter to see your find otherwise */
1516 cls();
1517 map_trap(trap, 1);
1518 display_self();
1519 cleared = TRUE;
1522 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1524 if (cleared) {
1525 display_nhwindow(WIN_MAP, TRUE); /* wait */
1526 docrt();
1530 STATIC_OVL int
1531 mfind0(mtmp, via_warning)
1532 struct monst *mtmp;
1533 boolean via_warning;
1535 xchar x = mtmp->mx,
1536 y = mtmp->my;
1538 if (via_warning && !warning_of(mtmp))
1539 return -1;
1541 if (mtmp->m_ap_type) {
1542 seemimic(mtmp);
1543 find:
1544 exercise(A_WIS, TRUE);
1545 if (!canspotmon(mtmp)) {
1546 if (glyph_is_invisible(levl[x][y].glyph)) {
1547 /* Found invisible monster in a square which already has
1548 * an 'I' in it. Logically, this should still take time
1549 * and lead to a return 1, but if we did that the player
1550 * would keep finding the same monster every turn.
1552 return -1;
1553 } else {
1554 You_feel("an unseen monster!");
1555 map_invisible(x, y);
1557 } else if (!sensemon(mtmp))
1558 You("find %s.",
1559 mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1560 return 1;
1562 if (!canspotmon(mtmp)) {
1563 if (mtmp->mundetected
1564 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1565 if (via_warning) {
1566 Your("warning senses cause you to take a second %s.",
1567 Blind ? "to check nearby" : "look close by");
1568 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1570 mtmp->mundetected = 0;
1571 newsym(x, y);
1572 goto find;
1574 return 0;
1578 dosearch0(aflag)
1579 register int aflag; /* intrinsic autosearch vs explicit searching */
1581 #ifdef GCC_BUG
1582 /* Some old versions of gcc seriously muck up nested loops. If you get
1583 * strange crashes while searching in a version compiled with gcc, try
1584 * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
1585 * the makefile).
1587 volatile xchar x, y;
1588 #else
1589 register xchar x, y;
1590 #endif
1591 register struct trap *trap;
1592 register struct monst *mtmp;
1594 if (u.uswallow) {
1595 if (!aflag)
1596 pline("What are you looking for? The exit?");
1597 } else {
1598 int fund = (uwep && uwep->oartifact
1599 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1601 if (ublindf && ublindf->otyp == LENSES && !Blind)
1602 fund += 2; /* JDS: lenses help searching */
1603 if (fund > 5)
1604 fund = 5;
1605 for (x = u.ux - 1; x < u.ux + 2; x++)
1606 for (y = u.uy - 1; y < u.uy + 2; y++) {
1607 if (!isok(x, y))
1608 continue;
1609 if (x == u.ux && y == u.uy)
1610 continue;
1612 if (Blind && !aflag)
1613 feel_location(x, y);
1614 if (levl[x][y].typ == SDOOR) {
1615 if (rnl(7 - fund))
1616 continue;
1617 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1618 exercise(A_WIS, TRUE);
1619 nomul(0);
1620 feel_location(x, y); /* make sure it shows up */
1621 You("find a hidden door.");
1622 } else if (levl[x][y].typ == SCORR) {
1623 if (rnl(7 - fund))
1624 continue;
1625 levl[x][y].typ = CORR;
1626 unblock_point(x, y); /* vision */
1627 exercise(A_WIS, TRUE);
1628 nomul(0);
1629 feel_newsym(x, y); /* make sure it shows up */
1630 You("find a hidden passage.");
1631 } else {
1632 /* Be careful not to find anything in an SCORR or SDOOR */
1633 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
1634 int mfres = mfind0(mtmp, 0);
1636 if (mfres == -1)
1637 continue;
1638 else if (mfres > 0)
1639 return mfres;
1642 /* see if an invisible monster has moved--if Blind,
1643 * feel_location() already did it
1645 if (!aflag && !mtmp && !Blind
1646 && glyph_is_invisible(levl[x][y].glyph)) {
1647 unmap_object(x, y);
1648 newsym(x, y);
1651 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
1652 nomul(0);
1653 if (trap->ttyp == STATUE_TRAP) {
1654 if (activate_statue_trap(trap, x, y, FALSE))
1655 exercise(A_WIS, TRUE);
1656 return 1;
1657 } else {
1658 find_trap(trap);
1664 return 1;
1667 /* the 's' command -- explicit searching */
1669 dosearch()
1671 return dosearch0(0);
1674 void
1675 warnreveal()
1677 int x, y;
1678 struct monst *mtmp;
1680 for (x = u.ux - 1; x <= u.ux + 1; x++)
1681 for (y = u.uy - 1; y <= u.uy + 1; y++) {
1682 if (!isok(x, y) || (x == u.ux && y == u.uy))
1683 continue;
1684 if ((mtmp = m_at(x, y)) != 0
1685 && warning_of(mtmp) && mtmp->mundetected)
1686 (void) mfind0(mtmp, 1); /* via_warning */
1690 /* Pre-map the sokoban levels */
1691 void
1692 sokoban_detect()
1694 register int x, y;
1695 register struct trap *ttmp;
1696 register struct obj *obj;
1698 /* Map the background and boulders */
1699 for (x = 1; x < COLNO; x++)
1700 for (y = 0; y < ROWNO; y++) {
1701 levl[x][y].seenv = SVALL;
1702 levl[x][y].waslit = TRUE;
1703 map_background(x, y, 1);
1704 if ((obj = sobj_at(BOULDER, x, y)) != 0)
1705 map_object(obj, 1);
1708 /* Map the traps */
1709 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1710 ttmp->tseen = 1;
1711 map_trap(ttmp, 1);
1712 /* set sokoban_rules when there is at least one pit or hole */
1713 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
1714 Sokoban = 1;
1718 /* idea from crawl; show known portion of map without any monsters,
1719 objects, or traps occluding the view of the underlying terrain */
1720 void
1721 reveal_terrain(full, which_subset)
1722 int full; /* wizard|explore modes allow player to request full map */
1723 int which_subset; /* when not full, whether to suppress objs and/or traps */
1725 if ((Hallucination || Stunned || Confusion) && !full) {
1726 You("are too disoriented for this.");
1727 } else {
1728 int x, y, glyph, levl_glyph, default_glyph;
1729 uchar seenv;
1730 struct monst *mtmp;
1731 struct trap *t;
1732 char buf[BUFSZ];
1733 /* there is a TER_MAP bit too; we always show map regardless of it */
1734 boolean keep_traps = (which_subset & TER_TRP) !=0,
1735 keep_objs = (which_subset & TER_OBJ) != 0,
1736 keep_mons = (which_subset & TER_MON) != 0; /* not used */
1737 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
1739 if (unconstrain_map())
1740 docrt();
1741 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1742 /* for 'full', show the actual terrain for the entire level,
1743 otherwise what the hero remembers for seen locations with
1744 monsters, objects, and/or traps removed as caller dictates */
1745 for (x = 1; x < COLNO; x++)
1746 for (y = 0; y < ROWNO; y++) {
1747 seenv = (full || level.flags.hero_memory)
1748 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
1749 if (full) {
1750 levl[x][y].seenv = SVALL;
1751 glyph = back_to_glyph(x, y);
1752 levl[x][y].seenv = seenv;
1753 } else {
1754 levl_glyph = level.flags.hero_memory
1755 ? levl[x][y].glyph
1756 : seenv
1757 ? back_to_glyph(x, y)
1758 : default_glyph;
1759 /* glyph_at() returns the displayed glyph, which might
1760 be a monster. levl[][].glyph contains the remembered
1761 glyph, which will never be a monster (unless it is
1762 the invisible monster glyph, which is handled like
1763 an object, replacing any object or trap at its spot) */
1764 glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
1765 if (keep_mons && x == u.ux && y == u.uy && swallowed)
1766 glyph = mon_to_glyph(u.ustuck);
1767 else if (((glyph_is_monster(glyph)
1768 || glyph_is_warning(glyph)) && !keep_mons)
1769 || glyph_is_swallow(glyph))
1770 glyph = levl_glyph;
1771 if (((glyph_is_object(glyph) && !keep_objs)
1772 || glyph_is_invisible(glyph))
1773 && keep_traps && !covers_traps(x, y)) {
1774 if ((t = t_at(x, y)) != 0 && t->tseen)
1775 glyph = trap_to_glyph(t);
1777 if ((glyph_is_object(glyph) && !keep_objs)
1778 || (glyph_is_trap(glyph) && !keep_traps)
1779 || glyph_is_invisible(glyph)) {
1780 if (!seenv) {
1781 glyph = default_glyph;
1782 } else if (lastseentyp[x][y] == levl[x][y].typ) {
1783 glyph = back_to_glyph(x, y);
1784 } else {
1785 /* look for a mimic here posing as furniture;
1786 if we don't find one, we'll have to fake it */
1787 if ((mtmp = m_at(x, y)) != 0
1788 && mtmp->m_ap_type == M_AP_FURNITURE) {
1789 glyph = cmap_to_glyph(mtmp->mappearance);
1790 } else {
1791 /* we have a topology type but we want a
1792 screen symbol in order to derive a glyph;
1793 some screen symbols need the flags field
1794 of levl[][] in addition to the type
1795 (to disambiguate STAIRS to S_upstair or
1796 S_dnstair, for example; current flags
1797 might not be intended for remembered
1798 type, but we've got no other choice) */
1799 schar save_typ = levl[x][y].typ;
1801 levl[x][y].typ = lastseentyp[x][y];
1802 glyph = back_to_glyph(x, y);
1803 levl[x][y].typ = save_typ;
1808 if (glyph == cmap_to_glyph(S_darkroom))
1809 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
1810 show_glyph(x, y, glyph);
1813 /* hero's location is not highlighted, but getpos() starts with
1814 cursor there, and after moving it anywhere '@' moves it back */
1815 flush_screen(1);
1816 if (full) {
1817 Strcpy(buf, "underlying terrain");
1818 } else {
1819 Strcpy(buf, "known terrain");
1820 if (keep_traps)
1821 Sprintf(eos(buf), "%s traps",
1822 (keep_objs || keep_mons) ? "," : " and");
1823 if (keep_objs)
1824 Sprintf(eos(buf), "%s%s objects",
1825 (keep_traps || keep_mons) ? "," : "",
1826 keep_mons ? "" : " and");
1827 if (keep_mons)
1828 Sprintf(eos(buf), "%s and monsters",
1829 (keep_traps || keep_objs) ? "," : "");
1831 pline("Showing %s only...", buf);
1833 /* allow player to move cursor around and get autodescribe feedback
1834 based on what is visible now rather than what is on 'real' map */
1835 which_subset |= TER_MAP; /* guarantee non-zero */
1836 browse_map(which_subset, "anything of interest");
1838 reconstrain_map();
1839 docrt(); /* redraw the screen, restoring regular map */
1840 if (Underwater)
1841 under_water(2);
1842 if (u.uburied)
1843 under_ground(2);
1845 return;
1848 /*detect.c*/