mplayer.c formatting
[aNetHack.git] / src / detect.c
blobe2214d20d50bd2078bf5c297a739fd961ba90a2a
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));
23 STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P));
25 /* Recursively search obj for an object in class oclass and return 1st found
27 struct obj *
28 o_in(obj, oclass)
29 struct obj *obj;
30 char oclass;
32 register struct obj *otmp;
33 struct obj *temp;
35 if (obj->oclass == oclass)
36 return obj;
38 if (Has_contents(obj)) {
39 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
40 if (otmp->oclass == oclass)
41 return otmp;
42 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)))
43 return temp;
45 return (struct obj *) 0;
48 /* Recursively search obj for an object made of specified material.
49 * Return first found.
51 struct obj *
52 o_material(obj, material)
53 struct obj *obj;
54 unsigned material;
56 register struct obj *otmp;
57 struct obj *temp;
59 if (objects[obj->otyp].oc_material == material)
60 return obj;
62 if (Has_contents(obj)) {
63 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
64 if (objects[otmp->otyp].oc_material == material)
65 return otmp;
66 else if (Has_contents(otmp)
67 && (temp = o_material(otmp, material)))
68 return temp;
70 return (struct obj *) 0;
73 STATIC_OVL void
74 do_dknown_of(obj)
75 struct obj *obj;
77 struct obj *otmp;
79 obj->dknown = 1;
80 if (Has_contents(obj)) {
81 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
82 do_dknown_of(otmp);
86 /* Check whether the location has an outdated object displayed on it. */
87 STATIC_OVL boolean
88 check_map_spot(x, y, oclass, material)
89 int x, y;
90 char oclass;
91 unsigned material;
93 int glyph;
94 register struct obj *otmp;
95 register struct monst *mtmp;
97 glyph = glyph_at(x, y);
98 if (glyph_is_object(glyph)) {
99 /* there's some object shown here */
100 if (oclass == ALL_CLASSES) {
101 return (boolean) !(level.objects[x][y] /* stale if nothing here */
102 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
103 } else {
104 if (material
105 && objects[glyph_to_obj(glyph)].oc_material == material) {
106 /* object shown here is of interest because material matches */
107 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
108 if (o_material(otmp, GOLD))
109 return FALSE;
110 /* didn't find it; perhaps a monster is carrying it */
111 if ((mtmp = m_at(x, y)) != 0) {
112 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
113 if (o_material(otmp, GOLD))
114 return FALSE;
116 /* detection indicates removal of this object from the map */
117 return TRUE;
119 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
120 /* obj shown here is of interest because its class matches */
121 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
122 if (o_in(otmp, oclass))
123 return FALSE;
124 /* didn't find it; perhaps a monster is carrying it */
125 if ((mtmp = m_at(x, y)) != 0) {
126 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
127 if (o_in(otmp, oclass))
128 return FALSE;
130 /* detection indicates removal of this object from the map */
131 return TRUE;
135 return FALSE;
139 * When doing detection, remove stale data from the map display (corpses
140 * rotted away, objects carried away by monsters, etc) so that it won't
141 * reappear after the detection has completed. Return true if noticeable
142 * change occurs.
144 STATIC_OVL boolean
145 clear_stale_map(oclass, material)
146 char oclass;
147 unsigned material;
149 register int zx, zy;
150 boolean change_made = FALSE;
152 for (zx = 1; zx < COLNO; zx++)
153 for (zy = 0; zy < ROWNO; zy++)
154 if (check_map_spot(zx, zy, oclass, material)) {
155 unmap_object(zx, zy);
156 change_made = TRUE;
159 return change_made;
162 /* look for gold, on the floor or in monsters' possession */
164 gold_detect(sobj)
165 register struct obj *sobj;
167 register struct obj *obj;
168 register struct monst *mtmp;
169 struct obj *temp;
170 boolean stale;
172 known = stale =
173 clear_stale_map(COIN_CLASS, (unsigned) (sobj->blessed ? GOLD : 0));
175 /* look for gold carried by monsters (might be in a container) */
176 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
177 if (DEADMONSTER(mtmp))
178 continue; /* probably not needed in this case but... */
179 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
180 known = TRUE;
181 goto outgoldmap; /* skip further searching */
182 } else
183 for (obj = mtmp->minvent; obj; obj = obj->nobj)
184 if (sobj->blessed && o_material(obj, GOLD)) {
185 known = TRUE;
186 goto outgoldmap;
187 } else if (o_in(obj, COIN_CLASS)) {
188 known = TRUE;
189 goto outgoldmap; /* skip further searching */
193 /* look for gold objects */
194 for (obj = fobj; obj; obj = obj->nobj) {
195 if (sobj->blessed && o_material(obj, GOLD)) {
196 known = TRUE;
197 if (obj->ox != u.ux || obj->oy != u.uy)
198 goto outgoldmap;
199 } else if (o_in(obj, COIN_CLASS)) {
200 known = TRUE;
201 if (obj->ox != u.ux || obj->oy != u.uy)
202 goto outgoldmap;
206 if (!known) {
207 /* no gold found on floor or monster's inventory.
208 adjust message if you have gold in your inventory */
209 if (sobj) {
210 char buf[BUFSZ];
211 if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
212 Sprintf(buf, "You feel like a million %s!", currency(2L));
213 } else if (hidden_gold() || money_cnt(invent))
214 Strcpy(buf,
215 "You feel worried about your future financial situation.");
216 else
217 Strcpy(buf, "You feel materially poor.");
218 strange_feeling(sobj, buf);
220 return 1;
222 /* only under me - no separate display required */
223 if (stale)
224 docrt();
225 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
226 return 0;
228 outgoldmap:
229 cls();
231 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
232 u.uinwater = u.uburied = 0;
233 /* Discover gold locations. */
234 for (obj = fobj; obj; obj = obj->nobj) {
235 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
236 if (temp != obj) {
237 temp->ox = obj->ox;
238 temp->oy = obj->oy;
240 map_object(temp, 1);
241 } else if ((temp = o_in(obj, COIN_CLASS))) {
242 if (temp != obj) {
243 temp->ox = obj->ox;
244 temp->oy = obj->oy;
246 map_object(temp, 1);
249 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
250 if (DEADMONSTER(mtmp))
251 continue; /* probably overkill here */
252 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
253 struct obj gold;
254 gold = zeroobj; /* ensure oextra is cleared too */
255 gold.otyp = GOLD_PIECE;
256 gold.ox = mtmp->mx;
257 gold.oy = mtmp->my;
258 map_object(&gold, 1);
259 } else
260 for (obj = mtmp->minvent; obj; obj = obj->nobj)
261 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
262 temp->ox = mtmp->mx;
263 temp->oy = mtmp->my;
264 map_object(temp, 1);
265 break;
266 } else if ((temp = o_in(obj, COIN_CLASS))) {
267 temp->ox = mtmp->mx;
268 temp->oy = mtmp->my;
269 map_object(temp, 1);
270 break;
273 newsym(u.ux, u.uy);
274 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
275 You_feel("very greedy, and sense gold!");
276 exercise(A_WIS, TRUE);
277 display_nhwindow(WIN_MAP, TRUE);
278 docrt();
279 if (Underwater)
280 under_water(2);
281 if (u.uburied)
282 under_ground(2);
283 return 0;
286 /* returns 1 if nothing was detected */
287 /* returns 0 if something was detected */
289 food_detect(sobj)
290 register struct obj *sobj;
292 register struct obj *obj;
293 register struct monst *mtmp;
294 register int ct = 0, ctu = 0;
295 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
296 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
297 const char *what = confused ? something : "food";
299 stale = clear_stale_map(oclass, 0);
301 for (obj = fobj; obj; obj = obj->nobj)
302 if (o_in(obj, oclass)) {
303 if (obj->ox == u.ux && obj->oy == u.uy)
304 ctu++;
305 else
306 ct++;
308 for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
309 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory
311 for (obj = mtmp->minvent; obj; obj = obj->nobj)
312 if (o_in(obj, oclass)) {
313 ct++;
314 break;
318 if (!ct && !ctu) {
319 known = stale && !confused;
320 if (stale) {
321 docrt();
322 You("sense a lack of %s nearby.", what);
323 if (sobj && sobj->blessed) {
324 if (!u.uedibility)
325 Your("%s starts to tingle.", body_part(NOSE));
326 u.uedibility = 1;
328 } else if (sobj) {
329 char buf[BUFSZ];
330 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
331 (sobj->blessed && !u.uedibility)
332 ? " then starts to tingle"
333 : "");
334 if (sobj->blessed && !u.uedibility) {
335 boolean savebeginner = flags.beginner;
337 flags.beginner = FALSE; /* prevent non-delivery of message */
338 strange_feeling(sobj, buf);
339 flags.beginner = savebeginner;
340 u.uedibility = 1;
341 } else
342 strange_feeling(sobj, buf);
344 return !stale;
345 } else if (!ct) {
346 known = TRUE;
347 You("%s %s nearby.", sobj ? "smell" : "sense", what);
348 if (sobj && sobj->blessed) {
349 if (!u.uedibility)
350 pline("Your %s starts to tingle.", body_part(NOSE));
351 u.uedibility = 1;
353 } else {
354 struct obj *temp;
355 known = TRUE;
356 cls();
357 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
358 u.uinwater = u.uburied = 0;
359 for (obj = fobj; obj; obj = obj->nobj)
360 if ((temp = o_in(obj, oclass)) != 0) {
361 if (temp != obj) {
362 temp->ox = obj->ox;
363 temp->oy = obj->oy;
365 map_object(temp, 1);
367 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
368 /* no DEADMONSTER(mtmp) check needed since dmons never have
369 * inventory */
370 for (obj = mtmp->minvent; obj; obj = obj->nobj)
371 if ((temp = o_in(obj, oclass)) != 0) {
372 temp->ox = mtmp->mx;
373 temp->oy = mtmp->my;
374 map_object(temp, 1);
375 break; /* skip rest of this monster's inventory */
377 newsym(u.ux, u.uy);
378 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
379 if (sobj) {
380 if (sobj->blessed) {
381 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
382 u.uedibility ? "continues" : "starts", what);
383 u.uedibility = 1;
384 } else
385 Your("%s tingles and you smell %s.", body_part(NOSE), what);
386 } else
387 You("sense %s.", what);
388 display_nhwindow(WIN_MAP, TRUE);
389 exercise(A_WIS, TRUE);
390 docrt();
391 if (Underwater)
392 under_water(2);
393 if (u.uburied)
394 under_ground(2);
396 return 0;
400 * Used for scrolls, potions, spells, and crystal balls. Returns:
402 * 1 - nothing was detected
403 * 0 - something was detected
406 object_detect(detector, class)
407 struct obj *detector; /* object doing the detecting */
408 int class; /* an object class, 0 for all */
410 register int x, y;
411 char stuff[BUFSZ];
412 int is_cursed = (detector && detector->cursed);
413 int do_dknown = (detector && (detector->oclass == POTION_CLASS
414 || detector->oclass == SPBOOK_CLASS)
415 && detector->blessed);
416 int ct = 0, ctu = 0;
417 register struct obj *obj, *otmp = (struct obj *) 0;
418 register struct monst *mtmp;
419 int sym, boulder = 0;
421 if (class < 0 || class >= MAXOCLASSES) {
422 impossible("object_detect: illegal class %d", class);
423 class = 0;
426 /* Special boulder symbol check - does the class symbol happen
427 * to match iflags.bouldersym which is a user-defined?
428 * If so, that means we aren't sure what they really wanted to
429 * detect. Rather than trump anything, show both possibilities.
430 * We can exclude checking the buried obj chain for boulders below.
432 sym = class ? def_oc_syms[class].sym : 0;
433 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
434 boulder = ROCK_CLASS;
436 if (Hallucination || (Confusion && class == SCROLL_CLASS))
437 Strcpy(stuff, something);
438 else
439 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
440 if (boulder && class != ROCK_CLASS)
441 Strcat(stuff, " and/or large stones");
443 if (do_dknown)
444 for (obj = invent; obj; obj = obj->nobj)
445 do_dknown_of(obj);
447 for (obj = fobj; obj; obj = obj->nobj) {
448 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
449 if (obj->ox == u.ux && obj->oy == u.uy)
450 ctu++;
451 else
452 ct++;
454 if (do_dknown)
455 do_dknown_of(obj);
458 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
459 if (!class || o_in(obj, class)) {
460 if (obj->ox == u.ux && obj->oy == u.uy)
461 ctu++;
462 else
463 ct++;
465 if (do_dknown)
466 do_dknown_of(obj);
469 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
470 if (DEADMONSTER(mtmp))
471 continue;
472 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
473 if ((!class && !boulder) || o_in(obj, class)
474 || o_in(obj, boulder))
475 ct++;
476 if (do_dknown)
477 do_dknown_of(obj);
479 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
480 && (!class || class == objects[mtmp->mappearance].oc_class))
481 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
482 ct++;
483 break;
487 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
488 if (!ctu) {
489 if (detector)
490 strange_feeling(detector, "You feel a lack of something.");
491 return 1;
494 You("sense %s nearby.", stuff);
495 return 0;
498 cls();
500 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
501 u.uinwater = u.uburied = 0;
503 * Map all buried objects first.
505 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
506 if (!class || (otmp = o_in(obj, class))) {
507 if (class) {
508 if (otmp != obj) {
509 otmp->ox = obj->ox;
510 otmp->oy = obj->oy;
512 map_object(otmp, 1);
513 } else
514 map_object(obj, 1);
517 * If we are mapping all objects, map only the top object of a pile or
518 * the first object in a monster's inventory. Otherwise, go looking
519 * for a matching object class and display the first one encountered
520 * at each location.
522 * Objects on the floor override buried objects.
524 for (x = 1; x < COLNO; x++)
525 for (y = 0; y < ROWNO; y++)
526 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
527 if ((!class && !boulder) || (otmp = o_in(obj, class))
528 || (otmp = o_in(obj, boulder))) {
529 if (class || boulder) {
530 if (otmp != obj) {
531 otmp->ox = obj->ox;
532 otmp->oy = obj->oy;
534 map_object(otmp, 1);
535 } else
536 map_object(obj, 1);
537 break;
540 /* Objects in the monster's inventory override floor objects. */
541 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
542 if (DEADMONSTER(mtmp))
543 continue;
544 for (obj = mtmp->minvent; obj; obj = obj->nobj)
545 if ((!class && !boulder) || (otmp = o_in(obj, class))
546 || (otmp = o_in(obj, boulder))) {
547 if (!class && !boulder)
548 otmp = obj;
549 otmp->ox = mtmp->mx; /* at monster location */
550 otmp->oy = mtmp->my;
551 map_object(otmp, 1);
552 break;
554 /* Allow a mimic to override the detected objects it is carrying. */
555 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
556 && (!class || class == objects[mtmp->mappearance].oc_class)) {
557 struct obj temp;
559 temp.oextra = (struct oextra *) 0;
560 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
561 temp.ox = mtmp->mx;
562 temp.oy = mtmp->my;
563 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
564 map_object(&temp, 1);
565 } else if (findgold(mtmp->minvent)
566 && (!class || class == COIN_CLASS)) {
567 struct obj gold;
568 gold = zeroobj; /* ensure oextra is cleared too */
569 gold.otyp = GOLD_PIECE;
570 gold.ox = mtmp->mx;
571 gold.oy = mtmp->my;
572 map_object(&gold, 1);
576 newsym(u.ux, u.uy);
577 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
578 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
579 display_nhwindow(WIN_MAP, TRUE);
581 * What are we going to do when the hero does an object detect while blind
582 * and the detected object covers a known pool?
584 docrt(); /* this will correctly reset vision */
586 if (Underwater)
587 under_water(2);
588 if (u.uburied)
589 under_ground(2);
590 return 0;
594 * Used by: crystal balls, potions, fountains
596 * Returns 1 if nothing was detected.
597 * Returns 0 if something was detected.
600 monster_detect(otmp, mclass)
601 register struct obj *otmp; /* detecting object (if any) */
602 int mclass; /* monster class, 0 for all */
604 register struct monst *mtmp;
605 int mcnt = 0;
607 /* Note: This used to just check fmon for a non-zero value
608 * but in versions since 3.3.0 fmon can test TRUE due to the
609 * presence of dmons, so we have to find at least one
610 * with positive hit-points to know for sure.
612 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
613 if (!DEADMONSTER(mtmp)) {
614 mcnt++;
615 break;
618 if (!mcnt) {
619 if (otmp)
620 strange_feeling(otmp, Hallucination
621 ? "You get the heebie jeebies."
622 : "You feel threatened.");
623 return 1;
624 } else {
625 boolean woken = FALSE;
627 cls();
628 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
629 if (DEADMONSTER(mtmp))
630 continue;
631 if (!mclass || mtmp->data->mlet == mclass
632 || (mtmp->data == &mons[PM_LONG_WORM]
633 && mclass == S_WORM_TAIL))
634 if (mtmp->mx > 0) {
635 if (mclass && def_monsyms[mclass].sym == ' ')
636 show_glyph(mtmp->mx, mtmp->my,
637 detected_mon_to_glyph(mtmp));
638 else
639 show_glyph(mtmp->mx, mtmp->my,
640 mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
641 /* don't be stingy - display entire worm */
642 if (mtmp->data == &mons[PM_LONG_WORM])
643 detect_wsegs(mtmp, 0);
645 if (otmp && otmp->cursed
646 && (mtmp->msleeping || !mtmp->mcanmove)) {
647 mtmp->msleeping = mtmp->mfrozen = 0;
648 mtmp->mcanmove = 1;
649 woken = TRUE;
652 display_self();
653 You("sense the presence of monsters.");
654 if (woken)
655 pline("Monsters sense the presence of you.");
656 display_nhwindow(WIN_MAP, TRUE);
657 docrt();
658 if (Underwater)
659 under_water(2);
660 if (u.uburied)
661 under_ground(2);
663 return 0;
666 STATIC_OVL void
667 sense_trap(trap, x, y, src_cursed)
668 struct trap *trap;
669 xchar x, y;
670 int src_cursed;
672 if (Hallucination || src_cursed) {
673 struct obj obj; /* fake object */
675 obj.oextra = (struct oextra *) 0;
676 if (trap) {
677 obj.ox = trap->tx;
678 obj.oy = trap->ty;
679 } else {
680 obj.ox = x;
681 obj.oy = y;
683 obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
684 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
685 map_object(&obj, 1);
686 } else if (trap) {
687 map_trap(trap, 1);
688 trap->tseen = 1;
689 } else {
690 struct trap temp_trap; /* fake trap */
691 temp_trap.tx = x;
692 temp_trap.ty = y;
693 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
694 map_trap(&temp_trap, 1);
698 #define OTRAP_NONE 0 /* nothing found */
699 #define OTRAP_HERE 1 /* found at hero's location */
700 #define OTRAP_THERE 2 /* found at any other location */
702 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
703 2 if found at some other spot, 3 if both, 0 otherwise; optionally
704 update the map to show where such traps were found */
705 STATIC_OVL int
706 detect_obj_traps(objlist, show_them, how)
707 struct obj *objlist;
708 boolean show_them;
709 int how; /* 1 for misleading map feedback */
711 struct obj *otmp;
712 xchar x, y;
713 int result = OTRAP_NONE;
715 for (otmp = objlist; otmp; otmp = otmp->nobj) {
716 if (Is_box(otmp) && otmp->otrapped
717 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
718 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
719 if (show_them)
720 sense_trap((struct trap *) 0, x, y, how);
722 if (Has_contents(otmp))
723 result |= detect_obj_traps(otmp->cobj, show_them, how);
725 return result;
728 /* the detections are pulled out so they can
729 * also be used in the crystal ball routine
730 * returns 1 if nothing was detected
731 * returns 0 if something was detected
734 trap_detect(sobj)
735 register struct obj *sobj;
736 /* sobj is null if crystal ball, *scroll if gold detection scroll */
738 register struct trap *ttmp;
739 struct monst *mon;
740 int door, glyph, tr;
741 int cursed_src = sobj && sobj->cursed;
742 boolean found = FALSE;
743 coord cc;
745 /* floor/ceiling traps */
746 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
747 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
748 goto outtrapmap;
749 else
750 found = TRUE;
752 /* chest traps (might be buried or carried) */
753 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
754 if (tr & OTRAP_THERE)
755 goto outtrapmap;
756 else
757 found = TRUE;
759 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0))
760 != OTRAP_NONE) {
761 if (tr & OTRAP_THERE)
762 goto outtrapmap;
763 else
764 found = TRUE;
766 for (mon = fmon; mon; mon = mon->nmon) {
767 if (DEADMONSTER(mon))
768 continue;
769 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
770 if (tr & OTRAP_THERE)
771 goto outtrapmap;
772 else
773 found = TRUE;
776 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
777 found = TRUE;
778 /* door traps */
779 for (door = 0; door < doorindex; door++) {
780 cc = doors[door];
781 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
782 if (cc.x != u.ux || cc.y != u.uy)
783 goto outtrapmap;
784 else
785 found = TRUE;
788 if (!found) {
789 char buf[BUFSZ];
791 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
792 strange_feeling(sobj, buf);
793 return 1;
795 /* traps exist, but only under me - no separate display required */
796 Your("%s itch.", makeplural(body_part(TOE)));
797 return 0;
798 outtrapmap:
799 cls();
801 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
802 u.uinwater = u.uburied = 0;
804 /* show chest traps first, so that subsequent floor trap display
805 will override if both types are present at the same location */
806 (void) detect_obj_traps(fobj, TRUE, cursed_src);
807 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
808 for (mon = fmon; mon; mon = mon->nmon) {
809 if (DEADMONSTER(mon))
810 continue;
811 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
813 (void) detect_obj_traps(invent, TRUE, cursed_src);
815 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
816 sense_trap(ttmp, 0, 0, cursed_src);
818 for (door = 0; door < doorindex; door++) {
819 cc = doors[door];
820 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
821 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
824 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
825 glyph = glyph_at(u.ux, u.uy);
826 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph)))
827 newsym(u.ux, u.uy);
828 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
830 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
831 /* wait for user to respond, then reset map display to normal */
832 display_nhwindow(WIN_MAP, TRUE);
833 docrt();
834 if (Underwater)
835 under_water(2);
836 if (u.uburied)
837 under_ground(2);
838 return 0;
841 const char *
842 level_distance(where)
843 d_level *where;
845 register schar ll = depth(&u.uz) - depth(where);
846 register boolean indun = (u.uz.dnum == where->dnum);
848 if (ll < 0) {
849 if (ll < (-8 - rn2(3)))
850 if (!indun)
851 return "far away";
852 else
853 return "far below";
854 else if (ll < -1)
855 if (!indun)
856 return "away below you";
857 else
858 return "below you";
859 else if (!indun)
860 return "in the distance";
861 else
862 return "just below";
863 } else if (ll > 0) {
864 if (ll > (8 + rn2(3)))
865 if (!indun)
866 return "far away";
867 else
868 return "far above";
869 else if (ll > 1)
870 if (!indun)
871 return "away above you";
872 else
873 return "above you";
874 else if (!indun)
875 return "in the distance";
876 else
877 return "just above";
878 } else if (!indun)
879 return "in the distance";
880 else
881 return "near you";
884 static const struct {
885 const char *what;
886 d_level *where;
887 } level_detects[] = {
888 { "Delphi", &oracle_level },
889 { "Medusa's lair", &medusa_level },
890 { "a castle", &stronghold_level },
891 { "the Wizard of Yendor's tower", &wiz1_level },
894 void
895 use_crystal_ball(optr)
896 struct obj **optr;
898 char ch;
899 int oops;
900 struct obj *obj = *optr;
902 if (Blind) {
903 pline("Too bad you can't see %s.", the(xname(obj)));
904 return;
906 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
907 if (oops && (obj->spe > 0)) {
908 switch (rnd(obj->oartifact ? 4 : 5)) {
909 case 1:
910 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
911 break;
912 case 2:
913 pline("%s you!", Tobjnam(obj, "confuse"));
914 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
915 break;
916 case 3:
917 if (!resists_blnd(&youmonst)) {
918 pline("%s your vision!", Tobjnam(obj, "damage"));
919 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
920 if (!Blind)
921 Your1(vision_clears);
922 } else {
923 pline("%s your vision.", Tobjnam(obj, "assault"));
924 You("are unaffected!");
926 break;
927 case 4:
928 pline("%s your mind!", Tobjnam(obj, "zap"));
929 (void) make_hallucinated(
930 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
931 break;
932 case 5:
933 pline("%s!", Tobjnam(obj, "explode"));
934 useup(obj);
935 *optr = obj = 0; /* it's gone */
936 /* physical damage cause by the shards and force */
937 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
938 KILLED_BY_AN);
939 break;
941 if (obj)
942 consume_obj_charge(obj, TRUE);
943 return;
946 if (Hallucination) {
947 if (!obj->spe) {
948 pline("All you see is funky %s haze.", hcolor((char *) 0));
949 } else {
950 switch (rnd(6)) {
951 case 1:
952 You("grok some groovy globs of incandescent lava.");
953 break;
954 case 2:
955 pline("Whoa! Psychedelic colors, %s!",
956 poly_gender() == 1 ? "babe" : "dude");
957 break;
958 case 3:
959 pline_The("crystal pulses with sinister %s light!",
960 hcolor((char *) 0));
961 break;
962 case 4:
963 You_see("goldfish swimming above fluorescent rocks.");
964 break;
965 case 5:
966 You_see(
967 "tiny snowflakes spinning around a miniature farmhouse.");
968 break;
969 default:
970 pline("Oh wow... like a kaleidoscope!");
971 break;
973 consume_obj_charge(obj, TRUE);
975 return;
978 /* read a single character */
979 if (flags.verbose)
980 You("may look for an object or monster symbol.");
981 ch = yn_function("What do you look for?", (char *) 0, '\0');
982 /* Don't filter out ' ' here; it has a use */
983 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
984 if (flags.verbose)
985 pline1(Never_mind);
986 return;
988 You("peer into %s...", the(xname(obj)));
989 nomul(-rnd(10));
990 multi_reason = "gazing into a crystal ball";
991 nomovemsg = "";
992 if (obj->spe <= 0)
993 pline_The("vision is unclear.");
994 else {
995 int class;
996 int ret = 0;
998 makeknown(CRYSTAL_BALL);
999 consume_obj_charge(obj, TRUE);
1001 /* special case: accept ']' as synonym for mimic
1002 * we have to do this before the def_char_to_objclass check
1004 if (ch == DEF_MIMIC_DEF)
1005 ch = DEF_MIMIC;
1007 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1008 ret = object_detect((struct obj *) 0, class);
1009 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1010 ret = monster_detect((struct obj *) 0, class);
1011 else if (iflags.bouldersym && (ch == iflags.bouldersym))
1012 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1013 else
1014 switch (ch) {
1015 case '^':
1016 ret = trap_detect((struct obj *) 0);
1017 break;
1018 default: {
1019 int i = rn2(SIZE(level_detects));
1020 You_see("%s, %s.", level_detects[i].what,
1021 level_distance(level_detects[i].where));
1023 ret = 0;
1024 break;
1027 if (ret) {
1028 if (!rn2(100)) /* make them nervous */
1029 You_see("the Wizard of Yendor gazing out at you.");
1030 else
1031 pline_The("vision is unclear.");
1034 return;
1037 STATIC_OVL void
1038 show_map_spot(x, y)
1039 register int x, y;
1041 struct rm *lev;
1042 struct trap *t;
1043 int oldglyph;
1045 if (Confusion && rn2(7))
1046 return;
1047 lev = &levl[x][y];
1049 lev->seenv = SVALL;
1051 /* Secret corridors are found, but not secret doors. */
1052 if (lev->typ == SCORR) {
1053 lev->typ = CORR;
1054 unblock_point(x, y);
1058 * Force the real background, then if it's not furniture and there's
1059 * a known trap there, display the trap, else if there was an object
1060 * shown there, redisplay the object. So during mapping, furniture
1061 * takes precedence over traps, which take precedence over objects,
1062 * opposite to how normal vision behaves.
1064 oldglyph = glyph_at(x, y);
1065 if (level.flags.hero_memory) {
1066 magic_map_background(x, y, 0);
1067 newsym(x, y); /* show it, if not blocked */
1068 } else {
1069 magic_map_background(x, y, 1); /* display it */
1071 if (!IS_FURNITURE(lev->typ)) {
1072 if ((t = t_at(x, y)) != 0 && t->tseen) {
1073 map_trap(t, 1);
1074 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1075 show_glyph(x, y, oldglyph);
1076 if (level.flags.hero_memory)
1077 lev->glyph = oldglyph;
1082 void
1083 do_mapping()
1085 register int zx, zy;
1087 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
1088 u.uinwater = u.uburied = 0;
1089 for (zx = 1; zx < COLNO; zx++)
1090 for (zy = 0; zy < ROWNO; zy++)
1091 show_map_spot(zx, zy);
1092 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
1093 if (!level.flags.hero_memory || Underwater) {
1094 flush_screen(1); /* flush temp screen */
1095 display_nhwindow(WIN_MAP, TRUE); /* wait */
1096 docrt();
1098 exercise(A_WIS, TRUE);
1101 void
1102 do_vicinity_map()
1104 register int zx, zy;
1105 int lo_y = (u.uy - 5 < 0 ? 0 : u.uy - 5),
1106 hi_y = (u.uy + 6 > ROWNO ? ROWNO : u.uy + 6),
1107 lo_x = (u.ux - 9 < 1 ? 1 : u.ux - 9), /* avoid column 0 */
1108 hi_x = (u.ux + 10 > COLNO ? COLNO : u.ux + 10);
1110 for (zx = lo_x; zx < hi_x; zx++)
1111 for (zy = lo_y; zy < hi_y; zy++)
1112 show_map_spot(zx, zy);
1114 if (!level.flags.hero_memory || Underwater) {
1115 flush_screen(1); /* flush temp screen */
1116 display_nhwindow(WIN_MAP, TRUE); /* wait */
1117 docrt();
1121 /* convert a secret door into a normal door */
1122 void
1123 cvt_sdoor_to_door(lev)
1124 struct rm *lev;
1126 int newmask = lev->doormask & ~WM_MASK;
1128 if (Is_rogue_level(&u.uz))
1129 /* rogue didn't have doors, only doorways */
1130 newmask = D_NODOOR;
1131 else
1132 /* newly exposed door is closed */
1133 if (!(newmask & D_LOCKED))
1134 newmask |= D_CLOSED;
1136 lev->typ = DOOR;
1137 lev->doormask = newmask;
1140 STATIC_PTR void
1141 findone(zx, zy, num)
1142 int zx, zy;
1143 genericptr_t num;
1145 register struct trap *ttmp;
1146 register struct monst *mtmp;
1148 if (levl[zx][zy].typ == SDOOR) {
1149 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1150 magic_map_background(zx, zy, 0);
1151 newsym(zx, zy);
1152 (*(int *) num)++;
1153 } else if (levl[zx][zy].typ == SCORR) {
1154 levl[zx][zy].typ = CORR;
1155 unblock_point(zx, zy);
1156 magic_map_background(zx, zy, 0);
1157 newsym(zx, zy);
1158 (*(int *) num)++;
1159 } else if ((ttmp = t_at(zx, zy)) != 0) {
1160 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1161 ttmp->tseen = 1;
1162 newsym(zx, zy);
1163 (*(int *) num)++;
1165 } else if ((mtmp = m_at(zx, zy)) != 0) {
1166 if (mtmp->m_ap_type) {
1167 seemimic(mtmp);
1168 (*(int *) num)++;
1170 if (mtmp->mundetected
1171 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1172 mtmp->mundetected = 0;
1173 newsym(zx, zy);
1174 (*(int *) num)++;
1176 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1177 map_invisible(zx, zy);
1178 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1179 unmap_object(zx, zy);
1180 newsym(zx, zy);
1181 (*(int *) num)++;
1185 STATIC_PTR void
1186 openone(zx, zy, num)
1187 int zx, zy;
1188 genericptr_t num;
1190 register struct trap *ttmp;
1191 register struct obj *otmp;
1192 int *num_p = (int *) num;
1194 if (OBJ_AT(zx, zy)) {
1195 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1196 if (Is_box(otmp) && otmp->olocked) {
1197 otmp->olocked = 0;
1198 (*num_p)++;
1201 /* let it fall to the next cases. could be on trap. */
1203 if (levl[zx][zy].typ == SDOOR
1204 || (levl[zx][zy].typ == DOOR
1205 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1206 if (levl[zx][zy].typ == SDOOR)
1207 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1208 if (levl[zx][zy].doormask & D_TRAPPED) {
1209 if (distu(zx, zy) < 3)
1210 b_trapped("door", 0);
1211 else
1212 Norep("You %s an explosion!",
1213 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1214 : "feel the shock of"));
1215 wake_nearto(zx, zy, 11 * 11);
1216 levl[zx][zy].doormask = D_NODOOR;
1217 } else
1218 levl[zx][zy].doormask = D_ISOPEN;
1219 unblock_point(zx, zy);
1220 newsym(zx, zy);
1221 (*num_p)++;
1222 } else if (levl[zx][zy].typ == SCORR) {
1223 levl[zx][zy].typ = CORR;
1224 unblock_point(zx, zy);
1225 newsym(zx, zy);
1226 (*num_p)++;
1227 } else if ((ttmp = t_at(zx, zy)) != 0) {
1228 struct monst *mon;
1229 boolean dummy; /* unneeded "you notice it arg" */
1231 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1232 ttmp->tseen = 1;
1233 newsym(zx, zy);
1234 (*num_p)++;
1236 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1237 if (openholdingtrap(mon, &dummy)
1238 || openfallingtrap(mon, TRUE, &dummy))
1239 (*num_p)++;
1240 } else if (find_drawbridge(&zx, &zy)) {
1241 /* make sure it isn't an open drawbridge */
1242 open_drawbridge(zx, zy);
1243 (*num_p)++;
1247 /* returns number of things found */
1249 findit()
1251 int num = 0;
1253 if (u.uswallow)
1254 return 0;
1255 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1256 return num;
1259 /* returns number of things found and opened */
1261 openit()
1263 int num = 0;
1265 if (u.uswallow) {
1266 if (is_animal(u.ustuck->data)) {
1267 if (Blind)
1268 pline("Its mouth opens!");
1269 else
1270 pline("%s opens its mouth!", Monnam(u.ustuck));
1272 expels(u.ustuck, u.ustuck->data, TRUE);
1273 return -1;
1276 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1277 return num;
1280 /* callback hack for overriding vision in do_clear_area() */
1281 boolean
1282 detecting(func)
1283 void FDECL((*func), (int, int, genericptr_t));
1285 return (func == findone || func == openone);
1288 void
1289 find_trap(trap)
1290 struct trap *trap;
1292 int tt = what_trap(trap->ttyp);
1293 boolean cleared = FALSE;
1295 trap->tseen = 1;
1296 exercise(A_WIS, TRUE);
1297 feel_newsym(trap->tx, trap->ty);
1299 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1300 /* There's too much clutter to see your find otherwise */
1301 cls();
1302 map_trap(trap, 1);
1303 display_self();
1304 cleared = TRUE;
1307 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1309 if (cleared) {
1310 display_nhwindow(WIN_MAP, TRUE); /* wait */
1311 docrt();
1315 STATIC_OVL int
1316 mfind0(mtmp, via_warning)
1317 struct monst *mtmp;
1318 boolean via_warning;
1320 xchar x = mtmp->mx,
1321 y = mtmp->my;
1323 if (via_warning && !warning_of(mtmp))
1324 return -1;
1326 if (mtmp->m_ap_type) {
1327 seemimic(mtmp);
1328 find:
1329 exercise(A_WIS, TRUE);
1330 if (!canspotmon(mtmp)) {
1331 if (glyph_is_invisible(levl[x][y].glyph)) {
1332 /* found invisible monster in a square
1333 * which already has an 'I' in it.
1334 * Logically, this should still take
1335 * time and lead to a return(1), but
1336 * if we did that the player would keep
1337 * finding the same monster every turn.
1339 return -1;
1340 } else {
1341 You_feel("an unseen monster!");
1342 map_invisible(x, y);
1344 } else if (!sensemon(mtmp))
1345 You("find %s.", mtmp->mtame
1346 ? y_monnam(mtmp)
1347 : a_monnam(mtmp));
1348 return 1;
1350 if (!canspotmon(mtmp)) {
1351 if (mtmp->mundetected
1352 && (is_hider(mtmp->data)
1353 || mtmp->data->mlet == S_EEL))
1354 if (via_warning) {
1355 Your("warning senses cause you to take a second %s.",
1356 Blind ? "to check nearby" : "look close by");
1357 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1359 mtmp->mundetected = 0;
1360 newsym(x, y);
1361 goto find;
1363 return 0;
1367 dosearch0(aflag)
1368 register int aflag; /* intrinsic autosearch vs explicit searching */
1370 #ifdef GCC_BUG
1371 /* some versions of gcc seriously muck up nested loops. if you get strange
1372 crashes while searching in a version compiled with gcc, try putting
1373 #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
1374 makefile).
1376 volatile xchar x, y;
1377 #else
1378 register xchar x, y;
1379 #endif
1380 register struct trap *trap;
1381 register struct monst *mtmp;
1383 if (u.uswallow) {
1384 if (!aflag)
1385 pline("What are you looking for? The exit?");
1386 } else {
1387 int fund = (uwep && uwep->oartifact
1388 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1390 if (ublindf && ublindf->otyp == LENSES && !Blind)
1391 fund += 2; /* JDS: lenses help searching */
1392 if (fund > 5)
1393 fund = 5;
1394 for (x = u.ux - 1; x < u.ux + 2; x++)
1395 for (y = u.uy - 1; y < u.uy + 2; y++) {
1396 if (!isok(x, y))
1397 continue;
1398 if (x == u.ux && y == u.uy)
1399 continue;
1401 if (Blind && !aflag)
1402 feel_location(x, y);
1403 if (levl[x][y].typ == SDOOR) {
1404 if (rnl(7 - fund))
1405 continue;
1406 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1407 exercise(A_WIS, TRUE);
1408 nomul(0);
1409 feel_location(x, y); /* make sure it shows up */
1410 You("find a hidden door.");
1411 } else if (levl[x][y].typ == SCORR) {
1412 if (rnl(7 - fund))
1413 continue;
1414 levl[x][y].typ = CORR;
1415 unblock_point(x, y); /* vision */
1416 exercise(A_WIS, TRUE);
1417 nomul(0);
1418 feel_newsym(x, y); /* make sure it shows up */
1419 You("find a hidden passage.");
1420 } else {
1421 /* Be careful not to find anything in an SCORR or SDOOR */
1422 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
1423 int mfres = mfind0(mtmp, 0);
1425 if (mfres == -1)
1426 continue;
1427 else if (mfres > 0)
1428 return mfres;
1431 /* see if an invisible monster has moved--if Blind,
1432 * feel_location() already did it
1434 if (!aflag && !mtmp && !Blind
1435 && glyph_is_invisible(levl[x][y].glyph)) {
1436 unmap_object(x, y);
1437 newsym(x, y);
1440 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
1441 nomul(0);
1442 if (trap->ttyp == STATUE_TRAP) {
1443 if (activate_statue_trap(trap, x, y, FALSE))
1444 exercise(A_WIS, TRUE);
1445 return 1;
1446 } else {
1447 find_trap(trap);
1453 return 1;
1456 /* the 's' command -- explicit searching */
1458 dosearch()
1460 return dosearch0(0);
1463 void
1464 warnreveal()
1466 xchar x, y;
1467 struct monst *mtmp;
1469 for (x = u.ux - 1; x < u.ux + 2; x++)
1470 for (y = u.uy - 1; y < u.uy + 2; y++) {
1471 if (!isok(x, y))
1472 continue;
1473 if (x == u.ux && y == u.uy)
1474 continue;
1476 if ((mtmp = m_at(x, y)) != 0
1477 && warning_of(mtmp) && mtmp->mundetected)
1478 (void) mfind0(mtmp, 1); /* via_warning */
1482 /* Pre-map the sokoban levels */
1483 void
1484 sokoban_detect()
1486 register int x, y;
1487 register struct trap *ttmp;
1488 register struct obj *obj;
1490 /* Map the background and boulders */
1491 for (x = 1; x < COLNO; x++)
1492 for (y = 0; y < ROWNO; y++) {
1493 levl[x][y].seenv = SVALL;
1494 levl[x][y].waslit = TRUE;
1495 map_background(x, y, 1);
1496 if ((obj = sobj_at(BOULDER, x, y)) != 0)
1497 map_object(obj, 1);
1500 /* Map the traps */
1501 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1502 ttmp->tseen = 1;
1503 map_trap(ttmp, 1);
1504 /* set sokoban_rules when there is at least one pit or hole */
1505 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
1506 Sokoban = 1;
1510 /* idea from crawl; show known portion of map without any monsters,
1511 objects, or traps occluding the view of the underlying terrain */
1512 void
1513 reveal_terrain(full, which_subset)
1514 int full; /* wizard|explore modes allow player to request full map */
1515 int which_subset; /* when not full, whether to suppress objs and/or traps */
1517 if ((Hallucination || Stunned || Confusion) && !full) {
1518 You("are too disoriented for this.");
1519 } else {
1520 int x, y, glyph, levl_glyph, default_glyph;
1521 uchar seenv;
1522 unsigned save_swallowed;
1523 struct monst *mtmp;
1524 struct trap *t;
1525 char buf[BUFSZ];
1526 boolean keep_traps = (which_subset & 1) !=0,
1527 keep_objs = (which_subset & 2) != 0,
1528 keep_mons = (which_subset & 4) != 0; /* actually always 0 */
1530 save_swallowed = u.uswallow;
1531 iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
1532 u.uinwater = u.uburied = 0;
1533 u.uswallow = 0;
1534 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
1535 /* for 'full', show the actual terrain for the entire level,
1536 otherwise what the hero remembers for seen locations with
1537 monsters, objects, and/or traps removed as caller dictates */
1538 for (x = 1; x < COLNO; x++)
1539 for (y = 0; y < ROWNO; y++) {
1540 seenv = (full || level.flags.hero_memory)
1541 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
1542 if (full) {
1543 levl[x][y].seenv = SVALL;
1544 glyph = back_to_glyph(x, y);
1545 levl[x][y].seenv = seenv;
1546 } else {
1547 levl_glyph = level.flags.hero_memory
1548 ? levl[x][y].glyph
1549 : seenv
1550 ? back_to_glyph(x, y)
1551 : default_glyph;
1552 /* glyph_at() returns the displayed glyph, which might
1553 be a monster. levl[][].glyph contains the remembered
1554 glyph, which will never be a monster (unless it is
1555 the invisible monster glyph, which is handled like
1556 an object, replacing any object or trap at its spot) */
1557 glyph = !save_swallowed ? glyph_at(x, y) : levl_glyph;
1558 if (keep_mons && x == u.ux && y == u.uy && save_swallowed)
1559 glyph = mon_to_glyph(u.ustuck);
1560 else if (((glyph_is_monster(glyph)
1561 || glyph_is_warning(glyph)) && !keep_mons)
1562 || glyph_is_swallow(glyph))
1563 glyph = levl_glyph;
1564 if (((glyph_is_object(glyph) && !keep_objs)
1565 || glyph_is_invisible(glyph))
1566 && keep_traps && !covers_traps(x, y)) {
1567 if ((t = t_at(x, y)) != 0 && t->tseen)
1568 glyph = trap_to_glyph(t);
1570 if ((glyph_is_object(glyph) && !keep_objs)
1571 || (glyph_is_trap(glyph) && !keep_traps)
1572 || glyph_is_invisible(glyph)) {
1573 if (!seenv) {
1574 glyph = default_glyph;
1575 } else if (lastseentyp[x][y] == levl[x][y].typ) {
1576 glyph = back_to_glyph(x, y);
1577 } else {
1578 /* look for a mimic here posing as furniture;
1579 if we don't find one, we'll have to fake it */
1580 if ((mtmp = m_at(x, y)) != 0
1581 && mtmp->m_ap_type == M_AP_FURNITURE) {
1582 glyph = cmap_to_glyph(mtmp->mappearance);
1583 } else {
1584 /* we have a topology type but we want a
1585 screen symbol in order to derive a glyph;
1586 some screen symbols need the flags field
1587 of levl[][] in addition to the type
1588 (to disambiguate STAIRS to S_upstair or
1589 S_dnstair, for example; current flags
1590 might not be intended for remembered
1591 type, but we've got no other choice) */
1592 schar save_typ = levl[x][y].typ;
1594 levl[x][y].typ = lastseentyp[x][y];
1595 glyph = back_to_glyph(x, y);
1596 levl[x][y].typ = save_typ;
1601 if (glyph == cmap_to_glyph(S_darkroom))
1602 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
1603 show_glyph(x, y, glyph);
1606 /* [TODO: highlight hero's location somehow] */
1607 u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
1608 if (save_swallowed)
1609 u.uswallow = 1;
1610 flush_screen(1);
1611 if (full) {
1612 Strcpy(buf, "underlying terrain");
1613 } else {
1614 Strcpy(buf, "known terrain");
1615 if (keep_traps)
1616 Sprintf(eos(buf), "%s traps",
1617 (keep_objs || keep_mons) ? "," : " and");
1618 if (keep_objs)
1619 Sprintf(eos(buf), "%s%s objects",
1620 (keep_traps || keep_mons) ? "," : "",
1621 keep_mons ? "" : " and");
1622 if (keep_mons)
1623 Sprintf(eos(buf), "%s and monsters",
1624 (keep_traps || keep_objs) ? "," : "");
1626 pline("Showing %s only...", buf);
1627 display_nhwindow(WIN_MAP, TRUE); /* give "--More--" prompt */
1628 docrt(); /* redraw the screen, restoring regular map */
1629 if (Underwater)
1630 under_water(2);
1631 if (u.uburied)
1632 under_ground(2);
1634 return;
1637 /*detect.c*/