Couple of extra nethack->anethack
[aNetHack.git] / src / detect.c
blob3dce8347df8f3726a82c1c95e3c19adf8786c34e
1 /* aNetHack 0.0.1 detect.c $ANH-Date: 1463191981 2016/05/14 02:13:01 $ $ANH-Branch: master $:$ANH-Revision: 1.70 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack 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));
28 STATIC_DCL int FDECL(reveal_terrain_getglyph, (int, int, int,
29 unsigned, int, int));
31 /* bring hero out from underwater or underground or being engulfed;
32 return True iff any change occurred */
33 STATIC_OVL boolean
34 unconstrain_map()
36 boolean res = u.uinwater || u.uburied || u.uswallow;
38 /* bring Underwater, buried, or swallowed hero to normal map */
39 iflags.save_uinwater = u.uinwater, u.uinwater = 0;
40 iflags.save_uburied = u.uburied, u.uburied = 0;
41 iflags.save_uswallow = u.uswallow, u.uswallow = 0;
43 return res;
46 /* put hero back underwater or underground or engulfed */
47 STATIC_OVL void
48 reconstrain_map()
50 u.uinwater = iflags.save_uinwater, iflags.save_uinwater = 0;
51 u.uburied = iflags.save_uburied, iflags.save_uburied = 0;
52 u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0;
55 /* use getpos()'s 'autodescribe' to view whatever is currently shown on map */
56 STATIC_DCL void
57 browse_map(ter_typ, ter_explain)
58 int ter_typ;
59 const char *ter_explain;
61 coord dummy_pos; /* don't care whether player actually picks a spot */
63 dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */
64 iflags.autodescribe = TRUE;
65 iflags.terrainmode = ter_typ;
66 getpos(&dummy_pos, FALSE, ter_explain);
67 iflags.terrainmode = 0;
68 /* leave iflags.autodescribe 'on' even if previously 'off' */
71 /* extracted from monster_detection() so can be shared by do_vicinity_map() */
72 STATIC_DCL void
73 map_monst(mtmp, showtail)
74 struct monst *mtmp;
75 boolean showtail;
77 if (def_monsyms[(int) mtmp->data->mlet].sym == ' ')
78 show_glyph(mtmp->mx, mtmp->my, detected_mon_to_glyph(mtmp));
79 else
80 show_glyph(mtmp->mx, mtmp->my,
81 mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
83 if (showtail && mtmp->data == &mons[PM_LONG_WORM])
84 detect_wsegs(mtmp, 0);
87 /* this is checking whether a trap symbol represents a trapped chest,
88 not whether a trapped chest is actually present */
89 boolean
90 trapped_chest_at(ttyp, x, y)
91 int ttyp;
92 int x, y;
94 struct monst *mtmp;
95 struct obj *otmp;
97 if (!glyph_is_trap(glyph_at(x, y)))
98 return FALSE;
99 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
100 return FALSE;
103 * TODO? We should check containers recursively like the trap
104 * detecting routine does. Chests and large boxes do not nest in
105 * themselves or each other, but could be contained inside statues.
107 * For farlook, we should also check for buried containers, but
108 * for '^' command to examine adjacent trap glyph, we shouldn't.
111 /* on map, presence of any trappable container will do */
112 if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y))
113 return TRUE;
114 /* in inventory, we need to find one which is actually trapped */
115 if (x == u.ux && y == u.uy) {
116 for (otmp = invent; otmp; otmp = otmp->nobj)
117 if (Is_box(otmp) && otmp->otrapped)
118 return TRUE;
119 if (u.usteed) { /* steed isn't on map so won't be found by m_at() */
120 for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj)
121 if (Is_box(otmp) && otmp->otrapped)
122 return TRUE;
125 if ((mtmp = m_at(x, y)) != 0)
126 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
127 if (Is_box(otmp) && otmp->otrapped)
128 return TRUE;
129 return FALSE;
132 /* this is checking whether a trap symbol represents a trapped door,
133 not whether the door here is actually trapped */
134 boolean
135 trapped_door_at(ttyp, x, y)
136 int ttyp;
137 int x, y;
139 struct rm *lev;
141 if (!glyph_is_trap(glyph_at(x, y)))
142 return FALSE;
143 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
144 return FALSE;
145 lev = &levl[x][y];
146 if (!IS_DOOR(lev->typ))
147 return FALSE;
148 if ((lev->doormask & (D_NODOOR | D_BROKEN | D_ISOPEN)) != 0
149 && trapped_chest_at(ttyp, x, y))
150 return FALSE;
151 return TRUE;
154 /* recursively search obj for an object in class oclass, return 1st found */
155 struct obj *
156 o_in(obj, oclass)
157 struct obj *obj;
158 char oclass;
160 register struct obj *otmp;
161 struct obj *temp;
163 if (obj->oclass == oclass)
164 return obj;
166 if (Has_contents(obj)) {
167 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
168 if (otmp->oclass == oclass)
169 return otmp;
170 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)) != 0)
171 return temp;
173 return (struct obj *) 0;
176 /* Recursively search obj for an object made of specified material.
177 * Return first found.
179 struct obj *
180 o_material(obj, material)
181 struct obj *obj;
182 unsigned material;
184 register struct obj *otmp;
185 struct obj *temp;
187 if (objects[obj->otyp].oc_material == material)
188 return obj;
190 if (Has_contents(obj)) {
191 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
192 if (objects[otmp->otyp].oc_material == material)
193 return otmp;
194 else if (Has_contents(otmp)
195 && (temp = o_material(otmp, material)) != 0)
196 return temp;
198 return (struct obj *) 0;
201 STATIC_OVL void
202 do_dknown_of(obj)
203 struct obj *obj;
205 struct obj *otmp;
207 obj->dknown = 1;
208 if (Has_contents(obj)) {
209 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
210 do_dknown_of(otmp);
214 /* Check whether the location has an outdated object displayed on it. */
215 STATIC_OVL boolean
216 check_map_spot(x, y, oclass, material)
217 int x, y;
218 char oclass;
219 unsigned material;
221 int glyph;
222 register struct obj *otmp;
223 register struct monst *mtmp;
225 glyph = glyph_at(x, y);
226 if (glyph_is_object(glyph)) {
227 /* there's some object shown here */
228 if (oclass == ALL_CLASSES) {
229 return (boolean) !(level.objects[x][y] /* stale if nothing here */
230 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
231 } else {
232 if (material
233 && objects[glyph_to_obj(glyph)].oc_material == material) {
234 /* object shown here is of interest because material matches */
235 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
236 if (o_material(otmp, GOLD))
237 return FALSE;
238 /* didn't find it; perhaps a monster is carrying it */
239 if ((mtmp = m_at(x, y)) != 0) {
240 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
241 if (o_material(otmp, GOLD))
242 return FALSE;
244 /* detection indicates removal of this object from the map */
245 return TRUE;
247 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
248 /* obj shown here is of interest because its class matches */
249 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
250 if (o_in(otmp, oclass))
251 return FALSE;
252 /* didn't find it; perhaps a monster is carrying it */
253 if ((mtmp = m_at(x, y)) != 0) {
254 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
255 if (o_in(otmp, oclass))
256 return FALSE;
258 /* detection indicates removal of this object from the map */
259 return TRUE;
263 return FALSE;
267 * When doing detection, remove stale data from the map display (corpses
268 * rotted away, objects carried away by monsters, etc) so that it won't
269 * reappear after the detection has completed. Return true if noticeable
270 * change occurs.
272 STATIC_OVL boolean
273 clear_stale_map(oclass, material)
274 char oclass;
275 unsigned material;
277 register int zx, zy;
278 boolean change_made = FALSE;
280 for (zx = 1; zx < COLNO; zx++)
281 for (zy = 0; zy < ROWNO; zy++)
282 if (check_map_spot(zx, zy, oclass, material)) {
283 unmap_object(zx, zy);
284 change_made = TRUE;
287 return change_made;
290 /* look for gold, on the floor or in monsters' possession */
292 gold_detect(sobj)
293 register struct obj *sobj;
295 register struct obj *obj;
296 register struct monst *mtmp;
297 struct obj gold, *temp = 0;
298 boolean stale, ugold = FALSE, steedgold = FALSE;
299 int ter_typ = TER_DETECT | TER_OBJ;
301 known = stale = clear_stale_map(COIN_CLASS,
302 (unsigned) (sobj->blessed ? GOLD : 0));
304 /* look for gold carried by monsters (might be in a container) */
305 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
306 if (DEADMONSTER(mtmp))
307 continue; /* probably not needed in this case but... */
308 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
309 if (mtmp == u.usteed) {
310 steedgold = TRUE;
311 } else {
312 known = TRUE;
313 goto outgoldmap; /* skip further searching */
315 } else {
316 for (obj = mtmp->minvent; obj; obj = obj->nobj)
317 if ((sobj->blessed && o_material(obj, GOLD))
318 || o_in(obj, COIN_CLASS)) {
319 if (mtmp == u.usteed) {
320 steedgold = TRUE;
321 } else {
322 known = TRUE;
323 goto outgoldmap; /* skip further searching */
329 /* look for gold objects */
330 for (obj = fobj; obj; obj = obj->nobj) {
331 if (sobj->blessed && o_material(obj, GOLD)) {
332 known = TRUE;
333 if (obj->ox != u.ux || obj->oy != u.uy)
334 goto outgoldmap;
335 } else if (o_in(obj, COIN_CLASS)) {
336 known = TRUE;
337 if (obj->ox != u.ux || obj->oy != u.uy)
338 goto outgoldmap;
342 if (!known) {
343 /* no gold found on floor or monster's inventory.
344 adjust message if you have gold in your inventory */
345 if (sobj) {
346 char buf[BUFSZ];
348 if (youmonst.data == &mons[PM_GOLD_GOLEM])
349 Sprintf(buf, "You feel like a million %s!", currency(2L));
350 else if (money_cnt(invent) || hidden_gold())
351 Strcpy(buf,
352 "You feel worried about your future financial situation.");
353 else if (steedgold)
354 Sprintf(buf, "You feel interested in %s financial situation.",
355 s_suffix(x_monnam(u.usteed,
356 u.usteed->mtame ? ARTICLE_YOUR
357 : ARTICLE_THE,
358 (char *) 0,
359 SUPPRESS_SADDLE, FALSE)));
360 else
361 Strcpy(buf, "You feel materially poor.");
363 strange_feeling(sobj, buf);
365 return 1;
367 /* only under me - no separate display required */
368 if (stale)
369 docrt();
370 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
371 return 0;
373 outgoldmap:
374 cls();
376 (void) unconstrain_map();
377 /* Discover gold locations. */
378 for (obj = fobj; obj; obj = obj->nobj) {
379 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
380 if (temp != obj) {
381 temp->ox = obj->ox;
382 temp->oy = obj->oy;
384 map_object(temp, 1);
385 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
386 if (temp != obj) {
387 temp->ox = obj->ox;
388 temp->oy = obj->oy;
390 map_object(temp, 1);
392 if (temp && temp->ox == u.ux && temp->oy == u.uy)
393 ugold = TRUE;
395 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
396 if (DEADMONSTER(mtmp))
397 continue; /* probably overkill here */
398 temp = 0;
399 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
400 gold = zeroobj; /* ensure oextra is cleared too */
401 gold.otyp = GOLD_PIECE;
402 gold.quan = (long) rnd(10); /* usually more than 1 */
403 gold.ox = mtmp->mx;
404 gold.oy = mtmp->my;
405 map_object(&gold, 1);
406 temp = &gold;
407 } else {
408 for (obj = mtmp->minvent; obj; obj = obj->nobj)
409 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
410 temp->ox = mtmp->mx;
411 temp->oy = mtmp->my;
412 map_object(temp, 1);
413 break;
414 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
415 temp->ox = mtmp->mx;
416 temp->oy = mtmp->my;
417 map_object(temp, 1);
418 break;
421 if (temp && temp->ox == u.ux && temp->oy == u.uy)
422 ugold = TRUE;
424 if (!ugold) {
425 newsym(u.ux, u.uy);
426 ter_typ |= TER_MON; /* so autodescribe will recognize hero */
428 You_feel("very greedy, and sense gold!");
429 exercise(A_WIS, TRUE);
431 browse_map(ter_typ, "gold");
433 reconstrain_map();
434 docrt();
435 if (Underwater)
436 under_water(2);
437 if (u.uburied)
438 under_ground(2);
439 return 0;
442 /* returns 1 if nothing was detected */
443 /* returns 0 if something was detected */
445 food_detect(sobj)
446 register struct obj *sobj;
448 register struct obj *obj;
449 register struct monst *mtmp;
450 register int ct = 0, ctu = 0;
451 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
452 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
453 const char *what = confused ? something : "food";
455 stale = clear_stale_map(oclass, 0);
456 if (u.usteed) /* some situations leave steed with stale coordinates */
457 u.usteed->mx = u.ux, u.usteed->my = u.uy;
459 for (obj = fobj; obj; obj = obj->nobj)
460 if (o_in(obj, oclass)) {
461 if (obj->ox == u.ux && obj->oy == u.uy)
462 ctu++;
463 else
464 ct++;
466 for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) {
467 /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */
468 for (obj = mtmp->minvent; obj; obj = obj->nobj)
469 if (o_in(obj, oclass)) {
470 if (mtmp->mx == u.ux && mtmp->my == u.uy)
471 ctu++; /* steed or an engulfer with inventory */
472 else
473 ct++;
474 break;
478 if (!ct && !ctu) {
479 known = stale && !confused;
480 if (stale) {
481 docrt();
482 You("sense a lack of %s nearby.", what);
483 if (sobj && sobj->blessed) {
484 if (!u.uedibility)
485 Your("%s starts to tingle.", body_part(NOSE));
486 u.uedibility = 1;
488 } else if (sobj) {
489 char buf[BUFSZ];
491 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
492 (sobj->blessed && !u.uedibility)
493 ? " then starts to tingle"
494 : "");
495 if (sobj->blessed && !u.uedibility) {
496 boolean savebeginner = flags.beginner;
498 flags.beginner = FALSE; /* prevent non-delivery of message */
499 strange_feeling(sobj, buf);
500 flags.beginner = savebeginner;
501 u.uedibility = 1;
502 } else
503 strange_feeling(sobj, buf);
505 return !stale;
506 } else if (!ct) {
507 known = TRUE;
508 You("%s %s nearby.", sobj ? "smell" : "sense", what);
509 if (sobj && sobj->blessed) {
510 if (!u.uedibility)
511 pline("Your %s starts to tingle.", body_part(NOSE));
512 u.uedibility = 1;
514 } else {
515 struct obj *temp;
516 int ter_typ = TER_DETECT | TER_OBJ;
518 known = TRUE;
519 cls();
520 (void) unconstrain_map();
521 for (obj = fobj; obj; obj = obj->nobj)
522 if ((temp = o_in(obj, oclass)) != 0) {
523 if (temp != obj) {
524 temp->ox = obj->ox;
525 temp->oy = obj->oy;
527 map_object(temp, 1);
529 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
530 /* no DEADMONSTER() check needed -- dmons never have inventory */
531 for (obj = mtmp->minvent; obj; obj = obj->nobj)
532 if ((temp = o_in(obj, oclass)) != 0) {
533 temp->ox = mtmp->mx;
534 temp->oy = mtmp->my;
535 map_object(temp, 1);
536 break; /* skip rest of this monster's inventory */
538 if (!ctu) {
539 newsym(u.ux, u.uy);
540 ter_typ |= TER_MON; /* for autodescribe of self */
542 if (sobj) {
543 if (sobj->blessed) {
544 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
545 u.uedibility ? "continues" : "starts", what);
546 u.uedibility = 1;
547 } else
548 Your("%s tingles and you smell %s.", body_part(NOSE), what);
549 } else
550 You("sense %s.", what);
551 exercise(A_WIS, TRUE);
553 browse_map(ter_typ, "food");
555 reconstrain_map();
556 docrt();
557 if (Underwater)
558 under_water(2);
559 if (u.uburied)
560 under_ground(2);
562 return 0;
566 * Used for scrolls, potions, spells, and crystal balls. Returns:
568 * 1 - nothing was detected
569 * 0 - something was detected
572 object_detect(detector, class)
573 struct obj *detector; /* object doing the detecting */
574 int class; /* an object class, 0 for all */
576 register int x, y;
577 char stuff[BUFSZ];
578 int is_cursed = (detector && detector->cursed);
579 int do_dknown = (detector && (detector->oclass == POTION_CLASS
580 || detector->oclass == SPBOOK_CLASS)
581 && detector->blessed);
582 int ct = 0, ctu = 0;
583 register struct obj *obj, *otmp = (struct obj *) 0;
584 register struct monst *mtmp;
585 int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ;
587 if (class < 0 || class >= MAXOCLASSES) {
588 impossible("object_detect: illegal class %d", class);
589 class = 0;
592 /* Special boulder symbol check - does the class symbol happen
593 * to match iflags.bouldersym which is a user-defined?
594 * If so, that means we aren't sure what they really wanted to
595 * detect. Rather than trump anything, show both possibilities.
596 * We can exclude checking the buried obj chain for boulders below.
598 sym = class ? def_oc_syms[class].sym : 0;
599 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
600 boulder = ROCK_CLASS;
602 if (Hallucination || (Confusion && class == SCROLL_CLASS))
603 Strcpy(stuff, something);
604 else
605 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
606 if (boulder && class != ROCK_CLASS)
607 Strcat(stuff, " and/or large stones");
609 if (do_dknown)
610 for (obj = invent; obj; obj = obj->nobj)
611 do_dknown_of(obj);
613 for (obj = fobj; obj; obj = obj->nobj) {
614 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
615 if (obj->ox == u.ux && obj->oy == u.uy)
616 ctu++;
617 else
618 ct++;
620 if (do_dknown)
621 do_dknown_of(obj);
624 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
625 if (!class || o_in(obj, class)) {
626 if (obj->ox == u.ux && obj->oy == u.uy)
627 ctu++;
628 else
629 ct++;
631 if (do_dknown)
632 do_dknown_of(obj);
635 if (u.usteed)
636 u.usteed->mx = u.ux, u.usteed->my = u.uy;
638 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
639 if (DEADMONSTER(mtmp))
640 continue;
641 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
642 if ((!class && !boulder) || o_in(obj, class)
643 || o_in(obj, boulder))
644 ct++;
645 if (do_dknown)
646 do_dknown_of(obj);
648 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
649 && (!class || class == objects[mtmp->mappearance].oc_class))
650 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
651 ct++;
652 break;
656 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
657 if (!ctu) {
658 if (detector)
659 strange_feeling(detector, "You feel a lack of something.");
660 return 1;
663 You("sense %s nearby.", stuff);
664 return 0;
667 cls();
669 (void) unconstrain_map();
671 * Map all buried objects first.
673 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
674 if (!class || (otmp = o_in(obj, class)) != 0) {
675 if (class) {
676 if (otmp != obj) {
677 otmp->ox = obj->ox;
678 otmp->oy = obj->oy;
680 map_object(otmp, 1);
681 } else
682 map_object(obj, 1);
685 * If we are mapping all objects, map only the top object of a pile or
686 * the first object in a monster's inventory. Otherwise, go looking
687 * for a matching object class and display the first one encountered
688 * at each location.
690 * Objects on the floor override buried objects.
692 for (x = 1; x < COLNO; x++)
693 for (y = 0; y < ROWNO; y++)
694 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
695 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
696 || (otmp = o_in(obj, boulder)) != 0) {
697 if (class || boulder) {
698 if (otmp != obj) {
699 otmp->ox = obj->ox;
700 otmp->oy = obj->oy;
702 map_object(otmp, 1);
703 } else
704 map_object(obj, 1);
705 break;
708 /* Objects in the monster's inventory override floor objects. */
709 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
710 if (DEADMONSTER(mtmp))
711 continue;
712 for (obj = mtmp->minvent; obj; obj = obj->nobj)
713 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
714 || (otmp = o_in(obj, boulder)) != 0) {
715 if (!class && !boulder)
716 otmp = obj;
717 otmp->ox = mtmp->mx; /* at monster location */
718 otmp->oy = mtmp->my;
719 map_object(otmp, 1);
720 break;
722 /* Allow a mimic to override the detected objects it is carrying. */
723 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
724 && (!class || class == objects[mtmp->mappearance].oc_class)) {
725 struct obj temp;
727 temp = zeroobj;
728 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
729 temp.quan = 1L;
730 temp.ox = mtmp->mx;
731 temp.oy = mtmp->my;
732 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
733 map_object(&temp, 1);
734 } else if (findgold(mtmp->minvent)
735 && (!class || class == COIN_CLASS)) {
736 struct obj gold;
738 gold = zeroobj; /* ensure oextra is cleared too */
739 gold.otyp = GOLD_PIECE;
740 gold.quan = (long) rnd(10); /* usually more than 1 */
741 gold.ox = mtmp->mx;
742 gold.oy = mtmp->my;
743 map_object(&gold, 1);
746 if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
747 newsym(u.ux, u.uy);
748 ter_typ |= TER_MON;
750 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
752 if (!ct)
753 display_nhwindow(WIN_MAP, TRUE);
754 else
755 browse_map(ter_typ, "object");
757 reconstrain_map();
758 docrt(); /* this will correctly reset vision */
759 if (Underwater)
760 under_water(2);
761 if (u.uburied)
762 under_ground(2);
763 return 0;
767 * Used by: crystal balls, potions, fountains
769 * Returns 1 if nothing was detected.
770 * Returns 0 if something was detected.
773 monster_detect(otmp, mclass)
774 register struct obj *otmp; /* detecting object (if any) */
775 int mclass; /* monster class, 0 for all */
777 register struct monst *mtmp;
778 int mcnt = 0;
780 /* Note: This used to just check fmon for a non-zero value
781 * but in versions since 3.3.0 fmon can test TRUE due to the
782 * presence of dmons, so we have to find at least one
783 * with positive hit-points to know for sure.
785 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
786 if (!DEADMONSTER(mtmp)) {
787 mcnt++;
788 break;
791 if (!mcnt) {
792 if (otmp)
793 strange_feeling(otmp, Hallucination
794 ? "You get the heebie jeebies."
795 : "You feel threatened.");
796 return 1;
797 } else {
798 boolean unconstrained, woken = FALSE;
799 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
801 cls();
802 unconstrained = unconstrain_map();
803 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
804 if (DEADMONSTER(mtmp))
805 continue;
806 if (!mclass || mtmp->data->mlet == mclass
807 || (mtmp->data == &mons[PM_LONG_WORM]
808 && mclass == S_WORM_TAIL))
809 map_monst(mtmp, TRUE);
811 if (otmp && otmp->cursed
812 && (mtmp->msleeping || !mtmp->mcanmove)) {
813 mtmp->msleeping = mtmp->mfrozen = 0;
814 mtmp->mcanmove = 1;
815 woken = TRUE;
818 if (!swallowed)
819 display_self();
820 You("sense the presence of monsters.");
821 if (woken)
822 pline("Monsters sense the presence of you.");
824 if ((otmp && otmp->blessed) && !unconstrained) {
825 /* persistent detection--just show updated map */
826 display_nhwindow(WIN_MAP, TRUE);
827 } else {
828 /* one-shot detection--allow player to move cursor around and
829 get autodescribe feedback */
830 EDetect_monsters |= I_SPECIAL;
831 browse_map(TER_DETECT | TER_MON, "monster of interest");
832 EDetect_monsters &= ~I_SPECIAL;
835 reconstrain_map();
836 docrt(); /* redraw the screen to remove unseen monsters from map */
837 if (Underwater)
838 under_water(2);
839 if (u.uburied)
840 under_ground(2);
842 return 0;
845 STATIC_OVL void
846 sense_trap(trap, x, y, src_cursed)
847 struct trap *trap;
848 xchar x, y;
849 int src_cursed;
851 if (Hallucination || src_cursed) {
852 struct obj obj; /* fake object */
854 obj = zeroobj;
855 if (trap) {
856 obj.ox = trap->tx;
857 obj.oy = trap->ty;
858 } else {
859 obj.ox = x;
860 obj.oy = y;
862 obj.otyp = !Hallucination ? GOLD_PIECE : random_object();
863 obj.quan = (long) ((obj.otyp == GOLD_PIECE) ? rnd(10)
864 : objects[obj.otyp].oc_merge ? rnd(2) : 1);
865 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
866 map_object(&obj, 1);
867 } else if (trap) {
868 map_trap(trap, 1);
869 trap->tseen = 1;
870 } else { /* trapped door or trapped chest */
871 struct trap temp_trap; /* fake trap */
873 (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap);
874 temp_trap.tx = x;
875 temp_trap.ty = y;
876 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
877 map_trap(&temp_trap, 1);
881 #define OTRAP_NONE 0 /* nothing found */
882 #define OTRAP_HERE 1 /* found at hero's location */
883 #define OTRAP_THERE 2 /* found at any other location */
885 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
886 2 if found at some other spot, 3 if both, 0 otherwise; optionally
887 update the map to show where such traps were found */
888 STATIC_OVL int
889 detect_obj_traps(objlist, show_them, how)
890 struct obj *objlist;
891 boolean show_them;
892 int how; /* 1 for misleading map feedback */
894 struct obj *otmp;
895 xchar x, y;
896 int result = OTRAP_NONE;
899 * TODO? Display locations of unarmed land mine and beartrap objects.
900 * If so, should they be displayed as objects or as traps?
903 for (otmp = objlist; otmp; otmp = otmp->nobj) {
904 if (Is_box(otmp) && otmp->otrapped
905 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
906 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
907 if (show_them)
908 sense_trap((struct trap *) 0, x, y, how);
910 if (Has_contents(otmp))
911 result |= detect_obj_traps(otmp->cobj, show_them, how);
913 return result;
916 /* the detections are pulled out so they can
917 * also be used in the crystal ball routine
918 * returns 1 if nothing was detected
919 * returns 0 if something was detected
922 trap_detect(sobj)
923 struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
925 register struct trap *ttmp;
926 struct monst *mon;
927 int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP;
928 int cursed_src = sobj && sobj->cursed;
929 boolean found = FALSE;
930 coord cc;
932 if (u.usteed)
933 u.usteed->mx = u.ux, u.usteed->my = u.uy;
935 /* floor/ceiling traps */
936 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
937 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
938 goto outtrapmap;
939 else
940 found = TRUE;
942 /* chest traps (might be buried or carried) */
943 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
944 if (tr & OTRAP_THERE)
945 goto outtrapmap;
946 else
947 found = TRUE;
949 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0)) != OTRAP_NONE) {
950 if (tr & OTRAP_THERE)
951 goto outtrapmap;
952 else
953 found = TRUE;
955 for (mon = fmon; mon; mon = mon->nmon) {
956 if (DEADMONSTER(mon))
957 continue;
958 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
959 if (tr & OTRAP_THERE)
960 goto outtrapmap;
961 else
962 found = TRUE;
965 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
966 found = TRUE;
967 /* door traps */
968 for (door = 0; door < doorindex; door++) {
969 cc = doors[door];
970 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
971 if (cc.x != u.ux || cc.y != u.uy)
972 goto outtrapmap;
973 else
974 found = TRUE;
977 if (!found) {
978 char buf[BUFSZ];
980 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
981 strange_feeling(sobj, buf);
982 return 1;
984 /* traps exist, but only under me - no separate display required */
985 Your("%s itch.", makeplural(body_part(TOE)));
986 return 0;
988 outtrapmap:
989 cls();
991 (void) unconstrain_map();
992 /* show chest traps first, so that subsequent floor trap display
993 will override if both types are present at the same location */
994 (void) detect_obj_traps(fobj, TRUE, cursed_src);
995 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
996 for (mon = fmon; mon; mon = mon->nmon) {
997 if (DEADMONSTER(mon))
998 continue;
999 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
1001 (void) detect_obj_traps(invent, TRUE, cursed_src);
1003 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
1004 sense_trap(ttmp, 0, 0, cursed_src);
1006 for (door = 0; door < doorindex; door++) {
1007 cc = doors[door];
1008 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
1009 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
1012 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
1013 glyph = glyph_at(u.ux, u.uy);
1014 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) {
1015 newsym(u.ux, u.uy);
1016 ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
1018 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
1020 browse_map(ter_typ, "trap of interest");
1022 reconstrain_map();
1023 docrt(); /* redraw the screen to remove unseen traps from the map */
1024 if (Underwater)
1025 under_water(2);
1026 if (u.uburied)
1027 under_ground(2);
1028 return 0;
1031 const char *
1032 level_distance(where)
1033 d_level *where;
1035 register schar ll = depth(&u.uz) - depth(where);
1036 register boolean indun = (u.uz.dnum == where->dnum);
1038 if (ll < 0) {
1039 if (ll < (-8 - rn2(3)))
1040 if (!indun)
1041 return "far away";
1042 else
1043 return "far below";
1044 else if (ll < -1)
1045 if (!indun)
1046 return "away below you";
1047 else
1048 return "below you";
1049 else if (!indun)
1050 return "in the distance";
1051 else
1052 return "just below";
1053 } else if (ll > 0) {
1054 if (ll > (8 + rn2(3)))
1055 if (!indun)
1056 return "far away";
1057 else
1058 return "far above";
1059 else if (ll > 1)
1060 if (!indun)
1061 return "away above you";
1062 else
1063 return "above you";
1064 else if (!indun)
1065 return "in the distance";
1066 else
1067 return "just above";
1068 } else if (!indun)
1069 return "in the distance";
1070 else
1071 return "near you";
1074 static const struct {
1075 const char *what;
1076 d_level *where;
1077 } level_detects[] = {
1078 { "Delphi", &oracle_level },
1079 { "Medusa's lair", &medusa_level },
1080 { "a castle", &stronghold_level },
1081 { "the Wizard of Yendor's tower", &wiz1_level },
1084 void
1085 use_crystal_ball(optr)
1086 struct obj **optr;
1088 char ch;
1089 int oops;
1090 struct obj *obj = *optr;
1092 if (Blind) {
1093 pline("Too bad you can't see %s.", the(xname(obj)));
1094 return;
1096 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
1097 if (oops && (obj->spe > 0)) {
1098 switch (rnd(obj->oartifact ? 4 : 5)) {
1099 case 1:
1100 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
1101 break;
1102 case 2:
1103 pline("%s you!", Tobjnam(obj, "confuse"));
1104 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
1105 break;
1106 case 3:
1107 if (!resists_blnd(&youmonst)) {
1108 pline("%s your vision!", Tobjnam(obj, "damage"));
1109 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
1110 if (!Blind)
1111 Your1(vision_clears);
1112 } else {
1113 pline("%s your vision.", Tobjnam(obj, "assault"));
1114 You("are unaffected!");
1116 break;
1117 case 4:
1118 pline("%s your mind!", Tobjnam(obj, "zap"));
1119 (void) make_hallucinated(
1120 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
1121 break;
1122 case 5:
1123 pline("%s!", Tobjnam(obj, "explode"));
1124 useup(obj);
1125 *optr = obj = 0; /* it's gone */
1126 /* physical damage cause by the shards and force */
1127 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
1128 KILLED_BY_AN);
1129 break;
1131 if (obj)
1132 consume_obj_charge(obj, TRUE);
1133 return;
1136 if (Hallucination) {
1137 if (!obj->spe) {
1138 pline("All you see is funky %s haze.", hcolor((char *) 0));
1139 } else {
1140 switch (rnd(6)) {
1141 case 1:
1142 You("grok some groovy globs of incandescent lava.");
1143 break;
1144 case 2:
1145 pline("Whoa! Psychedelic colors, %s!",
1146 poly_gender() == 1 ? "babe" : "dude");
1147 break;
1148 case 3:
1149 pline_The("crystal pulses with sinister %s light!",
1150 hcolor((char *) 0));
1151 break;
1152 case 4:
1153 You_see("goldfish swimming above fluorescent rocks.");
1154 break;
1155 case 5:
1156 You_see(
1157 "tiny snowflakes spinning around a miniature farmhouse.");
1158 break;
1159 default:
1160 pline("Oh wow... like a kaleidoscope!");
1161 break;
1163 consume_obj_charge(obj, TRUE);
1165 return;
1168 /* read a single character */
1169 if (flags.verbose)
1170 You("may look for an object or monster symbol.");
1171 ch = yn_function("What do you look for?", (char *) 0, '\0');
1172 /* Don't filter out ' ' here; it has a use */
1173 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
1174 if (flags.verbose)
1175 pline1(Never_mind);
1176 return;
1178 You("peer into %s...", the(xname(obj)));
1179 nomul(-rnd(10));
1180 multi_reason = "gazing into a crystal ball";
1181 nomovemsg = "";
1182 if (obj->spe <= 0) {
1183 pline_The("vision is unclear.");
1184 } else {
1185 int class, i;
1186 int ret = 0;
1188 makeknown(CRYSTAL_BALL);
1189 consume_obj_charge(obj, TRUE);
1191 /* special case: accept ']' as synonym for mimic
1192 * we have to do this before the def_char_to_objclass check
1194 if (ch == DEF_MIMIC_DEF)
1195 ch = DEF_MIMIC;
1197 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1198 ret = object_detect((struct obj *) 0, class);
1199 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1200 ret = monster_detect((struct obj *) 0, class);
1201 else if (iflags.bouldersym && (ch == iflags.bouldersym))
1202 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1203 else
1204 switch (ch) {
1205 case '^':
1206 ret = trap_detect((struct obj *) 0);
1207 break;
1208 default:
1209 i = rn2(SIZE(level_detects));
1210 You_see("%s, %s.", level_detects[i].what,
1211 level_distance(level_detects[i].where));
1212 ret = 0;
1213 break;
1216 if (ret) {
1217 if (!rn2(100)) /* make them nervous */
1218 You_see("the Wizard of Yendor gazing out at you.");
1219 else
1220 pline_The("vision is unclear.");
1223 return;
1226 STATIC_OVL void
1227 show_map_spot(x, y)
1228 register int x, y;
1230 struct rm *lev;
1231 struct trap *t;
1232 int oldglyph;
1234 if (Confusion && rn2(7))
1235 return;
1236 lev = &levl[x][y];
1238 lev->seenv = SVALL;
1240 /* Secret corridors are found, but not secret doors. */
1241 if (lev->typ == SCORR) {
1242 lev->typ = CORR;
1243 unblock_point(x, y);
1247 * Force the real background, then if it's not furniture and there's
1248 * a known trap there, display the trap, else if there was an object
1249 * shown there, redisplay the object. So during mapping, furniture
1250 * takes precedence over traps, which take precedence over objects,
1251 * opposite to how normal vision behaves.
1253 oldglyph = glyph_at(x, y);
1254 if (level.flags.hero_memory) {
1255 magic_map_background(x, y, 0);
1256 newsym(x, y); /* show it, if not blocked */
1257 } else {
1258 magic_map_background(x, y, 1); /* display it */
1260 if (!IS_FURNITURE(lev->typ)) {
1261 if ((t = t_at(x, y)) != 0 && t->tseen) {
1262 map_trap(t, 1);
1263 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1264 show_glyph(x, y, oldglyph);
1265 if (level.flags.hero_memory)
1266 lev->glyph = oldglyph;
1271 void
1272 do_mapping()
1274 register int zx, zy;
1275 boolean unconstrained;
1277 unconstrained = unconstrain_map();
1278 for (zx = 1; zx < COLNO; zx++)
1279 for (zy = 0; zy < ROWNO; zy++)
1280 show_map_spot(zx, zy);
1282 if (!level.flags.hero_memory || unconstrained) {
1283 flush_screen(1); /* flush temp screen */
1284 /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
1285 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1286 "anything of interest");
1287 docrt();
1289 reconstrain_map();
1290 exercise(A_WIS, TRUE);
1293 /* clairvoyance */
1294 void
1295 do_vicinity_map(sobj)
1296 struct obj *sobj; /* scroll--actually fake spellbook--object */
1298 register int zx, zy;
1299 struct monst *mtmp;
1300 boolean unconstrained, refresh = FALSE, mdetected = FALSE,
1301 extended = (sobj && sobj->blessed);
1302 int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
1303 hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
1304 lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
1305 hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
1306 ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
1308 unconstrained = unconstrain_map();
1309 for (zx = lo_x; zx <= hi_x; zx++)
1310 for (zy = lo_y; zy <= hi_y; zy++) {
1311 show_map_spot(zx, zy);
1313 if (extended && (mtmp = m_at(zx, zy)) != 0
1314 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
1315 int oldglyph = glyph_at(zx, zy);
1317 map_monst(mtmp, FALSE);
1318 if (glyph_at(zx, zy) != oldglyph)
1319 mdetected = TRUE;
1323 if (!level.flags.hero_memory || unconstrained || mdetected) {
1324 flush_screen(1); /* flush temp screen */
1325 if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
1326 ter_typ |= TER_MON;
1327 if (extended)
1328 EDetect_monsters |= I_SPECIAL;
1329 browse_map(ter_typ, "anything of interest");
1330 EDetect_monsters &= ~I_SPECIAL;
1331 refresh = TRUE;
1333 reconstrain_map();
1334 if (refresh)
1335 docrt();
1338 /* convert a secret door into a normal door */
1339 void
1340 cvt_sdoor_to_door(lev)
1341 struct rm *lev;
1343 int newmask = lev->doormask & ~WM_MASK;
1345 if (Is_rogue_level(&u.uz))
1346 /* rogue didn't have doors, only doorways */
1347 newmask = D_NODOOR;
1348 else
1349 /* newly exposed door is closed */
1350 if (!(newmask & D_LOCKED))
1351 newmask |= D_CLOSED;
1353 lev->typ = DOOR;
1354 lev->doormask = newmask;
1357 STATIC_PTR void
1358 findone(zx, zy, num)
1359 int zx, zy;
1360 genericptr_t num;
1362 register struct trap *ttmp;
1363 register struct monst *mtmp;
1365 if (levl[zx][zy].typ == SDOOR) {
1366 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1367 magic_map_background(zx, zy, 0);
1368 newsym(zx, zy);
1369 (*(int *) num)++;
1370 } else if (levl[zx][zy].typ == SCORR) {
1371 levl[zx][zy].typ = CORR;
1372 unblock_point(zx, zy);
1373 magic_map_background(zx, zy, 0);
1374 newsym(zx, zy);
1375 (*(int *) num)++;
1376 } else if ((ttmp = t_at(zx, zy)) != 0) {
1377 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1378 ttmp->tseen = 1;
1379 newsym(zx, zy);
1380 (*(int *) num)++;
1382 } else if ((mtmp = m_at(zx, zy)) != 0) {
1383 if (mtmp->m_ap_type) {
1384 seemimic(mtmp);
1385 (*(int *) num)++;
1387 if (mtmp->mundetected
1388 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1389 mtmp->mundetected = 0;
1390 newsym(zx, zy);
1391 (*(int *) num)++;
1393 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1394 map_invisible(zx, zy);
1395 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1396 unmap_object(zx, zy);
1397 newsym(zx, zy);
1398 (*(int *) num)++;
1402 STATIC_PTR void
1403 openone(zx, zy, num)
1404 int zx, zy;
1405 genericptr_t num;
1407 register struct trap *ttmp;
1408 register struct obj *otmp;
1409 int *num_p = (int *) num;
1411 if (OBJ_AT(zx, zy)) {
1412 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1413 if (Is_box(otmp) && otmp->olocked) {
1414 otmp->olocked = 0;
1415 (*num_p)++;
1418 /* let it fall to the next cases. could be on trap. */
1420 if (levl[zx][zy].typ == SDOOR
1421 || (levl[zx][zy].typ == DOOR
1422 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1423 if (levl[zx][zy].typ == SDOOR)
1424 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1425 if (levl[zx][zy].doormask & D_TRAPPED) {
1426 if (distu(zx, zy) < 3)
1427 b_trapped("door", 0);
1428 else
1429 Norep("You %s an explosion!",
1430 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1431 : "feel the shock of"));
1432 wake_nearto(zx, zy, 11 * 11);
1433 levl[zx][zy].doormask = D_NODOOR;
1434 } else
1435 levl[zx][zy].doormask = D_ISOPEN;
1436 unblock_point(zx, zy);
1437 newsym(zx, zy);
1438 (*num_p)++;
1439 } else if (levl[zx][zy].typ == SCORR) {
1440 levl[zx][zy].typ = CORR;
1441 unblock_point(zx, zy);
1442 newsym(zx, zy);
1443 (*num_p)++;
1444 } else if ((ttmp = t_at(zx, zy)) != 0) {
1445 struct monst *mon;
1446 boolean dummy; /* unneeded "you notice it arg" */
1448 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1449 ttmp->tseen = 1;
1450 newsym(zx, zy);
1451 (*num_p)++;
1453 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1454 if (openholdingtrap(mon, &dummy)
1455 || openfallingtrap(mon, TRUE, &dummy))
1456 (*num_p)++;
1457 } else if (find_drawbridge(&zx, &zy)) {
1458 /* make sure it isn't an open drawbridge */
1459 open_drawbridge(zx, zy);
1460 (*num_p)++;
1464 /* returns number of things found */
1466 findit()
1468 int num = 0;
1470 if (u.uswallow)
1471 return 0;
1472 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1473 return num;
1476 /* returns number of things found and opened */
1478 openit()
1480 int num = 0;
1482 if (u.uswallow) {
1483 if (is_animal(u.ustuck->data)) {
1484 if (Blind)
1485 pline("Its mouth opens!");
1486 else
1487 pline("%s opens its mouth!", Monnam(u.ustuck));
1489 expels(u.ustuck, u.ustuck->data, TRUE);
1490 return -1;
1493 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1494 return num;
1497 /* callback hack for overriding vision in do_clear_area() */
1498 boolean
1499 detecting(func)
1500 void FDECL((*func), (int, int, genericptr_t));
1502 return (func == findone || func == openone);
1505 void
1506 find_trap(trap)
1507 struct trap *trap;
1509 int tt = what_trap(trap->ttyp);
1510 boolean cleared = FALSE;
1512 trap->tseen = 1;
1513 exercise(A_WIS, TRUE);
1514 feel_newsym(trap->tx, trap->ty);
1516 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1517 /* There's too much clutter to see your find otherwise */
1518 cls();
1519 map_trap(trap, 1);
1520 display_self();
1521 cleared = TRUE;
1524 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1526 if (cleared) {
1527 display_nhwindow(WIN_MAP, TRUE); /* wait */
1528 docrt();
1532 STATIC_OVL int
1533 mfind0(mtmp, via_warning)
1534 struct monst *mtmp;
1535 boolean via_warning;
1537 xchar x = mtmp->mx,
1538 y = mtmp->my;
1540 if (via_warning && !warning_of(mtmp))
1541 return -1;
1543 if (mtmp->m_ap_type) {
1544 seemimic(mtmp);
1545 find:
1546 exercise(A_WIS, TRUE);
1547 if (!canspotmon(mtmp)) {
1548 if (glyph_is_invisible(levl[x][y].glyph)) {
1549 /* Found invisible monster in a square which already has
1550 * an 'I' in it. Logically, this should still take time
1551 * and lead to a return 1, but if we did that the player
1552 * would keep finding the same monster every turn.
1554 return -1;
1555 } else {
1556 You_feel("an unseen monster!");
1557 map_invisible(x, y);
1559 } else if (!sensemon(mtmp))
1560 You("find %s.",
1561 mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1562 return 1;
1564 if (!canspotmon(mtmp)) {
1565 if (mtmp->mundetected
1566 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1567 if (via_warning) {
1568 Your("warning senses cause you to take a second %s.",
1569 Blind ? "to check nearby" : "look close by");
1570 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1572 mtmp->mundetected = 0;
1573 newsym(x, y);
1574 goto find;
1576 return 0;
1580 dosearch0(aflag)
1581 register int aflag; /* intrinsic autosearch vs explicit searching */
1583 #ifdef GCC_BUG
1584 /* Some old versions of gcc seriously muck up nested loops. If you get
1585 * strange crashes while searching in a version compiled with gcc, try
1586 * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
1587 * the makefile).
1589 volatile xchar x, y;
1590 #else
1591 register xchar x, y;
1592 #endif
1593 register struct trap *trap;
1594 register struct monst *mtmp;
1596 if (u.uswallow) {
1597 if (!aflag)
1598 pline("What are you looking for? The exit?");
1599 } else {
1600 int fund = (uwep && uwep->oartifact
1601 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1603 if (ublindf && ublindf->otyp == LENSES && !Blind)
1604 fund += 2; /* JDS: lenses help searching */
1605 if (fund > 5)
1606 fund = 5;
1607 for (x = u.ux - 1; x < u.ux + 2; x++)
1608 for (y = u.uy - 1; y < u.uy + 2; y++) {
1609 if (!isok(x, y))
1610 continue;
1611 if (x == u.ux && y == u.uy)
1612 continue;
1614 if (Blind && !aflag)
1615 feel_location(x, y);
1616 if (levl[x][y].typ == SDOOR) {
1617 if (rnl(7 - fund))
1618 continue;
1619 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1620 exercise(A_WIS, TRUE);
1621 nomul(0);
1622 feel_location(x, y); /* make sure it shows up */
1623 You("find a hidden door.");
1624 } else if (levl[x][y].typ == SCORR) {
1625 if (rnl(7 - fund))
1626 continue;
1627 levl[x][y].typ = CORR;
1628 unblock_point(x, y); /* vision */
1629 exercise(A_WIS, TRUE);
1630 nomul(0);
1631 feel_newsym(x, y); /* make sure it shows up */
1632 You("find a hidden passage.");
1633 } else {
1634 /* Be careful not to find anything in an SCORR or SDOOR */
1635 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
1636 int mfres = mfind0(mtmp, 0);
1638 if (mfres == -1)
1639 continue;
1640 else if (mfres > 0)
1641 return mfres;
1644 /* see if an invisible monster has moved--if Blind,
1645 * feel_location() already did it
1647 if (!aflag && !mtmp && !Blind
1648 && glyph_is_invisible(levl[x][y].glyph)) {
1649 unmap_object(x, y);
1650 newsym(x, y);
1653 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
1654 nomul(0);
1655 if (trap->ttyp == STATUE_TRAP) {
1656 if (activate_statue_trap(trap, x, y, FALSE))
1657 exercise(A_WIS, TRUE);
1658 return 1;
1659 } else {
1660 find_trap(trap);
1666 return 1;
1669 /* the 's' command -- explicit searching */
1671 dosearch()
1673 return dosearch0(0);
1676 void
1677 warnreveal()
1679 int x, y;
1680 struct monst *mtmp;
1682 for (x = u.ux - 1; x <= u.ux + 1; x++)
1683 for (y = u.uy - 1; y <= u.uy + 1; y++) {
1684 if (!isok(x, y) || (x == u.ux && y == u.uy))
1685 continue;
1686 if ((mtmp = m_at(x, y)) != 0
1687 && warning_of(mtmp) && mtmp->mundetected)
1688 (void) mfind0(mtmp, 1); /* via_warning */
1692 /* Pre-map the sokoban levels */
1693 void
1694 sokoban_detect()
1696 register int x, y;
1697 register struct trap *ttmp;
1698 register struct obj *obj;
1700 /* Map the background and boulders */
1701 for (x = 1; x < COLNO; x++)
1702 for (y = 0; y < ROWNO; y++) {
1703 levl[x][y].seenv = SVALL;
1704 levl[x][y].waslit = TRUE;
1705 map_background(x, y, 1);
1706 if ((obj = sobj_at(BOULDER, x, y)) != 0)
1707 map_object(obj, 1);
1710 /* Map the traps */
1711 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1712 ttmp->tseen = 1;
1713 map_trap(ttmp, 1);
1714 /* set sokoban_rules when there is at least one pit or hole */
1715 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
1716 Sokoban = 1;
1720 STATIC_DCL int
1721 reveal_terrain_getglyph(x, y, full, swallowed, default_glyph, which_subset)
1722 int x, y, full;
1723 unsigned swallowed;
1724 int default_glyph, which_subset;
1726 int glyph, levl_glyph;
1727 uchar seenv;
1728 boolean keep_traps = (which_subset & TER_TRP) !=0,
1729 keep_objs = (which_subset & TER_OBJ) != 0,
1730 keep_mons = (which_subset & TER_MON) != 0;
1731 struct monst *mtmp;
1732 struct trap *t;
1734 /* for 'full', show the actual terrain for the entire level,
1735 otherwise what the hero remembers for seen locations with
1736 monsters, objects, and/or traps removed as caller dictates */
1737 seenv = (full || level.flags.hero_memory)
1738 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
1739 if (full) {
1740 levl[x][y].seenv = SVALL;
1741 glyph = back_to_glyph(x, y);
1742 levl[x][y].seenv = seenv;
1743 } else {
1744 levl_glyph = level.flags.hero_memory
1745 ? levl[x][y].glyph
1746 : seenv ? back_to_glyph(x, y): default_glyph;
1747 /* glyph_at() returns the displayed glyph, which might
1748 be a monster. levl[][].glyph contains the remembered
1749 glyph, which will never be a monster (unless it is
1750 the invisible monster glyph, which is handled like
1751 an object, replacing any object or trap at its spot) */
1752 glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
1753 if (keep_mons && x == u.ux && y == u.uy && swallowed)
1754 glyph = mon_to_glyph(u.ustuck);
1755 else if (((glyph_is_monster(glyph)
1756 || glyph_is_warning(glyph)) && !keep_mons)
1757 || glyph_is_swallow(glyph))
1758 glyph = levl_glyph;
1759 if (((glyph_is_object(glyph) && !keep_objs)
1760 || glyph_is_invisible(glyph))
1761 && keep_traps && !covers_traps(x, y)) {
1762 if ((t = t_at(x, y)) != 0 && t->tseen)
1763 glyph = trap_to_glyph(t);
1765 if ((glyph_is_object(glyph) && !keep_objs)
1766 || (glyph_is_trap(glyph) && !keep_traps)
1767 || glyph_is_invisible(glyph)) {
1768 if (!seenv) {
1769 glyph = default_glyph;
1770 } else if (lastseentyp[x][y] == levl[x][y].typ) {
1771 glyph = back_to_glyph(x, y);
1772 } else {
1773 /* look for a mimic here posing as furniture;
1774 if we don't find one, we'll have to fake it */
1775 if ((mtmp = m_at(x, y)) != 0
1776 && mtmp->m_ap_type == M_AP_FURNITURE) {
1777 glyph = cmap_to_glyph(mtmp->mappearance);
1778 } else {
1779 /* we have a topology type but we want a screen
1780 symbol in order to derive a glyph; some screen
1781 symbols need the flags field of levl[][] in
1782 addition to the type (to disambiguate STAIRS to
1783 S_upstair or S_dnstair, for example; current
1784 flags might not be intended for remembered type,
1785 but we've got no other choice) */
1786 schar save_typ = levl[x][y].typ;
1788 levl[x][y].typ = lastseentyp[x][y];
1789 glyph = back_to_glyph(x, y);
1790 levl[x][y].typ = save_typ;
1795 if (glyph == cmap_to_glyph(S_darkroom))
1796 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
1797 return glyph;
1800 void
1801 dump_map()
1803 int x, y, glyph;
1804 int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON;
1805 int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1806 char buf[BUFSZ];
1808 for (y = 0; y < ROWNO; y++) {
1809 for (x = 1; x < COLNO; x++) {
1810 int ch, color;
1811 unsigned special;
1813 glyph = reveal_terrain_getglyph(x,y, FALSE, u.uswallow,
1814 default_glyph, subset);
1815 (void) mapglyph(glyph, &ch, &color, &special, x, y);
1816 buf[x-1] = ch;
1818 buf[x-2] = '\0';
1819 putstr(0,0, buf);
1823 /* idea from crawl; show known portion of map without any monsters,
1824 objects, or traps occluding the view of the underlying terrain */
1825 void
1826 reveal_terrain(full, which_subset)
1827 int full; /* wizard|explore modes allow player to request full map */
1828 int which_subset; /* when not full, whether to suppress objs and/or traps */
1830 if ((Hallucination || Stunned || Confusion) && !full) {
1831 You("are too disoriented for this.");
1832 } else {
1833 int x, y, glyph, default_glyph;
1834 char buf[BUFSZ];
1835 /* there is a TER_MAP bit too; we always show map regardless of it */
1836 boolean keep_traps = (which_subset & TER_TRP) !=0,
1837 keep_objs = (which_subset & TER_OBJ) != 0,
1838 keep_mons = (which_subset & TER_MON) != 0; /* not used */
1839 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
1841 if (unconstrain_map())
1842 docrt();
1843 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1845 for (x = 1; x < COLNO; x++)
1846 for (y = 0; y < ROWNO; y++) {
1847 glyph = reveal_terrain_getglyph(x,y, full, swallowed,
1848 default_glyph, which_subset);
1849 show_glyph(x, y, glyph);
1852 /* hero's location is not highlighted, but getpos() starts with
1853 cursor there, and after moving it anywhere '@' moves it back */
1854 flush_screen(1);
1855 if (full) {
1856 Strcpy(buf, "underlying terrain");
1857 } else {
1858 Strcpy(buf, "known terrain");
1859 if (keep_traps)
1860 Sprintf(eos(buf), "%s traps",
1861 (keep_objs || keep_mons) ? "," : " and");
1862 if (keep_objs)
1863 Sprintf(eos(buf), "%s%s objects",
1864 (keep_traps || keep_mons) ? "," : "",
1865 keep_mons ? "" : " and");
1866 if (keep_mons)
1867 Sprintf(eos(buf), "%s and monsters",
1868 (keep_traps || keep_objs) ? "," : "");
1870 pline("Showing %s only...", buf);
1872 /* allow player to move cursor around and get autodescribe feedback
1873 based on what is visible now rather than what is on 'real' map */
1874 which_subset |= TER_MAP; /* guarantee non-zero */
1875 browse_map(which_subset, "anything of interest");
1877 reconstrain_map();
1878 docrt(); /* redraw the screen, restoring regular map */
1879 if (Underwater)
1880 under_water(2);
1881 if (u.uburied)
1882 under_ground(2);
1884 return;
1887 /*detect.c*/