Use define for iron ball weight increment
[aNetHack.git] / src / detect.c
blobc29ad83eb69aa2c28b4374651b551b7edf731b0a
1 /* NetHack 3.6 detect.c $NHDT-Date: 1446369464 2015/11/01 09:17:44 $ $NHDT-Branch: master $:$NHDT-Revision: 1.61 $ */
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 void FDECL(do_dknown_of, (struct obj *));
16 STATIC_DCL boolean FDECL(check_map_spot, (int, int, CHAR_P, unsigned));
17 STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P, unsigned));
18 STATIC_DCL void FDECL(sense_trap, (struct trap *, XCHAR_P, XCHAR_P, int));
19 STATIC_DCL int FDECL(detect_obj_traps, (struct obj *, BOOLEAN_P, int));
20 STATIC_DCL void FDECL(show_map_spot, (int, int));
21 STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
22 STATIC_PTR void FDECL(openone, (int, int, genericptr_t));
24 /* Recursively search obj for an object in class oclass and return 1st found
26 struct obj *
27 o_in(obj, oclass)
28 struct obj *obj;
29 char oclass;
31 register struct obj *otmp;
32 struct obj *temp;
34 if (obj->oclass == oclass)
35 return obj;
37 if (Has_contents(obj)) {
38 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
39 if (otmp->oclass == oclass)
40 return otmp;
41 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)))
42 return temp;
44 return (struct obj *) 0;
47 /* Recursively search obj for an object made of specified material.
48 * Return first found.
50 struct obj *
51 o_material(obj, material)
52 struct obj *obj;
53 unsigned material;
55 register struct obj *otmp;
56 struct obj *temp;
58 if (objects[obj->otyp].oc_material == material)
59 return obj;
61 if (Has_contents(obj)) {
62 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
63 if (objects[otmp->otyp].oc_material == material)
64 return otmp;
65 else if (Has_contents(otmp)
66 && (temp = o_material(otmp, material)))
67 return temp;
69 return (struct obj *) 0;
72 STATIC_OVL void
73 do_dknown_of(obj)
74 struct obj *obj;
76 struct obj *otmp;
78 obj->dknown = 1;
79 if (Has_contents(obj)) {
80 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
81 do_dknown_of(otmp);
85 /* Check whether the location has an outdated object displayed on it. */
86 STATIC_OVL boolean
87 check_map_spot(x, y, oclass, material)
88 int x, y;
89 char oclass;
90 unsigned material;
92 int glyph;
93 register struct obj *otmp;
94 register struct monst *mtmp;
96 glyph = glyph_at(x, y);
97 if (glyph_is_object(glyph)) {
98 /* there's some object shown here */
99 if (oclass == ALL_CLASSES) {
100 return (boolean) !(level.objects[x][y] /* stale if nothing here */
101 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
102 } else {
103 if (material
104 && objects[glyph_to_obj(glyph)].oc_material == material) {
105 /* object shown here is of interest because material matches */
106 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
107 if (o_material(otmp, GOLD))
108 return FALSE;
109 /* didn't find it; perhaps a monster is carrying it */
110 if ((mtmp = m_at(x, y)) != 0) {
111 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
112 if (o_material(otmp, GOLD))
113 return FALSE;
115 /* detection indicates removal of this object from the map */
116 return TRUE;
118 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
119 /* obj shown here is of interest because its class matches */
120 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
121 if (o_in(otmp, oclass))
122 return FALSE;
123 /* didn't find it; perhaps a monster is carrying it */
124 if ((mtmp = m_at(x, y)) != 0) {
125 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
126 if (o_in(otmp, oclass))
127 return FALSE;
129 /* detection indicates removal of this object from the map */
130 return TRUE;
134 return FALSE;
138 * When doing detection, remove stale data from the map display (corpses
139 * rotted away, objects carried away by monsters, etc) so that it won't
140 * reappear after the detection has completed. Return true if noticeable
141 * change occurs.
143 STATIC_OVL boolean
144 clear_stale_map(oclass, material)
145 char oclass;
146 unsigned material;
148 register int zx, zy;
149 boolean change_made = FALSE;
151 for (zx = 1; zx < COLNO; zx++)
152 for (zy = 0; zy < ROWNO; zy++)
153 if (check_map_spot(zx, zy, oclass, material)) {
154 unmap_object(zx, zy);
155 change_made = TRUE;
158 return change_made;
161 /* look for gold, on the floor or in monsters' possession */
163 gold_detect(sobj)
164 register struct obj *sobj;
166 register struct obj *obj;
167 register struct monst *mtmp;
168 struct obj *temp;
169 boolean stale;
171 known = stale =
172 clear_stale_map(COIN_CLASS, (unsigned) (sobj->blessed ? GOLD : 0));
174 /* look for gold carried by monsters (might be in a container) */
175 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
176 if (DEADMONSTER(mtmp))
177 continue; /* probably not needed in this case but... */
178 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
179 known = TRUE;
180 goto outgoldmap; /* skip further searching */
181 } else
182 for (obj = mtmp->minvent; obj; obj = obj->nobj)
183 if (sobj->blessed && o_material(obj, GOLD)) {
184 known = TRUE;
185 goto outgoldmap;
186 } else if (o_in(obj, COIN_CLASS)) {
187 known = TRUE;
188 goto outgoldmap; /* skip further searching */
192 /* look for gold objects */
193 for (obj = fobj; obj; obj = obj->nobj) {
194 if (sobj->blessed && o_material(obj, GOLD)) {
195 known = TRUE;
196 if (obj->ox != u.ux || obj->oy != u.uy)
197 goto outgoldmap;
198 } else if (o_in(obj, COIN_CLASS)) {
199 known = TRUE;
200 if (obj->ox != u.ux || obj->oy != u.uy)
201 goto outgoldmap;
205 if (!known) {
206 /* no gold found on floor or monster's inventory.
207 adjust message if you have gold in your inventory */
208 if (sobj) {
209 char buf[BUFSZ];
210 if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
211 Sprintf(buf, "You feel like a million %s!", currency(2L));
212 } else if (hidden_gold() || money_cnt(invent))
213 Strcpy(buf,
214 "You feel worried about your future financial situation.");
215 else
216 Strcpy(buf, "You feel materially poor.");
217 strange_feeling(sobj, buf);
219 return 1;
221 /* only under me - no separate display required */
222 if (stale)
223 docrt();
224 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
225 return 0;
227 outgoldmap:
228 cls();
230 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
231 u.uinwater = u.uburied = 0;
232 /* Discover gold locations. */
233 for (obj = fobj; obj; obj = obj->nobj) {
234 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
235 if (temp != obj) {
236 temp->ox = obj->ox;
237 temp->oy = obj->oy;
239 map_object(temp, 1);
240 } else if ((temp = o_in(obj, COIN_CLASS))) {
241 if (temp != obj) {
242 temp->ox = obj->ox;
243 temp->oy = obj->oy;
245 map_object(temp, 1);
248 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
249 if (DEADMONSTER(mtmp))
250 continue; /* probably overkill here */
251 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
252 struct obj gold;
253 gold = zeroobj; /* ensure oextra is cleared too */
254 gold.otyp = GOLD_PIECE;
255 gold.ox = mtmp->mx;
256 gold.oy = mtmp->my;
257 map_object(&gold, 1);
258 } else
259 for (obj = mtmp->minvent; obj; obj = obj->nobj)
260 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
261 temp->ox = mtmp->mx;
262 temp->oy = mtmp->my;
263 map_object(temp, 1);
264 break;
265 } else if ((temp = o_in(obj, COIN_CLASS))) {
266 temp->ox = mtmp->mx;
267 temp->oy = mtmp->my;
268 map_object(temp, 1);
269 break;
272 newsym(u.ux, u.uy);
273 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
274 You_feel("very greedy, and sense gold!");
275 exercise(A_WIS, TRUE);
276 display_nhwindow(WIN_MAP, TRUE);
277 docrt();
278 if (Underwater)
279 under_water(2);
280 if (u.uburied)
281 under_ground(2);
282 return 0;
285 /* returns 1 if nothing was detected */
286 /* returns 0 if something was detected */
288 food_detect(sobj)
289 register struct obj *sobj;
291 register struct obj *obj;
292 register struct monst *mtmp;
293 register int ct = 0, ctu = 0;
294 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
295 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
296 const char *what = confused ? something : "food";
298 stale = clear_stale_map(oclass, 0);
300 for (obj = fobj; obj; obj = obj->nobj)
301 if (o_in(obj, oclass)) {
302 if (obj->ox == u.ux && obj->oy == u.uy)
303 ctu++;
304 else
305 ct++;
307 for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
308 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory
310 for (obj = mtmp->minvent; obj; obj = obj->nobj)
311 if (o_in(obj, oclass)) {
312 ct++;
313 break;
317 if (!ct && !ctu) {
318 known = stale && !confused;
319 if (stale) {
320 docrt();
321 You("sense a lack of %s nearby.", what);
322 if (sobj && sobj->blessed) {
323 if (!u.uedibility)
324 Your("%s starts to tingle.", body_part(NOSE));
325 u.uedibility = 1;
327 } else if (sobj) {
328 char buf[BUFSZ];
329 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
330 (sobj->blessed && !u.uedibility)
331 ? " then starts to tingle"
332 : "");
333 if (sobj->blessed && !u.uedibility) {
334 boolean savebeginner = flags.beginner;
336 flags.beginner = FALSE; /* prevent non-delivery of message */
337 strange_feeling(sobj, buf);
338 flags.beginner = savebeginner;
339 u.uedibility = 1;
340 } else
341 strange_feeling(sobj, buf);
343 return !stale;
344 } else if (!ct) {
345 known = TRUE;
346 You("%s %s nearby.", sobj ? "smell" : "sense", what);
347 if (sobj && sobj->blessed) {
348 if (!u.uedibility)
349 pline("Your %s starts to tingle.", body_part(NOSE));
350 u.uedibility = 1;
352 } else {
353 struct obj *temp;
354 known = TRUE;
355 cls();
356 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
357 u.uinwater = u.uburied = 0;
358 for (obj = fobj; obj; obj = obj->nobj)
359 if ((temp = o_in(obj, oclass)) != 0) {
360 if (temp != obj) {
361 temp->ox = obj->ox;
362 temp->oy = obj->oy;
364 map_object(temp, 1);
366 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
367 /* no DEADMONSTER(mtmp) check needed since dmons never have
368 * inventory */
369 for (obj = mtmp->minvent; obj; obj = obj->nobj)
370 if ((temp = o_in(obj, oclass)) != 0) {
371 temp->ox = mtmp->mx;
372 temp->oy = mtmp->my;
373 map_object(temp, 1);
374 break; /* skip rest of this monster's inventory */
376 newsym(u.ux, u.uy);
377 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
378 if (sobj) {
379 if (sobj->blessed) {
380 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
381 u.uedibility ? "continues" : "starts", what);
382 u.uedibility = 1;
383 } else
384 Your("%s tingles and you smell %s.", body_part(NOSE), what);
385 } else
386 You("sense %s.", what);
387 display_nhwindow(WIN_MAP, TRUE);
388 exercise(A_WIS, TRUE);
389 docrt();
390 if (Underwater)
391 under_water(2);
392 if (u.uburied)
393 under_ground(2);
395 return 0;
399 * Used for scrolls, potions, spells, and crystal balls. Returns:
401 * 1 - nothing was detected
402 * 0 - something was detected
405 object_detect(detector, class)
406 struct obj *detector; /* object doing the detecting */
407 int class; /* an object class, 0 for all */
409 register int x, y;
410 char stuff[BUFSZ];
411 int is_cursed = (detector && detector->cursed);
412 int do_dknown = (detector && (detector->oclass == POTION_CLASS
413 || detector->oclass == SPBOOK_CLASS)
414 && detector->blessed);
415 int ct = 0, ctu = 0;
416 register struct obj *obj, *otmp = (struct obj *) 0;
417 register struct monst *mtmp;
418 int sym, boulder = 0;
420 if (class < 0 || class >= MAXOCLASSES) {
421 impossible("object_detect: illegal class %d", class);
422 class = 0;
425 /* Special boulder symbol check - does the class symbol happen
426 * to match iflags.bouldersym which is a user-defined?
427 * If so, that means we aren't sure what they really wanted to
428 * detect. Rather than trump anything, show both possibilities.
429 * We can exclude checking the buried obj chain for boulders below.
431 sym = class ? def_oc_syms[class].sym : 0;
432 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
433 boulder = ROCK_CLASS;
435 if (Hallucination || (Confusion && class == SCROLL_CLASS))
436 Strcpy(stuff, something);
437 else
438 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
439 if (boulder && class != ROCK_CLASS)
440 Strcat(stuff, " and/or large stones");
442 if (do_dknown)
443 for (obj = invent; obj; obj = obj->nobj)
444 do_dknown_of(obj);
446 for (obj = fobj; obj; obj = obj->nobj) {
447 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
448 if (obj->ox == u.ux && obj->oy == u.uy)
449 ctu++;
450 else
451 ct++;
453 if (do_dknown)
454 do_dknown_of(obj);
457 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
458 if (!class || o_in(obj, class)) {
459 if (obj->ox == u.ux && obj->oy == u.uy)
460 ctu++;
461 else
462 ct++;
464 if (do_dknown)
465 do_dknown_of(obj);
468 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
469 if (DEADMONSTER(mtmp))
470 continue;
471 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
472 if ((!class && !boulder) || o_in(obj, class)
473 || o_in(obj, boulder))
474 ct++;
475 if (do_dknown)
476 do_dknown_of(obj);
478 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
479 && (!class || class == objects[mtmp->mappearance].oc_class))
480 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
481 ct++;
482 break;
486 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
487 if (!ctu) {
488 if (detector)
489 strange_feeling(detector, "You feel a lack of something.");
490 return 1;
493 You("sense %s nearby.", stuff);
494 return 0;
497 cls();
499 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
500 u.uinwater = u.uburied = 0;
502 * Map all buried objects first.
504 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
505 if (!class || (otmp = o_in(obj, class))) {
506 if (class) {
507 if (otmp != obj) {
508 otmp->ox = obj->ox;
509 otmp->oy = obj->oy;
511 map_object(otmp, 1);
512 } else
513 map_object(obj, 1);
516 * If we are mapping all objects, map only the top object of a pile or
517 * the first object in a monster's inventory. Otherwise, go looking
518 * for a matching object class and display the first one encountered
519 * at each location.
521 * Objects on the floor override buried objects.
523 for (x = 1; x < COLNO; x++)
524 for (y = 0; y < ROWNO; y++)
525 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
526 if ((!class && !boulder) || (otmp = o_in(obj, class))
527 || (otmp = o_in(obj, boulder))) {
528 if (class || boulder) {
529 if (otmp != obj) {
530 otmp->ox = obj->ox;
531 otmp->oy = obj->oy;
533 map_object(otmp, 1);
534 } else
535 map_object(obj, 1);
536 break;
539 /* Objects in the monster's inventory override floor objects. */
540 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
541 if (DEADMONSTER(mtmp))
542 continue;
543 for (obj = mtmp->minvent; obj; obj = obj->nobj)
544 if ((!class && !boulder) || (otmp = o_in(obj, class))
545 || (otmp = o_in(obj, boulder))) {
546 if (!class && !boulder)
547 otmp = obj;
548 otmp->ox = mtmp->mx; /* at monster location */
549 otmp->oy = mtmp->my;
550 map_object(otmp, 1);
551 break;
553 /* Allow a mimic to override the detected objects it is carrying. */
554 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
555 && (!class || class == objects[mtmp->mappearance].oc_class)) {
556 struct obj temp;
558 temp.oextra = (struct oextra *) 0;
559 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
560 temp.ox = mtmp->mx;
561 temp.oy = mtmp->my;
562 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
563 map_object(&temp, 1);
564 } else if (findgold(mtmp->minvent)
565 && (!class || class == COIN_CLASS)) {
566 struct obj gold;
567 gold = zeroobj; /* ensure oextra is cleared too */
568 gold.otyp = GOLD_PIECE;
569 gold.ox = mtmp->mx;
570 gold.oy = mtmp->my;
571 map_object(&gold, 1);
575 newsym(u.ux, u.uy);
576 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
577 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
578 display_nhwindow(WIN_MAP, TRUE);
580 * What are we going to do when the hero does an object detect while blind
581 * and the detected object covers a known pool?
583 docrt(); /* this will correctly reset vision */
585 if (Underwater)
586 under_water(2);
587 if (u.uburied)
588 under_ground(2);
589 return 0;
593 * Used by: crystal balls, potions, fountains
595 * Returns 1 if nothing was detected.
596 * Returns 0 if something was detected.
599 monster_detect(otmp, mclass)
600 register struct obj *otmp; /* detecting object (if any) */
601 int mclass; /* monster class, 0 for all */
603 register struct monst *mtmp;
604 int mcnt = 0;
606 /* Note: This used to just check fmon for a non-zero value
607 * but in versions since 3.3.0 fmon can test TRUE due to the
608 * presence of dmons, so we have to find at least one
609 * with positive hit-points to know for sure.
611 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
612 if (!DEADMONSTER(mtmp)) {
613 mcnt++;
614 break;
617 if (!mcnt) {
618 if (otmp)
619 strange_feeling(otmp, Hallucination
620 ? "You get the heebie jeebies."
621 : "You feel threatened.");
622 return 1;
623 } else {
624 boolean woken = FALSE;
626 cls();
627 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
628 if (DEADMONSTER(mtmp))
629 continue;
630 if (!mclass || mtmp->data->mlet == mclass
631 || (mtmp->data == &mons[PM_LONG_WORM]
632 && mclass == S_WORM_TAIL))
633 if (mtmp->mx > 0) {
634 if (mclass && def_monsyms[mclass].sym == ' ')
635 show_glyph(mtmp->mx, mtmp->my,
636 detected_mon_to_glyph(mtmp));
637 else
638 show_glyph(mtmp->mx, mtmp->my,
639 mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
640 /* don't be stingy - display entire worm */
641 if (mtmp->data == &mons[PM_LONG_WORM])
642 detect_wsegs(mtmp, 0);
644 if (otmp && otmp->cursed
645 && (mtmp->msleeping || !mtmp->mcanmove)) {
646 mtmp->msleeping = mtmp->mfrozen = 0;
647 mtmp->mcanmove = 1;
648 woken = TRUE;
651 display_self();
652 You("sense the presence of monsters.");
653 if (woken)
654 pline("Monsters sense the presence of you.");
655 display_nhwindow(WIN_MAP, TRUE);
656 docrt();
657 if (Underwater)
658 under_water(2);
659 if (u.uburied)
660 under_ground(2);
662 return 0;
665 STATIC_OVL void
666 sense_trap(trap, x, y, src_cursed)
667 struct trap *trap;
668 xchar x, y;
669 int src_cursed;
671 if (Hallucination || src_cursed) {
672 struct obj obj; /* fake object */
674 obj.oextra = (struct oextra *) 0;
675 if (trap) {
676 obj.ox = trap->tx;
677 obj.oy = trap->ty;
678 } else {
679 obj.ox = x;
680 obj.oy = y;
682 obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
683 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
684 map_object(&obj, 1);
685 } else if (trap) {
686 map_trap(trap, 1);
687 trap->tseen = 1;
688 } else {
689 struct trap temp_trap; /* fake trap */
690 temp_trap.tx = x;
691 temp_trap.ty = y;
692 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
693 map_trap(&temp_trap, 1);
697 #define OTRAP_NONE 0 /* nothing found */
698 #define OTRAP_HERE 1 /* found at hero's location */
699 #define OTRAP_THERE 2 /* found at any other location */
701 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
702 2 if found at some other spot, 3 if both, 0 otherwise; optionally
703 update the map to show where such traps were found */
704 STATIC_OVL int
705 detect_obj_traps(objlist, show_them, how)
706 struct obj *objlist;
707 boolean show_them;
708 int how; /* 1 for misleading map feedback */
710 struct obj *otmp;
711 xchar x, y;
712 int result = OTRAP_NONE;
714 for (otmp = objlist; otmp; otmp = otmp->nobj) {
715 if (Is_box(otmp) && otmp->otrapped
716 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
717 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
718 if (show_them)
719 sense_trap((struct trap *) 0, x, y, how);
721 if (Has_contents(otmp))
722 result |= detect_obj_traps(otmp->cobj, show_them, how);
724 return result;
727 /* the detections are pulled out so they can
728 * also be used in the crystal ball routine
729 * returns 1 if nothing was detected
730 * returns 0 if something was detected
733 trap_detect(sobj)
734 register struct obj *sobj;
735 /* sobj is null if crystal ball, *scroll if gold detection scroll */
737 register struct trap *ttmp;
738 struct monst *mon;
739 int door, glyph, tr;
740 int cursed_src = sobj && sobj->cursed;
741 boolean found = FALSE;
742 coord cc;
744 /* floor/ceiling traps */
745 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
746 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
747 goto outtrapmap;
748 else
749 found = TRUE;
751 /* chest traps (might be buried or carried) */
752 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
753 if (tr & OTRAP_THERE)
754 goto outtrapmap;
755 else
756 found = TRUE;
758 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0))
759 != OTRAP_NONE) {
760 if (tr & OTRAP_THERE)
761 goto outtrapmap;
762 else
763 found = TRUE;
765 for (mon = fmon; mon; mon = mon->nmon) {
766 if (DEADMONSTER(mon))
767 continue;
768 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
769 if (tr & OTRAP_THERE)
770 goto outtrapmap;
771 else
772 found = TRUE;
775 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
776 found = TRUE;
777 /* door traps */
778 for (door = 0; door < doorindex; door++) {
779 cc = doors[door];
780 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
781 if (cc.x != u.ux || cc.y != u.uy)
782 goto outtrapmap;
783 else
784 found = TRUE;
787 if (!found) {
788 char buf[BUFSZ];
790 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
791 strange_feeling(sobj, buf);
792 return 1;
794 /* traps exist, but only under me - no separate display required */
795 Your("%s itch.", makeplural(body_part(TOE)));
796 return 0;
797 outtrapmap:
798 cls();
800 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
801 u.uinwater = u.uburied = 0;
803 /* show chest traps first, so that subsequent floor trap display
804 will override if both types are present at the same location */
805 (void) detect_obj_traps(fobj, TRUE, cursed_src);
806 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
807 for (mon = fmon; mon; mon = mon->nmon) {
808 if (DEADMONSTER(mon))
809 continue;
810 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
812 (void) detect_obj_traps(invent, TRUE, cursed_src);
814 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
815 sense_trap(ttmp, 0, 0, cursed_src);
817 for (door = 0; door < doorindex; door++) {
818 cc = doors[door];
819 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
820 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
823 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
824 glyph = glyph_at(u.ux, u.uy);
825 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph)))
826 newsym(u.ux, u.uy);
827 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
829 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
830 /* wait for user to respond, then reset map display to normal */
831 display_nhwindow(WIN_MAP, TRUE);
832 docrt();
833 if (Underwater)
834 under_water(2);
835 if (u.uburied)
836 under_ground(2);
837 return 0;
840 const char *
841 level_distance(where)
842 d_level *where;
844 register schar ll = depth(&u.uz) - depth(where);
845 register boolean indun = (u.uz.dnum == where->dnum);
847 if (ll < 0) {
848 if (ll < (-8 - rn2(3)))
849 if (!indun)
850 return "far away";
851 else
852 return "far below";
853 else if (ll < -1)
854 if (!indun)
855 return "away below you";
856 else
857 return "below you";
858 else if (!indun)
859 return "in the distance";
860 else
861 return "just below";
862 } else if (ll > 0) {
863 if (ll > (8 + rn2(3)))
864 if (!indun)
865 return "far away";
866 else
867 return "far above";
868 else if (ll > 1)
869 if (!indun)
870 return "away above you";
871 else
872 return "above you";
873 else if (!indun)
874 return "in the distance";
875 else
876 return "just above";
877 } else if (!indun)
878 return "in the distance";
879 else
880 return "near you";
883 static const struct {
884 const char *what;
885 d_level *where;
886 } level_detects[] = {
887 { "Delphi", &oracle_level },
888 { "Medusa's lair", &medusa_level },
889 { "a castle", &stronghold_level },
890 { "the Wizard of Yendor's tower", &wiz1_level },
893 void
894 use_crystal_ball(optr)
895 struct obj **optr;
897 char ch;
898 int oops;
899 struct obj *obj = *optr;
901 if (Blind) {
902 pline("Too bad you can't see %s.", the(xname(obj)));
903 return;
905 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
906 if (oops && (obj->spe > 0)) {
907 switch (rnd(obj->oartifact ? 4 : 5)) {
908 case 1:
909 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
910 break;
911 case 2:
912 pline("%s you!", Tobjnam(obj, "confuse"));
913 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
914 break;
915 case 3:
916 if (!resists_blnd(&youmonst)) {
917 pline("%s your vision!", Tobjnam(obj, "damage"));
918 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
919 if (!Blind)
920 Your1(vision_clears);
921 } else {
922 pline("%s your vision.", Tobjnam(obj, "assault"));
923 You("are unaffected!");
925 break;
926 case 4:
927 pline("%s your mind!", Tobjnam(obj, "zap"));
928 (void) make_hallucinated(
929 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
930 break;
931 case 5:
932 pline("%s!", Tobjnam(obj, "explode"));
933 useup(obj);
934 *optr = obj = 0; /* it's gone */
935 /* physical damage cause by the shards and force */
936 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
937 KILLED_BY_AN);
938 break;
940 if (obj)
941 consume_obj_charge(obj, TRUE);
942 return;
945 if (Hallucination) {
946 if (!obj->spe) {
947 pline("All you see is funky %s haze.", hcolor((char *) 0));
948 } else {
949 switch (rnd(6)) {
950 case 1:
951 You("grok some groovy globs of incandescent lava.");
952 break;
953 case 2:
954 pline("Whoa! Psychedelic colors, %s!",
955 poly_gender() == 1 ? "babe" : "dude");
956 break;
957 case 3:
958 pline_The("crystal pulses with sinister %s light!",
959 hcolor((char *) 0));
960 break;
961 case 4:
962 You_see("goldfish swimming above fluorescent rocks.");
963 break;
964 case 5:
965 You_see(
966 "tiny snowflakes spinning around a miniature farmhouse.");
967 break;
968 default:
969 pline("Oh wow... like a kaleidoscope!");
970 break;
972 consume_obj_charge(obj, TRUE);
974 return;
977 /* read a single character */
978 if (flags.verbose)
979 You("may look for an object or monster symbol.");
980 ch = yn_function("What do you look for?", (char *) 0, '\0');
981 /* Don't filter out ' ' here; it has a use */
982 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
983 if (flags.verbose)
984 pline1(Never_mind);
985 return;
987 You("peer into %s...", the(xname(obj)));
988 nomul(-rnd(10));
989 multi_reason = "gazing into a crystal ball";
990 nomovemsg = "";
991 if (obj->spe <= 0)
992 pline_The("vision is unclear.");
993 else {
994 int class;
995 int ret = 0;
997 makeknown(CRYSTAL_BALL);
998 consume_obj_charge(obj, TRUE);
1000 /* special case: accept ']' as synonym for mimic
1001 * we have to do this before the def_char_to_objclass check
1003 if (ch == DEF_MIMIC_DEF)
1004 ch = DEF_MIMIC;
1006 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1007 ret = object_detect((struct obj *) 0, class);
1008 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1009 ret = monster_detect((struct obj *) 0, class);
1010 else if (iflags.bouldersym && (ch == iflags.bouldersym))
1011 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1012 else
1013 switch (ch) {
1014 case '^':
1015 ret = trap_detect((struct obj *) 0);
1016 break;
1017 default: {
1018 int i = rn2(SIZE(level_detects));
1019 You_see("%s, %s.", level_detects[i].what,
1020 level_distance(level_detects[i].where));
1022 ret = 0;
1023 break;
1026 if (ret) {
1027 if (!rn2(100)) /* make them nervous */
1028 You_see("the Wizard of Yendor gazing out at you.");
1029 else
1030 pline_The("vision is unclear.");
1033 return;
1036 STATIC_OVL void
1037 show_map_spot(x, y)
1038 register int x, y;
1040 struct rm *lev;
1041 struct trap *t;
1042 int oldglyph;
1044 if (Confusion && rn2(7))
1045 return;
1046 lev = &levl[x][y];
1048 lev->seenv = SVALL;
1050 /* Secret corridors are found, but not secret doors. */
1051 if (lev->typ == SCORR) {
1052 lev->typ = CORR;
1053 unblock_point(x, y);
1057 * Force the real background, then if it's not furniture and there's
1058 * a known trap there, display the trap, else if there was an object
1059 * shown there, redisplay the object. So during mapping, furniture
1060 * takes precedence over traps, which take precedence over objects,
1061 * opposite to how normal vision behaves.
1063 oldglyph = glyph_at(x, y);
1064 if (level.flags.hero_memory) {
1065 magic_map_background(x, y, 0);
1066 newsym(x, y); /* show it, if not blocked */
1067 } else {
1068 magic_map_background(x, y, 1); /* display it */
1070 if (!IS_FURNITURE(lev->typ)) {
1071 if ((t = t_at(x, y)) != 0 && t->tseen) {
1072 map_trap(t, 1);
1073 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1074 show_glyph(x, y, oldglyph);
1075 if (level.flags.hero_memory)
1076 lev->glyph = oldglyph;
1081 void
1082 do_mapping()
1084 register int zx, zy;
1086 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
1087 u.uinwater = u.uburied = 0;
1088 for (zx = 1; zx < COLNO; zx++)
1089 for (zy = 0; zy < ROWNO; zy++)
1090 show_map_spot(zx, zy);
1091 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
1092 if (!level.flags.hero_memory || Underwater) {
1093 flush_screen(1); /* flush temp screen */
1094 display_nhwindow(WIN_MAP, TRUE); /* wait */
1095 docrt();
1097 exercise(A_WIS, TRUE);
1100 void
1101 do_vicinity_map()
1103 register int zx, zy;
1104 int lo_y = (u.uy - 5 < 0 ? 0 : u.uy - 5),
1105 hi_y = (u.uy + 6 > ROWNO ? ROWNO : u.uy + 6),
1106 lo_x = (u.ux - 9 < 1 ? 1 : u.ux - 9), /* avoid column 0 */
1107 hi_x = (u.ux + 10 > COLNO ? COLNO : u.ux + 10);
1109 for (zx = lo_x; zx < hi_x; zx++)
1110 for (zy = lo_y; zy < hi_y; zy++)
1111 show_map_spot(zx, zy);
1113 if (!level.flags.hero_memory || Underwater) {
1114 flush_screen(1); /* flush temp screen */
1115 display_nhwindow(WIN_MAP, TRUE); /* wait */
1116 docrt();
1120 /* convert a secret door into a normal door */
1121 void
1122 cvt_sdoor_to_door(lev)
1123 struct rm *lev;
1125 int newmask = lev->doormask & ~WM_MASK;
1127 if (Is_rogue_level(&u.uz))
1128 /* rogue didn't have doors, only doorways */
1129 newmask = D_NODOOR;
1130 else
1131 /* newly exposed door is closed */
1132 if (!(newmask & D_LOCKED))
1133 newmask |= D_CLOSED;
1135 lev->typ = DOOR;
1136 lev->doormask = newmask;
1139 STATIC_PTR void
1140 findone(zx, zy, num)
1141 int zx, zy;
1142 genericptr_t num;
1144 register struct trap *ttmp;
1145 register struct monst *mtmp;
1147 if (levl[zx][zy].typ == SDOOR) {
1148 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1149 magic_map_background(zx, zy, 0);
1150 newsym(zx, zy);
1151 (*(int *) num)++;
1152 } else if (levl[zx][zy].typ == SCORR) {
1153 levl[zx][zy].typ = CORR;
1154 unblock_point(zx, zy);
1155 magic_map_background(zx, zy, 0);
1156 newsym(zx, zy);
1157 (*(int *) num)++;
1158 } else if ((ttmp = t_at(zx, zy)) != 0) {
1159 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1160 ttmp->tseen = 1;
1161 newsym(zx, zy);
1162 (*(int *) num)++;
1164 } else if ((mtmp = m_at(zx, zy)) != 0) {
1165 if (mtmp->m_ap_type) {
1166 seemimic(mtmp);
1167 (*(int *) num)++;
1169 if (mtmp->mundetected
1170 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1171 mtmp->mundetected = 0;
1172 newsym(zx, zy);
1173 (*(int *) num)++;
1175 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1176 map_invisible(zx, zy);
1177 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1178 unmap_object(zx, zy);
1179 newsym(zx, zy);
1180 (*(int *) num)++;
1184 STATIC_PTR void
1185 openone(zx, zy, num)
1186 int zx, zy;
1187 genericptr_t num;
1189 register struct trap *ttmp;
1190 register struct obj *otmp;
1191 int *num_p = (int *) num;
1193 if (OBJ_AT(zx, zy)) {
1194 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1195 if (Is_box(otmp) && otmp->olocked) {
1196 otmp->olocked = 0;
1197 (*num_p)++;
1200 /* let it fall to the next cases. could be on trap. */
1202 if (levl[zx][zy].typ == SDOOR
1203 || (levl[zx][zy].typ == DOOR
1204 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1205 if (levl[zx][zy].typ == SDOOR)
1206 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1207 if (levl[zx][zy].doormask & D_TRAPPED) {
1208 if (distu(zx, zy) < 3)
1209 b_trapped("door", 0);
1210 else
1211 Norep("You %s an explosion!",
1212 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1213 : "feel the shock of"));
1214 wake_nearto(zx, zy, 11 * 11);
1215 levl[zx][zy].doormask = D_NODOOR;
1216 } else
1217 levl[zx][zy].doormask = D_ISOPEN;
1218 unblock_point(zx, zy);
1219 newsym(zx, zy);
1220 (*num_p)++;
1221 } else if (levl[zx][zy].typ == SCORR) {
1222 levl[zx][zy].typ = CORR;
1223 unblock_point(zx, zy);
1224 newsym(zx, zy);
1225 (*num_p)++;
1226 } else if ((ttmp = t_at(zx, zy)) != 0) {
1227 struct monst *mon;
1228 boolean dummy; /* unneeded "you notice it arg" */
1230 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1231 ttmp->tseen = 1;
1232 newsym(zx, zy);
1233 (*num_p)++;
1235 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1236 if (openholdingtrap(mon, &dummy)
1237 || openfallingtrap(mon, TRUE, &dummy))
1238 (*num_p)++;
1239 } else if (find_drawbridge(&zx, &zy)) {
1240 /* make sure it isn't an open drawbridge */
1241 open_drawbridge(zx, zy);
1242 (*num_p)++;
1246 /* returns number of things found */
1248 findit()
1250 int num = 0;
1252 if (u.uswallow)
1253 return 0;
1254 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1255 return num;
1258 /* returns number of things found and opened */
1260 openit()
1262 int num = 0;
1264 if (u.uswallow) {
1265 if (is_animal(u.ustuck->data)) {
1266 if (Blind)
1267 pline("Its mouth opens!");
1268 else
1269 pline("%s opens its mouth!", Monnam(u.ustuck));
1271 expels(u.ustuck, u.ustuck->data, TRUE);
1272 return -1;
1275 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1276 return num;
1279 /* callback hack for overriding vision in do_clear_area() */
1280 boolean
1281 detecting(func)
1282 void FDECL((*func), (int, int, genericptr_t));
1284 return (func == findone || func == openone);
1287 void
1288 find_trap(trap)
1289 struct trap *trap;
1291 int tt = what_trap(trap->ttyp);
1292 boolean cleared = FALSE;
1294 trap->tseen = 1;
1295 exercise(A_WIS, TRUE);
1296 feel_newsym(trap->tx, trap->ty);
1298 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1299 /* There's too much clutter to see your find otherwise */
1300 cls();
1301 map_trap(trap, 1);
1302 display_self();
1303 cleared = TRUE;
1306 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1308 if (cleared) {
1309 display_nhwindow(WIN_MAP, TRUE); /* wait */
1310 docrt();
1315 dosearch0(aflag)
1316 register int aflag; /* intrinsic autosearch vs explicit searching */
1318 #ifdef GCC_BUG
1319 /* some versions of gcc seriously muck up nested loops. if you get strange
1320 crashes while searching in a version compiled with gcc, try putting
1321 #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
1322 makefile).
1324 volatile xchar x, y;
1325 #else
1326 register xchar x, y;
1327 #endif
1328 register struct trap *trap;
1329 register struct monst *mtmp;
1331 if (u.uswallow) {
1332 if (!aflag)
1333 pline("What are you looking for? The exit?");
1334 } else {
1335 int fund = (uwep && uwep->oartifact
1336 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1338 if (ublindf && ublindf->otyp == LENSES && !Blind)
1339 fund += 2; /* JDS: lenses help searching */
1340 if (fund > 5)
1341 fund = 5;
1342 for (x = u.ux - 1; x < u.ux + 2; x++)
1343 for (y = u.uy - 1; y < u.uy + 2; y++) {
1344 if (!isok(x, y))
1345 continue;
1346 if (x == u.ux && y == u.uy)
1347 continue;
1349 if (Blind && !aflag)
1350 feel_location(x, y);
1351 if (levl[x][y].typ == SDOOR) {
1352 if (rnl(7 - fund))
1353 continue;
1354 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1355 exercise(A_WIS, TRUE);
1356 nomul(0);
1357 feel_location(x, y); /* make sure it shows up */
1358 You("find a hidden door.");
1359 } else if (levl[x][y].typ == SCORR) {
1360 if (rnl(7 - fund))
1361 continue;
1362 levl[x][y].typ = CORR;
1363 unblock_point(x, y); /* vision */
1364 exercise(A_WIS, TRUE);
1365 nomul(0);
1366 feel_newsym(x, y); /* make sure it shows up */
1367 You("find a hidden passage.");
1368 } else {
1369 /* Be careful not to find anything in an SCORR or SDOOR */
1370 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
1371 if (mtmp->m_ap_type) {
1372 seemimic(mtmp);
1373 find:
1374 exercise(A_WIS, TRUE);
1375 if (!canspotmon(mtmp)) {
1376 if (glyph_is_invisible(levl[x][y].glyph)) {
1377 /* found invisible monster in a square
1378 * which already has an 'I' in it.
1379 * Logically, this should still take
1380 * time and lead to a return(1), but
1381 * if we did that the player would keep
1382 * finding the same monster every turn.
1384 continue;
1385 } else {
1386 You_feel("an unseen monster!");
1387 map_invisible(x, y);
1389 } else if (!sensemon(mtmp))
1390 You("find %s.", mtmp->mtame
1391 ? y_monnam(mtmp)
1392 : a_monnam(mtmp));
1393 return 1;
1395 if (!canspotmon(mtmp)) {
1396 if (mtmp->mundetected
1397 && (is_hider(mtmp->data)
1398 || mtmp->data->mlet == S_EEL))
1399 mtmp->mundetected = 0;
1400 newsym(x, y);
1401 goto find;
1405 /* see if an invisible monster has moved--if Blind,
1406 * feel_location() already did it
1408 if (!aflag && !mtmp && !Blind
1409 && glyph_is_invisible(levl[x][y].glyph)) {
1410 unmap_object(x, y);
1411 newsym(x, y);
1414 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
1415 nomul(0);
1416 if (trap->ttyp == STATUE_TRAP) {
1417 if (activate_statue_trap(trap, x, y, FALSE))
1418 exercise(A_WIS, TRUE);
1419 return 1;
1420 } else {
1421 find_trap(trap);
1427 return 1;
1430 /* the 's' command -- explicit searching */
1432 dosearch()
1434 return dosearch0(0);
1437 /* Pre-map the sokoban levels */
1438 void
1439 sokoban_detect()
1441 register int x, y;
1442 register struct trap *ttmp;
1443 register struct obj *obj;
1445 /* Map the background and boulders */
1446 for (x = 1; x < COLNO; x++)
1447 for (y = 0; y < ROWNO; y++) {
1448 levl[x][y].seenv = SVALL;
1449 levl[x][y].waslit = TRUE;
1450 map_background(x, y, 1);
1451 if ((obj = sobj_at(BOULDER, x, y)) != 0)
1452 map_object(obj, 1);
1455 /* Map the traps */
1456 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1457 ttmp->tseen = 1;
1458 map_trap(ttmp, 1);
1459 /* set sokoban_rules when there is at least one pit or hole */
1460 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
1461 Sokoban = 1;
1465 /* idea from crawl; show known portion of map without any monsters,
1466 objects, or traps occluding the view of the underlying terrain */
1467 void
1468 reveal_terrain(full, which_subset)
1469 int full; /* wizard|explore modes allow player to request full map */
1470 int which_subset; /* when not full, whether to suppress objs and/or traps */
1472 if ((Hallucination || Stunned || Confusion) && !full) {
1473 You("are too disoriented for this.");
1474 } else {
1475 int x, y, glyph, levl_glyph, default_glyph;
1476 uchar seenv;
1477 unsigned save_swallowed;
1478 struct monst *mtmp;
1479 struct trap *t;
1480 char buf[BUFSZ];
1481 boolean keep_traps = (which_subset & 1) !=0,
1482 keep_objs = (which_subset & 2) != 0,
1483 keep_mons = (which_subset & 4) != 0; /* actually always 0 */
1485 save_swallowed = u.uswallow;
1486 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
1487 u.uinwater = u.uburied = 0;
1488 u.uswallow = 0;
1489 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1490 /* for 'full', show the actual terrain for the entire level,
1491 otherwise what the hero remembers for seen locations with
1492 monsters, objects, and/or traps removed as caller dictates */
1493 for (x = 1; x < COLNO; x++)
1494 for (y = 0; y < ROWNO; y++) {
1495 seenv = (full || level.flags.hero_memory)
1496 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
1497 if (full) {
1498 levl[x][y].seenv = SVALL;
1499 glyph = back_to_glyph(x, y);
1500 levl[x][y].seenv = seenv;
1501 } else {
1502 levl_glyph = level.flags.hero_memory
1503 ? levl[x][y].glyph
1504 : seenv
1505 ? back_to_glyph(x, y)
1506 : default_glyph;
1507 /* glyph_at() returns the displayed glyph, which might
1508 be a monster. levl[][].glyph contains the remembered
1509 glyph, which will never be a monster (unless it is
1510 the invisible monster glyph, which is handled like
1511 an object, replacing any object or trap at its spot) */
1512 glyph = !save_swallowed ? glyph_at(x, y) : levl_glyph;
1513 if (keep_mons && x == u.ux && y == u.uy && save_swallowed)
1514 glyph = mon_to_glyph(u.ustuck);
1515 else if (((glyph_is_monster(glyph)
1516 || glyph_is_warning(glyph)) && !keep_mons)
1517 || glyph_is_swallow(glyph))
1518 glyph = levl_glyph;
1519 if (((glyph_is_object(glyph) && !keep_objs)
1520 || glyph_is_invisible(glyph))
1521 && keep_traps && !covers_traps(x, y)) {
1522 if ((t = t_at(x, y)) != 0 && t->tseen)
1523 glyph = trap_to_glyph(t);
1525 if ((glyph_is_object(glyph) && !keep_objs)
1526 || (glyph_is_trap(glyph) && !keep_traps)
1527 || glyph_is_invisible(glyph)) {
1528 if (!seenv) {
1529 glyph = default_glyph;
1530 } else if (lastseentyp[x][y] == levl[x][y].typ) {
1531 glyph = back_to_glyph(x, y);
1532 } else {
1533 /* look for a mimic here posing as furniture;
1534 if we don't find one, we'll have to fake it */
1535 if ((mtmp = m_at(x, y)) != 0
1536 && mtmp->m_ap_type == M_AP_FURNITURE) {
1537 glyph = cmap_to_glyph(mtmp->mappearance);
1538 } else {
1539 /* we have a topology type but we want a
1540 screen symbol in order to derive a glyph;
1541 some screen symbols need the flags field
1542 of levl[][] in addition to the type
1543 (to disambiguate STAIRS to S_upstair or
1544 S_dnstair, for example; current flags
1545 might not be intended for remembered
1546 type, but we've got no other choice) */
1547 schar save_typ = levl[x][y].typ;
1549 levl[x][y].typ = lastseentyp[x][y];
1550 glyph = back_to_glyph(x, y);
1551 levl[x][y].typ = save_typ;
1556 if (glyph == cmap_to_glyph(S_darkroom))
1557 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
1558 show_glyph(x, y, glyph);
1561 /* [TODO: highlight hero's location somehow] */
1562 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
1563 if (save_swallowed)
1564 u.uswallow = 1;
1565 flush_screen(1);
1566 if (full) {
1567 Strcpy(buf, "underlying terrain");
1568 } else {
1569 Strcpy(buf, "known terrain");
1570 if (keep_traps)
1571 Sprintf(eos(buf), "%s traps",
1572 (keep_objs || keep_mons) ? "," : " and");
1573 if (keep_objs)
1574 Sprintf(eos(buf), "%s%s objects",
1575 (keep_traps || keep_mons) ? "," : "",
1576 keep_mons ? "" : " and");
1577 if (keep_mons)
1578 Sprintf(eos(buf), "%s and monsters",
1579 (keep_traps || keep_objs) ? "," : "");
1581 pline("Showing %s only...", buf);
1582 display_nhwindow(WIN_MAP, TRUE); /* give "--More--" prompt */
1583 docrt(); /* redraw the screen, restoring regular map */
1584 if (Underwater)
1585 under_water(2);
1586 if (u.uburied)
1587 under_ground(2);
1589 return;
1592 /*detect.c*/