Add end-of-game dumplogs
[aNetHack.git] / src / detect.c
blob8a5af31ddfb5bb9e89af9017f50766faa9777548
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));
28 STATIC_DCL int FDECL(reveal_terrain_getglyph, (int, int, int, unsigned, int, int));
30 /* bring hero out from underwater or underground or being engulfed;
31 return True iff any change occurred */
32 STATIC_OVL boolean
33 unconstrain_map()
35 boolean res = u.uinwater || u.uburied || u.uswallow;
37 /* bring Underwater, buried, or swallowed hero to normal map */
38 iflags.save_uinwater = u.uinwater, u.uinwater = 0;
39 iflags.save_uburied = u.uburied, u.uburied = 0;
40 iflags.save_uswallow = u.uswallow, u.uswallow = 0;
42 return res;
45 /* put hero back underwater or underground or engulfed */
46 STATIC_OVL void
47 reconstrain_map()
49 u.uinwater = iflags.save_uinwater, iflags.save_uinwater = 0;
50 u.uburied = iflags.save_uburied, iflags.save_uburied = 0;
51 u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0;
54 /* use getpos()'s 'autodescribe' to view whatever is currently shown on map */
55 STATIC_DCL void
56 browse_map(ter_typ, ter_explain)
57 int ter_typ;
58 const char *ter_explain;
60 coord dummy_pos; /* don't care whether player actually picks a spot */
62 dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */
63 iflags.autodescribe = TRUE;
64 iflags.terrainmode = ter_typ;
65 getpos(&dummy_pos, FALSE, ter_explain);
66 iflags.terrainmode = 0;
67 /* leave iflags.autodescribe 'on' even if previously 'off' */
70 /* extracted from monster_detection() so can be shared by do_vicinity_map() */
71 STATIC_DCL void
72 map_monst(mtmp, showtail)
73 struct monst *mtmp;
74 boolean showtail;
76 if (def_monsyms[(int) mtmp->data->mlet].sym == ' ')
77 show_glyph(mtmp->mx, mtmp->my, detected_mon_to_glyph(mtmp));
78 else
79 show_glyph(mtmp->mx, mtmp->my,
80 mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
82 if (showtail && mtmp->data == &mons[PM_LONG_WORM])
83 detect_wsegs(mtmp, 0);
86 /* this is checking whether a trap symbol represents a trapped chest,
87 not whether a trapped chest is actually present */
88 boolean
89 trapped_chest_at(ttyp, x, y)
90 int ttyp;
91 int x, y;
93 struct monst *mtmp;
94 struct obj *otmp;
96 if (!glyph_is_trap(glyph_at(x, y)))
97 return FALSE;
98 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
99 return FALSE;
102 * TODO? We should check containers recursively like the trap
103 * detecting routine does. Chests and large boxes do not nest in
104 * themselves or each other, but could be contained inside statues.
106 * For farlook, we should also check for buried containers, but
107 * for '^' command to examine adjacent trap glyph, we shouldn't.
110 /* on map, presence of any trappable container will do */
111 if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y))
112 return TRUE;
113 /* in inventory, we need to find one which is actually trapped */
114 if (x == u.ux && y == u.uy) {
115 for (otmp = invent; otmp; otmp = otmp->nobj)
116 if (Is_box(otmp) && otmp->otrapped)
117 return TRUE;
118 if (u.usteed) { /* steed isn't on map so won't be found by m_at() */
119 for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj)
120 if (Is_box(otmp) && otmp->otrapped)
121 return TRUE;
124 if ((mtmp = m_at(x, y)) != 0)
125 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
126 if (Is_box(otmp) && otmp->otrapped)
127 return TRUE;
128 return FALSE;
131 /* this is checking whether a trap symbol represents a trapped door,
132 not whether the door here is actually trapped */
133 boolean
134 trapped_door_at(ttyp, x, y)
135 int ttyp;
136 int x, y;
138 struct rm *lev;
140 if (!glyph_is_trap(glyph_at(x, y)))
141 return FALSE;
142 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
143 return FALSE;
144 lev = &levl[x][y];
145 if (!IS_DOOR(lev->typ))
146 return FALSE;
147 if ((lev->doormask & (D_NODOOR | D_BROKEN | D_ISOPEN)) != 0
148 && trapped_chest_at(ttyp, x, y))
149 return FALSE;
150 return TRUE;
153 /* recursively search obj for an object in class oclass, return 1st found */
154 struct obj *
155 o_in(obj, oclass)
156 struct obj *obj;
157 char oclass;
159 register struct obj *otmp;
160 struct obj *temp;
162 if (obj->oclass == oclass)
163 return obj;
165 if (Has_contents(obj)) {
166 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
167 if (otmp->oclass == oclass)
168 return otmp;
169 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)) != 0)
170 return temp;
172 return (struct obj *) 0;
175 /* Recursively search obj for an object made of specified material.
176 * Return first found.
178 struct obj *
179 o_material(obj, material)
180 struct obj *obj;
181 unsigned material;
183 register struct obj *otmp;
184 struct obj *temp;
186 if (objects[obj->otyp].oc_material == material)
187 return obj;
189 if (Has_contents(obj)) {
190 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
191 if (objects[otmp->otyp].oc_material == material)
192 return otmp;
193 else if (Has_contents(otmp)
194 && (temp = o_material(otmp, material)) != 0)
195 return temp;
197 return (struct obj *) 0;
200 STATIC_OVL void
201 do_dknown_of(obj)
202 struct obj *obj;
204 struct obj *otmp;
206 obj->dknown = 1;
207 if (Has_contents(obj)) {
208 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
209 do_dknown_of(otmp);
213 /* Check whether the location has an outdated object displayed on it. */
214 STATIC_OVL boolean
215 check_map_spot(x, y, oclass, material)
216 int x, y;
217 char oclass;
218 unsigned material;
220 int glyph;
221 register struct obj *otmp;
222 register struct monst *mtmp;
224 glyph = glyph_at(x, y);
225 if (glyph_is_object(glyph)) {
226 /* there's some object shown here */
227 if (oclass == ALL_CLASSES) {
228 return (boolean) !(level.objects[x][y] /* stale if nothing here */
229 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
230 } else {
231 if (material
232 && objects[glyph_to_obj(glyph)].oc_material == material) {
233 /* object shown here is of interest because material matches */
234 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
235 if (o_material(otmp, GOLD))
236 return FALSE;
237 /* didn't find it; perhaps a monster is carrying it */
238 if ((mtmp = m_at(x, y)) != 0) {
239 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
240 if (o_material(otmp, GOLD))
241 return FALSE;
243 /* detection indicates removal of this object from the map */
244 return TRUE;
246 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
247 /* obj shown here is of interest because its class matches */
248 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
249 if (o_in(otmp, oclass))
250 return FALSE;
251 /* didn't find it; perhaps a monster is carrying it */
252 if ((mtmp = m_at(x, y)) != 0) {
253 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
254 if (o_in(otmp, oclass))
255 return FALSE;
257 /* detection indicates removal of this object from the map */
258 return TRUE;
262 return FALSE;
266 * When doing detection, remove stale data from the map display (corpses
267 * rotted away, objects carried away by monsters, etc) so that it won't
268 * reappear after the detection has completed. Return true if noticeable
269 * change occurs.
271 STATIC_OVL boolean
272 clear_stale_map(oclass, material)
273 char oclass;
274 unsigned material;
276 register int zx, zy;
277 boolean change_made = FALSE;
279 for (zx = 1; zx < COLNO; zx++)
280 for (zy = 0; zy < ROWNO; zy++)
281 if (check_map_spot(zx, zy, oclass, material)) {
282 unmap_object(zx, zy);
283 change_made = TRUE;
286 return change_made;
289 /* look for gold, on the floor or in monsters' possession */
291 gold_detect(sobj)
292 register struct obj *sobj;
294 register struct obj *obj;
295 register struct monst *mtmp;
296 struct obj gold, *temp = 0;
297 boolean stale, ugold = FALSE, steedgold = FALSE;
298 int ter_typ = TER_DETECT | TER_OBJ;
300 known = stale = clear_stale_map(COIN_CLASS,
301 (unsigned) (sobj->blessed ? GOLD : 0));
303 /* look for gold carried by monsters (might be in a container) */
304 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
305 if (DEADMONSTER(mtmp))
306 continue; /* probably not needed in this case but... */
307 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
308 if (mtmp == u.usteed) {
309 steedgold = TRUE;
310 } else {
311 known = TRUE;
312 goto outgoldmap; /* skip further searching */
314 } else {
315 for (obj = mtmp->minvent; obj; obj = obj->nobj)
316 if ((sobj->blessed && o_material(obj, GOLD))
317 || o_in(obj, COIN_CLASS)) {
318 if (mtmp == u.usteed) {
319 steedgold = TRUE;
320 } else {
321 known = TRUE;
322 goto outgoldmap; /* skip further searching */
328 /* look for gold objects */
329 for (obj = fobj; obj; obj = obj->nobj) {
330 if (sobj->blessed && o_material(obj, GOLD)) {
331 known = TRUE;
332 if (obj->ox != u.ux || obj->oy != u.uy)
333 goto outgoldmap;
334 } else if (o_in(obj, COIN_CLASS)) {
335 known = TRUE;
336 if (obj->ox != u.ux || obj->oy != u.uy)
337 goto outgoldmap;
341 if (!known) {
342 /* no gold found on floor or monster's inventory.
343 adjust message if you have gold in your inventory */
344 if (sobj) {
345 char buf[BUFSZ];
347 if (youmonst.data == &mons[PM_GOLD_GOLEM])
348 Sprintf(buf, "You feel like a million %s!", currency(2L));
349 else if (money_cnt(invent) || hidden_gold())
350 Strcpy(buf,
351 "You feel worried about your future financial situation.");
352 else if (steedgold)
353 Sprintf(buf, "You feel interested in %s financial situation.",
354 s_suffix(x_monnam(u.usteed,
355 u.usteed->mtame ? ARTICLE_YOUR
356 : ARTICLE_THE,
357 (char *) 0,
358 SUPPRESS_SADDLE, FALSE)));
359 else
360 Strcpy(buf, "You feel materially poor.");
362 strange_feeling(sobj, buf);
364 return 1;
366 /* only under me - no separate display required */
367 if (stale)
368 docrt();
369 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
370 return 0;
372 outgoldmap:
373 cls();
375 (void) unconstrain_map();
376 /* Discover gold locations. */
377 for (obj = fobj; obj; obj = obj->nobj) {
378 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
379 if (temp != obj) {
380 temp->ox = obj->ox;
381 temp->oy = obj->oy;
383 map_object(temp, 1);
384 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
385 if (temp != obj) {
386 temp->ox = obj->ox;
387 temp->oy = obj->oy;
389 map_object(temp, 1);
391 if (temp && temp->ox == u.ux && temp->oy == u.uy)
392 ugold = TRUE;
394 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
395 if (DEADMONSTER(mtmp))
396 continue; /* probably overkill here */
397 temp = 0;
398 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
399 gold = zeroobj; /* ensure oextra is cleared too */
400 gold.otyp = GOLD_PIECE;
401 gold.quan = (long) rnd(10); /* usually more than 1 */
402 gold.ox = mtmp->mx;
403 gold.oy = mtmp->my;
404 map_object(&gold, 1);
405 temp = &gold;
406 } else {
407 for (obj = mtmp->minvent; obj; obj = obj->nobj)
408 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
409 temp->ox = mtmp->mx;
410 temp->oy = mtmp->my;
411 map_object(temp, 1);
412 break;
413 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
414 temp->ox = mtmp->mx;
415 temp->oy = mtmp->my;
416 map_object(temp, 1);
417 break;
420 if (temp && temp->ox == u.ux && temp->oy == u.uy)
421 ugold = TRUE;
423 if (!ugold) {
424 newsym(u.ux, u.uy);
425 ter_typ |= TER_MON; /* so autodescribe will recognize hero */
427 You_feel("very greedy, and sense gold!");
428 exercise(A_WIS, TRUE);
430 browse_map(ter_typ, "gold");
432 reconstrain_map();
433 docrt();
434 if (Underwater)
435 under_water(2);
436 if (u.uburied)
437 under_ground(2);
438 return 0;
441 /* returns 1 if nothing was detected */
442 /* returns 0 if something was detected */
444 food_detect(sobj)
445 register struct obj *sobj;
447 register struct obj *obj;
448 register struct monst *mtmp;
449 register int ct = 0, ctu = 0;
450 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
451 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
452 const char *what = confused ? something : "food";
454 stale = clear_stale_map(oclass, 0);
455 if (u.usteed) /* some situations leave steed with stale coordinates */
456 u.usteed->mx = u.ux, u.usteed->my = u.uy;
458 for (obj = fobj; obj; obj = obj->nobj)
459 if (o_in(obj, oclass)) {
460 if (obj->ox == u.ux && obj->oy == u.uy)
461 ctu++;
462 else
463 ct++;
465 for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) {
466 /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */
467 for (obj = mtmp->minvent; obj; obj = obj->nobj)
468 if (o_in(obj, oclass)) {
469 if (mtmp->mx == u.ux && mtmp->my == u.uy)
470 ctu++; /* steed or an engulfer with inventory */
471 else
472 ct++;
473 break;
477 if (!ct && !ctu) {
478 known = stale && !confused;
479 if (stale) {
480 docrt();
481 You("sense a lack of %s nearby.", what);
482 if (sobj && sobj->blessed) {
483 if (!u.uedibility)
484 Your("%s starts to tingle.", body_part(NOSE));
485 u.uedibility = 1;
487 } else if (sobj) {
488 char buf[BUFSZ];
490 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
491 (sobj->blessed && !u.uedibility)
492 ? " then starts to tingle"
493 : "");
494 if (sobj->blessed && !u.uedibility) {
495 boolean savebeginner = flags.beginner;
497 flags.beginner = FALSE; /* prevent non-delivery of message */
498 strange_feeling(sobj, buf);
499 flags.beginner = savebeginner;
500 u.uedibility = 1;
501 } else
502 strange_feeling(sobj, buf);
504 return !stale;
505 } else if (!ct) {
506 known = TRUE;
507 You("%s %s nearby.", sobj ? "smell" : "sense", what);
508 if (sobj && sobj->blessed) {
509 if (!u.uedibility)
510 pline("Your %s starts to tingle.", body_part(NOSE));
511 u.uedibility = 1;
513 } else {
514 struct obj *temp;
515 int ter_typ = TER_DETECT | TER_OBJ;
517 known = TRUE;
518 cls();
519 (void) unconstrain_map();
520 for (obj = fobj; obj; obj = obj->nobj)
521 if ((temp = o_in(obj, oclass)) != 0) {
522 if (temp != obj) {
523 temp->ox = obj->ox;
524 temp->oy = obj->oy;
526 map_object(temp, 1);
528 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
529 /* no DEADMONSTER() check needed -- dmons never have inventory */
530 for (obj = mtmp->minvent; obj; obj = obj->nobj)
531 if ((temp = o_in(obj, oclass)) != 0) {
532 temp->ox = mtmp->mx;
533 temp->oy = mtmp->my;
534 map_object(temp, 1);
535 break; /* skip rest of this monster's inventory */
537 if (!ctu) {
538 newsym(u.ux, u.uy);
539 ter_typ |= TER_MON; /* for autodescribe of self */
541 if (sobj) {
542 if (sobj->blessed) {
543 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
544 u.uedibility ? "continues" : "starts", what);
545 u.uedibility = 1;
546 } else
547 Your("%s tingles and you smell %s.", body_part(NOSE), what);
548 } else
549 You("sense %s.", what);
550 exercise(A_WIS, TRUE);
552 browse_map(ter_typ, "food");
554 reconstrain_map();
555 docrt();
556 if (Underwater)
557 under_water(2);
558 if (u.uburied)
559 under_ground(2);
561 return 0;
565 * Used for scrolls, potions, spells, and crystal balls. Returns:
567 * 1 - nothing was detected
568 * 0 - something was detected
571 object_detect(detector, class)
572 struct obj *detector; /* object doing the detecting */
573 int class; /* an object class, 0 for all */
575 register int x, y;
576 char stuff[BUFSZ];
577 int is_cursed = (detector && detector->cursed);
578 int do_dknown = (detector && (detector->oclass == POTION_CLASS
579 || detector->oclass == SPBOOK_CLASS)
580 && detector->blessed);
581 int ct = 0, ctu = 0;
582 register struct obj *obj, *otmp = (struct obj *) 0;
583 register struct monst *mtmp;
584 int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ;
586 if (class < 0 || class >= MAXOCLASSES) {
587 impossible("object_detect: illegal class %d", class);
588 class = 0;
591 /* Special boulder symbol check - does the class symbol happen
592 * to match iflags.bouldersym which is a user-defined?
593 * If so, that means we aren't sure what they really wanted to
594 * detect. Rather than trump anything, show both possibilities.
595 * We can exclude checking the buried obj chain for boulders below.
597 sym = class ? def_oc_syms[class].sym : 0;
598 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
599 boulder = ROCK_CLASS;
601 if (Hallucination || (Confusion && class == SCROLL_CLASS))
602 Strcpy(stuff, something);
603 else
604 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
605 if (boulder && class != ROCK_CLASS)
606 Strcat(stuff, " and/or large stones");
608 if (do_dknown)
609 for (obj = invent; obj; obj = obj->nobj)
610 do_dknown_of(obj);
612 for (obj = fobj; obj; obj = obj->nobj) {
613 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
614 if (obj->ox == u.ux && obj->oy == u.uy)
615 ctu++;
616 else
617 ct++;
619 if (do_dknown)
620 do_dknown_of(obj);
623 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
624 if (!class || o_in(obj, class)) {
625 if (obj->ox == u.ux && obj->oy == u.uy)
626 ctu++;
627 else
628 ct++;
630 if (do_dknown)
631 do_dknown_of(obj);
634 if (u.usteed)
635 u.usteed->mx = u.ux, u.usteed->my = u.uy;
637 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
638 if (DEADMONSTER(mtmp))
639 continue;
640 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
641 if ((!class && !boulder) || o_in(obj, class)
642 || o_in(obj, boulder))
643 ct++;
644 if (do_dknown)
645 do_dknown_of(obj);
647 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
648 && (!class || class == objects[mtmp->mappearance].oc_class))
649 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
650 ct++;
651 break;
655 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
656 if (!ctu) {
657 if (detector)
658 strange_feeling(detector, "You feel a lack of something.");
659 return 1;
662 You("sense %s nearby.", stuff);
663 return 0;
666 cls();
668 (void) unconstrain_map();
670 * Map all buried objects first.
672 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
673 if (!class || (otmp = o_in(obj, class)) != 0) {
674 if (class) {
675 if (otmp != obj) {
676 otmp->ox = obj->ox;
677 otmp->oy = obj->oy;
679 map_object(otmp, 1);
680 } else
681 map_object(obj, 1);
684 * If we are mapping all objects, map only the top object of a pile or
685 * the first object in a monster's inventory. Otherwise, go looking
686 * for a matching object class and display the first one encountered
687 * at each location.
689 * Objects on the floor override buried objects.
691 for (x = 1; x < COLNO; x++)
692 for (y = 0; y < ROWNO; y++)
693 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
694 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
695 || (otmp = o_in(obj, boulder)) != 0) {
696 if (class || boulder) {
697 if (otmp != obj) {
698 otmp->ox = obj->ox;
699 otmp->oy = obj->oy;
701 map_object(otmp, 1);
702 } else
703 map_object(obj, 1);
704 break;
707 /* Objects in the monster's inventory override floor objects. */
708 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
709 if (DEADMONSTER(mtmp))
710 continue;
711 for (obj = mtmp->minvent; obj; obj = obj->nobj)
712 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
713 || (otmp = o_in(obj, boulder)) != 0) {
714 if (!class && !boulder)
715 otmp = obj;
716 otmp->ox = mtmp->mx; /* at monster location */
717 otmp->oy = mtmp->my;
718 map_object(otmp, 1);
719 break;
721 /* Allow a mimic to override the detected objects it is carrying. */
722 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
723 && (!class || class == objects[mtmp->mappearance].oc_class)) {
724 struct obj temp;
726 temp = zeroobj;
727 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
728 temp.quan = 1L;
729 temp.ox = mtmp->mx;
730 temp.oy = mtmp->my;
731 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
732 map_object(&temp, 1);
733 } else if (findgold(mtmp->minvent)
734 && (!class || class == COIN_CLASS)) {
735 struct obj gold;
737 gold = zeroobj; /* ensure oextra is cleared too */
738 gold.otyp = GOLD_PIECE;
739 gold.quan = (long) rnd(10); /* usually more than 1 */
740 gold.ox = mtmp->mx;
741 gold.oy = mtmp->my;
742 map_object(&gold, 1);
745 if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
746 newsym(u.ux, u.uy);
747 ter_typ |= TER_MON;
749 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
751 if (!ct)
752 display_nhwindow(WIN_MAP, TRUE);
753 else
754 browse_map(ter_typ, "object");
756 reconstrain_map();
757 docrt(); /* this will correctly reset vision */
758 if (Underwater)
759 under_water(2);
760 if (u.uburied)
761 under_ground(2);
762 return 0;
766 * Used by: crystal balls, potions, fountains
768 * Returns 1 if nothing was detected.
769 * Returns 0 if something was detected.
772 monster_detect(otmp, mclass)
773 register struct obj *otmp; /* detecting object (if any) */
774 int mclass; /* monster class, 0 for all */
776 register struct monst *mtmp;
777 int mcnt = 0;
779 /* Note: This used to just check fmon for a non-zero value
780 * but in versions since 3.3.0 fmon can test TRUE due to the
781 * presence of dmons, so we have to find at least one
782 * with positive hit-points to know for sure.
784 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
785 if (!DEADMONSTER(mtmp)) {
786 mcnt++;
787 break;
790 if (!mcnt) {
791 if (otmp)
792 strange_feeling(otmp, Hallucination
793 ? "You get the heebie jeebies."
794 : "You feel threatened.");
795 return 1;
796 } else {
797 boolean unconstrained, woken = FALSE;
798 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
800 cls();
801 unconstrained = unconstrain_map();
802 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
803 if (DEADMONSTER(mtmp))
804 continue;
805 if (!mclass || mtmp->data->mlet == mclass
806 || (mtmp->data == &mons[PM_LONG_WORM]
807 && mclass == S_WORM_TAIL))
808 map_monst(mtmp, TRUE);
810 if (otmp && otmp->cursed
811 && (mtmp->msleeping || !mtmp->mcanmove)) {
812 mtmp->msleeping = mtmp->mfrozen = 0;
813 mtmp->mcanmove = 1;
814 woken = TRUE;
817 if (!swallowed)
818 display_self();
819 You("sense the presence of monsters.");
820 if (woken)
821 pline("Monsters sense the presence of you.");
823 if ((otmp && otmp->blessed) && !unconstrained) {
824 /* persistent detection--just show updated map */
825 display_nhwindow(WIN_MAP, TRUE);
826 } else {
827 /* one-shot detection--allow player to move cursor around and
828 get autodescribe feedback */
829 EDetect_monsters |= I_SPECIAL;
830 browse_map(TER_DETECT | TER_MON, "monster of interest");
831 EDetect_monsters &= ~I_SPECIAL;
834 reconstrain_map();
835 docrt(); /* redraw the screen to remove unseen monsters from map */
836 if (Underwater)
837 under_water(2);
838 if (u.uburied)
839 under_ground(2);
841 return 0;
844 STATIC_OVL void
845 sense_trap(trap, x, y, src_cursed)
846 struct trap *trap;
847 xchar x, y;
848 int src_cursed;
850 if (Hallucination || src_cursed) {
851 struct obj obj; /* fake object */
853 obj = zeroobj;
854 if (trap) {
855 obj.ox = trap->tx;
856 obj.oy = trap->ty;
857 } else {
858 obj.ox = x;
859 obj.oy = y;
861 obj.otyp = !Hallucination ? GOLD_PIECE : random_object();
862 obj.quan = (long) ((obj.otyp == GOLD_PIECE) ? rnd(10)
863 : objects[obj.otyp].oc_merge ? rnd(2) : 1);
864 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
865 map_object(&obj, 1);
866 } else if (trap) {
867 map_trap(trap, 1);
868 trap->tseen = 1;
869 } else { /* trapped door or trapped chest */
870 struct trap temp_trap; /* fake trap */
872 (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap);
873 temp_trap.tx = x;
874 temp_trap.ty = y;
875 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
876 map_trap(&temp_trap, 1);
880 #define OTRAP_NONE 0 /* nothing found */
881 #define OTRAP_HERE 1 /* found at hero's location */
882 #define OTRAP_THERE 2 /* found at any other location */
884 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
885 2 if found at some other spot, 3 if both, 0 otherwise; optionally
886 update the map to show where such traps were found */
887 STATIC_OVL int
888 detect_obj_traps(objlist, show_them, how)
889 struct obj *objlist;
890 boolean show_them;
891 int how; /* 1 for misleading map feedback */
893 struct obj *otmp;
894 xchar x, y;
895 int result = OTRAP_NONE;
898 * TODO? Display locations of unarmed land mine and beartrap objects.
899 * If so, should they be displayed as objects or as traps?
902 for (otmp = objlist; otmp; otmp = otmp->nobj) {
903 if (Is_box(otmp) && otmp->otrapped
904 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
905 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
906 if (show_them)
907 sense_trap((struct trap *) 0, x, y, how);
909 if (Has_contents(otmp))
910 result |= detect_obj_traps(otmp->cobj, show_them, how);
912 return result;
915 /* the detections are pulled out so they can
916 * also be used in the crystal ball routine
917 * returns 1 if nothing was detected
918 * returns 0 if something was detected
921 trap_detect(sobj)
922 struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
924 register struct trap *ttmp;
925 struct monst *mon;
926 int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP;
927 int cursed_src = sobj && sobj->cursed;
928 boolean found = FALSE;
929 coord cc;
931 if (u.usteed)
932 u.usteed->mx = u.ux, u.usteed->my = u.uy;
934 /* floor/ceiling traps */
935 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
936 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
937 goto outtrapmap;
938 else
939 found = TRUE;
941 /* chest traps (might be buried or carried) */
942 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
943 if (tr & OTRAP_THERE)
944 goto outtrapmap;
945 else
946 found = TRUE;
948 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0)) != OTRAP_NONE) {
949 if (tr & OTRAP_THERE)
950 goto outtrapmap;
951 else
952 found = TRUE;
954 for (mon = fmon; mon; mon = mon->nmon) {
955 if (DEADMONSTER(mon))
956 continue;
957 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
958 if (tr & OTRAP_THERE)
959 goto outtrapmap;
960 else
961 found = TRUE;
964 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
965 found = TRUE;
966 /* door traps */
967 for (door = 0; door < doorindex; door++) {
968 cc = doors[door];
969 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
970 if (cc.x != u.ux || cc.y != u.uy)
971 goto outtrapmap;
972 else
973 found = TRUE;
976 if (!found) {
977 char buf[BUFSZ];
979 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
980 strange_feeling(sobj, buf);
981 return 1;
983 /* traps exist, but only under me - no separate display required */
984 Your("%s itch.", makeplural(body_part(TOE)));
985 return 0;
987 outtrapmap:
988 cls();
990 (void) unconstrain_map();
991 /* show chest traps first, so that subsequent floor trap display
992 will override if both types are present at the same location */
993 (void) detect_obj_traps(fobj, TRUE, cursed_src);
994 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
995 for (mon = fmon; mon; mon = mon->nmon) {
996 if (DEADMONSTER(mon))
997 continue;
998 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
1000 (void) detect_obj_traps(invent, TRUE, cursed_src);
1002 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
1003 sense_trap(ttmp, 0, 0, cursed_src);
1005 for (door = 0; door < doorindex; door++) {
1006 cc = doors[door];
1007 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
1008 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
1011 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
1012 glyph = glyph_at(u.ux, u.uy);
1013 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) {
1014 newsym(u.ux, u.uy);
1015 ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
1017 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
1019 browse_map(ter_typ, "trap of interest");
1021 reconstrain_map();
1022 docrt(); /* redraw the screen to remove unseen traps from the map */
1023 if (Underwater)
1024 under_water(2);
1025 if (u.uburied)
1026 under_ground(2);
1027 return 0;
1030 const char *
1031 level_distance(where)
1032 d_level *where;
1034 register schar ll = depth(&u.uz) - depth(where);
1035 register boolean indun = (u.uz.dnum == where->dnum);
1037 if (ll < 0) {
1038 if (ll < (-8 - rn2(3)))
1039 if (!indun)
1040 return "far away";
1041 else
1042 return "far below";
1043 else if (ll < -1)
1044 if (!indun)
1045 return "away below you";
1046 else
1047 return "below you";
1048 else if (!indun)
1049 return "in the distance";
1050 else
1051 return "just below";
1052 } else if (ll > 0) {
1053 if (ll > (8 + rn2(3)))
1054 if (!indun)
1055 return "far away";
1056 else
1057 return "far above";
1058 else if (ll > 1)
1059 if (!indun)
1060 return "away above you";
1061 else
1062 return "above you";
1063 else if (!indun)
1064 return "in the distance";
1065 else
1066 return "just above";
1067 } else if (!indun)
1068 return "in the distance";
1069 else
1070 return "near you";
1073 static const struct {
1074 const char *what;
1075 d_level *where;
1076 } level_detects[] = {
1077 { "Delphi", &oracle_level },
1078 { "Medusa's lair", &medusa_level },
1079 { "a castle", &stronghold_level },
1080 { "the Wizard of Yendor's tower", &wiz1_level },
1083 void
1084 use_crystal_ball(optr)
1085 struct obj **optr;
1087 char ch;
1088 int oops;
1089 struct obj *obj = *optr;
1091 if (Blind) {
1092 pline("Too bad you can't see %s.", the(xname(obj)));
1093 return;
1095 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
1096 if (oops && (obj->spe > 0)) {
1097 switch (rnd(obj->oartifact ? 4 : 5)) {
1098 case 1:
1099 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
1100 break;
1101 case 2:
1102 pline("%s you!", Tobjnam(obj, "confuse"));
1103 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
1104 break;
1105 case 3:
1106 if (!resists_blnd(&youmonst)) {
1107 pline("%s your vision!", Tobjnam(obj, "damage"));
1108 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
1109 if (!Blind)
1110 Your1(vision_clears);
1111 } else {
1112 pline("%s your vision.", Tobjnam(obj, "assault"));
1113 You("are unaffected!");
1115 break;
1116 case 4:
1117 pline("%s your mind!", Tobjnam(obj, "zap"));
1118 (void) make_hallucinated(
1119 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
1120 break;
1121 case 5:
1122 pline("%s!", Tobjnam(obj, "explode"));
1123 useup(obj);
1124 *optr = obj = 0; /* it's gone */
1125 /* physical damage cause by the shards and force */
1126 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
1127 KILLED_BY_AN);
1128 break;
1130 if (obj)
1131 consume_obj_charge(obj, TRUE);
1132 return;
1135 if (Hallucination) {
1136 if (!obj->spe) {
1137 pline("All you see is funky %s haze.", hcolor((char *) 0));
1138 } else {
1139 switch (rnd(6)) {
1140 case 1:
1141 You("grok some groovy globs of incandescent lava.");
1142 break;
1143 case 2:
1144 pline("Whoa! Psychedelic colors, %s!",
1145 poly_gender() == 1 ? "babe" : "dude");
1146 break;
1147 case 3:
1148 pline_The("crystal pulses with sinister %s light!",
1149 hcolor((char *) 0));
1150 break;
1151 case 4:
1152 You_see("goldfish swimming above fluorescent rocks.");
1153 break;
1154 case 5:
1155 You_see(
1156 "tiny snowflakes spinning around a miniature farmhouse.");
1157 break;
1158 default:
1159 pline("Oh wow... like a kaleidoscope!");
1160 break;
1162 consume_obj_charge(obj, TRUE);
1164 return;
1167 /* read a single character */
1168 if (flags.verbose)
1169 You("may look for an object or monster symbol.");
1170 ch = yn_function("What do you look for?", (char *) 0, '\0');
1171 /* Don't filter out ' ' here; it has a use */
1172 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
1173 if (flags.verbose)
1174 pline1(Never_mind);
1175 return;
1177 You("peer into %s...", the(xname(obj)));
1178 nomul(-rnd(10));
1179 multi_reason = "gazing into a crystal ball";
1180 nomovemsg = "";
1181 if (obj->spe <= 0) {
1182 pline_The("vision is unclear.");
1183 } else {
1184 int class, i;
1185 int ret = 0;
1187 makeknown(CRYSTAL_BALL);
1188 consume_obj_charge(obj, TRUE);
1190 /* special case: accept ']' as synonym for mimic
1191 * we have to do this before the def_char_to_objclass check
1193 if (ch == DEF_MIMIC_DEF)
1194 ch = DEF_MIMIC;
1196 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1197 ret = object_detect((struct obj *) 0, class);
1198 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1199 ret = monster_detect((struct obj *) 0, class);
1200 else if (iflags.bouldersym && (ch == iflags.bouldersym))
1201 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1202 else
1203 switch (ch) {
1204 case '^':
1205 ret = trap_detect((struct obj *) 0);
1206 break;
1207 default:
1208 i = rn2(SIZE(level_detects));
1209 You_see("%s, %s.", level_detects[i].what,
1210 level_distance(level_detects[i].where));
1211 ret = 0;
1212 break;
1215 if (ret) {
1216 if (!rn2(100)) /* make them nervous */
1217 You_see("the Wizard of Yendor gazing out at you.");
1218 else
1219 pline_The("vision is unclear.");
1222 return;
1225 STATIC_OVL void
1226 show_map_spot(x, y)
1227 register int x, y;
1229 struct rm *lev;
1230 struct trap *t;
1231 int oldglyph;
1233 if (Confusion && rn2(7))
1234 return;
1235 lev = &levl[x][y];
1237 lev->seenv = SVALL;
1239 /* Secret corridors are found, but not secret doors. */
1240 if (lev->typ == SCORR) {
1241 lev->typ = CORR;
1242 unblock_point(x, y);
1246 * Force the real background, then if it's not furniture and there's
1247 * a known trap there, display the trap, else if there was an object
1248 * shown there, redisplay the object. So during mapping, furniture
1249 * takes precedence over traps, which take precedence over objects,
1250 * opposite to how normal vision behaves.
1252 oldglyph = glyph_at(x, y);
1253 if (level.flags.hero_memory) {
1254 magic_map_background(x, y, 0);
1255 newsym(x, y); /* show it, if not blocked */
1256 } else {
1257 magic_map_background(x, y, 1); /* display it */
1259 if (!IS_FURNITURE(lev->typ)) {
1260 if ((t = t_at(x, y)) != 0 && t->tseen) {
1261 map_trap(t, 1);
1262 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1263 show_glyph(x, y, oldglyph);
1264 if (level.flags.hero_memory)
1265 lev->glyph = oldglyph;
1270 void
1271 do_mapping()
1273 register int zx, zy;
1274 boolean unconstrained;
1276 unconstrained = unconstrain_map();
1277 for (zx = 1; zx < COLNO; zx++)
1278 for (zy = 0; zy < ROWNO; zy++)
1279 show_map_spot(zx, zy);
1281 if (!level.flags.hero_memory || unconstrained) {
1282 flush_screen(1); /* flush temp screen */
1283 /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
1284 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1285 "anything of interest");
1286 docrt();
1288 reconstrain_map();
1289 exercise(A_WIS, TRUE);
1292 /* clairvoyance */
1293 void
1294 do_vicinity_map(sobj)
1295 struct obj *sobj; /* scroll--actually fake spellbook--object */
1297 register int zx, zy;
1298 struct monst *mtmp;
1299 boolean unconstrained, refresh = FALSE, mdetected = FALSE,
1300 extended = (sobj && sobj->blessed);
1301 int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
1302 hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
1303 lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
1304 hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
1305 ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
1307 unconstrained = unconstrain_map();
1308 for (zx = lo_x; zx <= hi_x; zx++)
1309 for (zy = lo_y; zy <= hi_y; zy++) {
1310 show_map_spot(zx, zy);
1312 if (extended && (mtmp = m_at(zx, zy)) != 0
1313 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
1314 int oldglyph = glyph_at(zx, zy);
1316 map_monst(mtmp, FALSE);
1317 if (glyph_at(zx, zy) != oldglyph)
1318 mdetected = TRUE;
1322 if (!level.flags.hero_memory || unconstrained || mdetected) {
1323 flush_screen(1); /* flush temp screen */
1324 if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
1325 ter_typ |= TER_MON;
1326 if (extended)
1327 EDetect_monsters |= I_SPECIAL;
1328 browse_map(ter_typ, "anything of interest");
1329 EDetect_monsters &= ~I_SPECIAL;
1330 refresh = TRUE;
1332 reconstrain_map();
1333 if (refresh)
1334 docrt();
1337 /* convert a secret door into a normal door */
1338 void
1339 cvt_sdoor_to_door(lev)
1340 struct rm *lev;
1342 int newmask = lev->doormask & ~WM_MASK;
1344 if (Is_rogue_level(&u.uz))
1345 /* rogue didn't have doors, only doorways */
1346 newmask = D_NODOOR;
1347 else
1348 /* newly exposed door is closed */
1349 if (!(newmask & D_LOCKED))
1350 newmask |= D_CLOSED;
1352 lev->typ = DOOR;
1353 lev->doormask = newmask;
1356 STATIC_PTR void
1357 findone(zx, zy, num)
1358 int zx, zy;
1359 genericptr_t num;
1361 register struct trap *ttmp;
1362 register struct monst *mtmp;
1364 if (levl[zx][zy].typ == SDOOR) {
1365 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1366 magic_map_background(zx, zy, 0);
1367 newsym(zx, zy);
1368 (*(int *) num)++;
1369 } else if (levl[zx][zy].typ == SCORR) {
1370 levl[zx][zy].typ = CORR;
1371 unblock_point(zx, zy);
1372 magic_map_background(zx, zy, 0);
1373 newsym(zx, zy);
1374 (*(int *) num)++;
1375 } else if ((ttmp = t_at(zx, zy)) != 0) {
1376 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1377 ttmp->tseen = 1;
1378 newsym(zx, zy);
1379 (*(int *) num)++;
1381 } else if ((mtmp = m_at(zx, zy)) != 0) {
1382 if (mtmp->m_ap_type) {
1383 seemimic(mtmp);
1384 (*(int *) num)++;
1386 if (mtmp->mundetected
1387 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1388 mtmp->mundetected = 0;
1389 newsym(zx, zy);
1390 (*(int *) num)++;
1392 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1393 map_invisible(zx, zy);
1394 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1395 unmap_object(zx, zy);
1396 newsym(zx, zy);
1397 (*(int *) num)++;
1401 STATIC_PTR void
1402 openone(zx, zy, num)
1403 int zx, zy;
1404 genericptr_t num;
1406 register struct trap *ttmp;
1407 register struct obj *otmp;
1408 int *num_p = (int *) num;
1410 if (OBJ_AT(zx, zy)) {
1411 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1412 if (Is_box(otmp) && otmp->olocked) {
1413 otmp->olocked = 0;
1414 (*num_p)++;
1417 /* let it fall to the next cases. could be on trap. */
1419 if (levl[zx][zy].typ == SDOOR
1420 || (levl[zx][zy].typ == DOOR
1421 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1422 if (levl[zx][zy].typ == SDOOR)
1423 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1424 if (levl[zx][zy].doormask & D_TRAPPED) {
1425 if (distu(zx, zy) < 3)
1426 b_trapped("door", 0);
1427 else
1428 Norep("You %s an explosion!",
1429 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1430 : "feel the shock of"));
1431 wake_nearto(zx, zy, 11 * 11);
1432 levl[zx][zy].doormask = D_NODOOR;
1433 } else
1434 levl[zx][zy].doormask = D_ISOPEN;
1435 unblock_point(zx, zy);
1436 newsym(zx, zy);
1437 (*num_p)++;
1438 } else if (levl[zx][zy].typ == SCORR) {
1439 levl[zx][zy].typ = CORR;
1440 unblock_point(zx, zy);
1441 newsym(zx, zy);
1442 (*num_p)++;
1443 } else if ((ttmp = t_at(zx, zy)) != 0) {
1444 struct monst *mon;
1445 boolean dummy; /* unneeded "you notice it arg" */
1447 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1448 ttmp->tseen = 1;
1449 newsym(zx, zy);
1450 (*num_p)++;
1452 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1453 if (openholdingtrap(mon, &dummy)
1454 || openfallingtrap(mon, TRUE, &dummy))
1455 (*num_p)++;
1456 } else if (find_drawbridge(&zx, &zy)) {
1457 /* make sure it isn't an open drawbridge */
1458 open_drawbridge(zx, zy);
1459 (*num_p)++;
1463 /* returns number of things found */
1465 findit()
1467 int num = 0;
1469 if (u.uswallow)
1470 return 0;
1471 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1472 return num;
1475 /* returns number of things found and opened */
1477 openit()
1479 int num = 0;
1481 if (u.uswallow) {
1482 if (is_animal(u.ustuck->data)) {
1483 if (Blind)
1484 pline("Its mouth opens!");
1485 else
1486 pline("%s opens its mouth!", Monnam(u.ustuck));
1488 expels(u.ustuck, u.ustuck->data, TRUE);
1489 return -1;
1492 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1493 return num;
1496 /* callback hack for overriding vision in do_clear_area() */
1497 boolean
1498 detecting(func)
1499 void FDECL((*func), (int, int, genericptr_t));
1501 return (func == findone || func == openone);
1504 void
1505 find_trap(trap)
1506 struct trap *trap;
1508 int tt = what_trap(trap->ttyp);
1509 boolean cleared = FALSE;
1511 trap->tseen = 1;
1512 exercise(A_WIS, TRUE);
1513 feel_newsym(trap->tx, trap->ty);
1515 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1516 /* There's too much clutter to see your find otherwise */
1517 cls();
1518 map_trap(trap, 1);
1519 display_self();
1520 cleared = TRUE;
1523 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1525 if (cleared) {
1526 display_nhwindow(WIN_MAP, TRUE); /* wait */
1527 docrt();
1531 STATIC_OVL int
1532 mfind0(mtmp, via_warning)
1533 struct monst *mtmp;
1534 boolean via_warning;
1536 xchar x = mtmp->mx,
1537 y = mtmp->my;
1539 if (via_warning && !warning_of(mtmp))
1540 return -1;
1542 if (mtmp->m_ap_type) {
1543 seemimic(mtmp);
1544 find:
1545 exercise(A_WIS, TRUE);
1546 if (!canspotmon(mtmp)) {
1547 if (glyph_is_invisible(levl[x][y].glyph)) {
1548 /* Found invisible monster in a square which already has
1549 * an 'I' in it. Logically, this should still take time
1550 * and lead to a return 1, but if we did that the player
1551 * would keep finding the same monster every turn.
1553 return -1;
1554 } else {
1555 You_feel("an unseen monster!");
1556 map_invisible(x, y);
1558 } else if (!sensemon(mtmp))
1559 You("find %s.",
1560 mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1561 return 1;
1563 if (!canspotmon(mtmp)) {
1564 if (mtmp->mundetected
1565 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1566 if (via_warning) {
1567 Your("warning senses cause you to take a second %s.",
1568 Blind ? "to check nearby" : "look close by");
1569 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1571 mtmp->mundetected = 0;
1572 newsym(x, y);
1573 goto find;
1575 return 0;
1579 dosearch0(aflag)
1580 register int aflag; /* intrinsic autosearch vs explicit searching */
1582 #ifdef GCC_BUG
1583 /* Some old versions of gcc seriously muck up nested loops. If you get
1584 * strange crashes while searching in a version compiled with gcc, try
1585 * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
1586 * the makefile).
1588 volatile xchar x, y;
1589 #else
1590 register xchar x, y;
1591 #endif
1592 register struct trap *trap;
1593 register struct monst *mtmp;
1595 if (u.uswallow) {
1596 if (!aflag)
1597 pline("What are you looking for? The exit?");
1598 } else {
1599 int fund = (uwep && uwep->oartifact
1600 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1602 if (ublindf && ublindf->otyp == LENSES && !Blind)
1603 fund += 2; /* JDS: lenses help searching */
1604 if (fund > 5)
1605 fund = 5;
1606 for (x = u.ux - 1; x < u.ux + 2; x++)
1607 for (y = u.uy - 1; y < u.uy + 2; y++) {
1608 if (!isok(x, y))
1609 continue;
1610 if (x == u.ux && y == u.uy)
1611 continue;
1613 if (Blind && !aflag)
1614 feel_location(x, y);
1615 if (levl[x][y].typ == SDOOR) {
1616 if (rnl(7 - fund))
1617 continue;
1618 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1619 exercise(A_WIS, TRUE);
1620 nomul(0);
1621 feel_location(x, y); /* make sure it shows up */
1622 You("find a hidden door.");
1623 } else if (levl[x][y].typ == SCORR) {
1624 if (rnl(7 - fund))
1625 continue;
1626 levl[x][y].typ = CORR;
1627 unblock_point(x, y); /* vision */
1628 exercise(A_WIS, TRUE);
1629 nomul(0);
1630 feel_newsym(x, y); /* make sure it shows up */
1631 You("find a hidden passage.");
1632 } else {
1633 /* Be careful not to find anything in an SCORR or SDOOR */
1634 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
1635 int mfres = mfind0(mtmp, 0);
1637 if (mfres == -1)
1638 continue;
1639 else if (mfres > 0)
1640 return mfres;
1643 /* see if an invisible monster has moved--if Blind,
1644 * feel_location() already did it
1646 if (!aflag && !mtmp && !Blind
1647 && glyph_is_invisible(levl[x][y].glyph)) {
1648 unmap_object(x, y);
1649 newsym(x, y);
1652 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
1653 nomul(0);
1654 if (trap->ttyp == STATUE_TRAP) {
1655 if (activate_statue_trap(trap, x, y, FALSE))
1656 exercise(A_WIS, TRUE);
1657 return 1;
1658 } else {
1659 find_trap(trap);
1665 return 1;
1668 /* the 's' command -- explicit searching */
1670 dosearch()
1672 return dosearch0(0);
1675 void
1676 warnreveal()
1678 int x, y;
1679 struct monst *mtmp;
1681 for (x = u.ux - 1; x <= u.ux + 1; x++)
1682 for (y = u.uy - 1; y <= u.uy + 1; y++) {
1683 if (!isok(x, y) || (x == u.ux && y == u.uy))
1684 continue;
1685 if ((mtmp = m_at(x, y)) != 0
1686 && warning_of(mtmp) && mtmp->mundetected)
1687 (void) mfind0(mtmp, 1); /* via_warning */
1691 /* Pre-map the sokoban levels */
1692 void
1693 sokoban_detect()
1695 register int x, y;
1696 register struct trap *ttmp;
1697 register struct obj *obj;
1699 /* Map the background and boulders */
1700 for (x = 1; x < COLNO; x++)
1701 for (y = 0; y < ROWNO; y++) {
1702 levl[x][y].seenv = SVALL;
1703 levl[x][y].waslit = TRUE;
1704 map_background(x, y, 1);
1705 if ((obj = sobj_at(BOULDER, x, y)) != 0)
1706 map_object(obj, 1);
1709 /* Map the traps */
1710 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1711 ttmp->tseen = 1;
1712 map_trap(ttmp, 1);
1713 /* set sokoban_rules when there is at least one pit or hole */
1714 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
1715 Sokoban = 1;
1719 STATIC_DCL int
1720 reveal_terrain_getglyph(x,y, full, swallowed, default_glyph, which_subset)
1721 int x,y, full;
1722 unsigned swallowed;
1723 int default_glyph, which_subset;
1725 int glyph, levl_glyph;
1726 uchar seenv;
1727 boolean keep_traps = (which_subset & TER_TRP) !=0,
1728 keep_objs = (which_subset & TER_OBJ) != 0,
1729 keep_mons = (which_subset & TER_MON) != 0;
1730 struct monst *mtmp;
1731 struct trap *t;
1733 /* for 'full', show the actual terrain for the entire level,
1734 otherwise what the hero remembers for seen locations with
1735 monsters, objects, and/or traps removed as caller dictates */
1736 seenv = (full || level.flags.hero_memory)
1737 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
1738 if (full) {
1739 levl[x][y].seenv = SVALL;
1740 glyph = back_to_glyph(x, y);
1741 levl[x][y].seenv = seenv;
1742 } else {
1743 levl_glyph = level.flags.hero_memory
1744 ? levl[x][y].glyph
1745 : seenv
1746 ? back_to_glyph(x, y)
1747 : default_glyph;
1748 /* glyph_at() returns the displayed glyph, which might
1749 be a monster. levl[][].glyph contains the remembered
1750 glyph, which will never be a monster (unless it is
1751 the invisible monster glyph, which is handled like
1752 an object, replacing any object or trap at its spot) */
1753 glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
1754 if (keep_mons && x == u.ux && y == u.uy && swallowed)
1755 glyph = mon_to_glyph(u.ustuck);
1756 else if (((glyph_is_monster(glyph)
1757 || glyph_is_warning(glyph)) && !keep_mons)
1758 || glyph_is_swallow(glyph))
1759 glyph = levl_glyph;
1760 if (((glyph_is_object(glyph) && !keep_objs)
1761 || glyph_is_invisible(glyph))
1762 && keep_traps && !covers_traps(x, y)) {
1763 if ((t = t_at(x, y)) != 0 && t->tseen)
1764 glyph = trap_to_glyph(t);
1766 if ((glyph_is_object(glyph) && !keep_objs)
1767 || (glyph_is_trap(glyph) && !keep_traps)
1768 || glyph_is_invisible(glyph)) {
1769 if (!seenv) {
1770 glyph = default_glyph;
1771 } else if (lastseentyp[x][y] == levl[x][y].typ) {
1772 glyph = back_to_glyph(x, y);
1773 } else {
1774 /* look for a mimic here posing as furniture;
1775 if we don't find one, we'll have to fake it */
1776 if ((mtmp = m_at(x, y)) != 0
1777 && mtmp->m_ap_type == M_AP_FURNITURE) {
1778 glyph = cmap_to_glyph(mtmp->mappearance);
1779 } else {
1780 /* we have a topology type but we want a
1781 screen symbol in order to derive a glyph;
1782 some screen symbols need the flags field
1783 of levl[][] in addition to the type
1784 (to disambiguate STAIRS to S_upstair or
1785 S_dnstair, for example; current flags
1786 might not be intended for remembered
1787 type, but we've got no other choice) */
1788 schar save_typ = levl[x][y].typ;
1790 levl[x][y].typ = lastseentyp[x][y];
1791 glyph = back_to_glyph(x, y);
1792 levl[x][y].typ = save_typ;
1797 if (glyph == cmap_to_glyph(S_darkroom))
1798 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
1799 return glyph;
1802 void
1803 dump_map()
1805 int x, y, glyph;
1806 int subset = TER_MAP|TER_TRP|TER_OBJ|TER_MON;
1807 int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1808 char buf[BUFSZ];
1810 for (y = 0; y < ROWNO; y++) {
1811 for (x = 1; x < COLNO; x++) {
1812 int ch, color;
1813 unsigned special;
1814 glyph = reveal_terrain_getglyph(x,y, FALSE, u.uswallow,
1815 default_glyph, subset);
1816 (void) mapglyph(glyph, &ch, &color, &special, x, y);
1817 buf[x-1] = ch;
1819 buf[x-2] = '\0';
1820 putstr(0,0, buf);
1824 /* idea from crawl; show known portion of map without any monsters,
1825 objects, or traps occluding the view of the underlying terrain */
1826 void
1827 reveal_terrain(full, which_subset)
1828 int full; /* wizard|explore modes allow player to request full map */
1829 int which_subset; /* when not full, whether to suppress objs and/or traps */
1831 if ((Hallucination || Stunned || Confusion) && !full) {
1832 You("are too disoriented for this.");
1833 } else {
1834 int x, y, glyph, default_glyph;
1835 char buf[BUFSZ];
1836 /* there is a TER_MAP bit too; we always show map regardless of it */
1837 boolean keep_traps = (which_subset & TER_TRP) !=0,
1838 keep_objs = (which_subset & TER_OBJ) != 0,
1839 keep_mons = (which_subset & TER_MON) != 0; /* not used */
1840 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
1842 if (unconstrain_map())
1843 docrt();
1844 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1846 for (x = 1; x < COLNO; x++)
1847 for (y = 0; y < ROWNO; y++) {
1848 glyph = reveal_terrain_getglyph(x,y, full, swallowed,
1849 default_glyph, which_subset);
1850 show_glyph(x, y, glyph);
1853 /* hero's location is not highlighted, but getpos() starts with
1854 cursor there, and after moving it anywhere '@' moves it back */
1855 flush_screen(1);
1856 if (full) {
1857 Strcpy(buf, "underlying terrain");
1858 } else {
1859 Strcpy(buf, "known terrain");
1860 if (keep_traps)
1861 Sprintf(eos(buf), "%s traps",
1862 (keep_objs || keep_mons) ? "," : " and");
1863 if (keep_objs)
1864 Sprintf(eos(buf), "%s%s objects",
1865 (keep_traps || keep_mons) ? "," : "",
1866 keep_mons ? "" : " and");
1867 if (keep_mons)
1868 Sprintf(eos(buf), "%s and monsters",
1869 (keep_traps || keep_objs) ? "," : "");
1871 pline("Showing %s only...", buf);
1873 /* allow player to move cursor around and get autodescribe feedback
1874 based on what is visible now rather than what is on 'real' map */
1875 which_subset |= TER_MAP; /* guarantee non-zero */
1876 browse_map(which_subset, "anything of interest");
1878 reconstrain_map();
1879 docrt(); /* redraw the screen, restoring regular map */
1880 if (Underwater)
1881 under_water(2);
1882 if (u.uburied)
1883 under_ground(2);
1885 return;
1888 /*detect.c*/