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. */
6 * Detection routines, including crystal ball, magic mapping, and search
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 */
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;
45 /* put hero back underwater or underground or engulfed */
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 */
56 browse_map(ter_typ
, ter_explain
)
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() */
72 map_monst(mtmp
, showtail
)
76 if (def_monsyms
[(int) mtmp
->data
->mlet
].sym
== ' ')
77 show_glyph(mtmp
->mx
, mtmp
->my
, detected_mon_to_glyph(mtmp
));
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 */
89 trapped_chest_at(ttyp
, x
, y
)
96 if (!glyph_is_trap(glyph_at(x
, y
)))
98 if (ttyp
!= BEAR_TRAP
|| (Hallucination
&& rn2(20)))
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
))
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
)
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
)
124 if ((mtmp
= m_at(x
, y
)) != 0)
125 for (otmp
= mtmp
->minvent
; otmp
; otmp
= otmp
->nobj
)
126 if (Is_box(otmp
) && otmp
->otrapped
)
131 /* this is checking whether a trap symbol represents a trapped door,
132 not whether the door here is actually trapped */
134 trapped_door_at(ttyp
, x
, y
)
140 if (!glyph_is_trap(glyph_at(x
, y
)))
142 if (ttyp
!= BEAR_TRAP
|| (Hallucination
&& rn2(20)))
145 if (!IS_DOOR(lev
->typ
))
147 if ((lev
->doormask
& (D_NODOOR
| D_BROKEN
| D_ISOPEN
)) != 0
148 && trapped_chest_at(ttyp
, x
, y
))
153 /* recursively search obj for an object in class oclass, return 1st found */
159 register struct obj
*otmp
;
162 if (obj
->oclass
== oclass
)
165 if (Has_contents(obj
)) {
166 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
167 if (otmp
->oclass
== oclass
)
169 else if (Has_contents(otmp
) && (temp
= o_in(otmp
, oclass
)) != 0)
172 return (struct obj
*) 0;
175 /* Recursively search obj for an object made of specified material.
176 * Return first found.
179 o_material(obj
, material
)
183 register struct obj
*otmp
;
186 if (objects
[obj
->otyp
].oc_material
== material
)
189 if (Has_contents(obj
)) {
190 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
191 if (objects
[otmp
->otyp
].oc_material
== material
)
193 else if (Has_contents(otmp
)
194 && (temp
= o_material(otmp
, material
)) != 0)
197 return (struct obj
*) 0;
207 if (Has_contents(obj
)) {
208 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
213 /* Check whether the location has an outdated object displayed on it. */
215 check_map_spot(x
, y
, oclass
, material
)
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
));
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
))
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
))
243 /* detection indicates removal of this object from the map */
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
))
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
))
257 /* detection indicates removal of this object from the map */
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
272 clear_stale_map(oclass
, material
)
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
);
289 /* look for gold, on the floor or in monsters' possession */
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
) {
312 goto outgoldmap
; /* skip further searching */
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
) {
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
)) {
332 if (obj
->ox
!= u
.ux
|| obj
->oy
!= u
.uy
)
334 } else if (o_in(obj
, COIN_CLASS
)) {
336 if (obj
->ox
!= u
.ux
|| obj
->oy
!= u
.uy
)
342 /* no gold found on floor or monster's inventory.
343 adjust message if you have gold in your inventory */
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())
351 "You feel worried about your future financial situation.");
353 Sprintf(buf
, "You feel interested in %s financial situation.",
354 s_suffix(x_monnam(u
.usteed
,
355 u
.usteed
->mtame
? ARTICLE_YOUR
358 SUPPRESS_SADDLE
, FALSE
)));
360 Strcpy(buf
, "You feel materially poor.");
362 strange_feeling(sobj
, buf
);
366 /* only under me - no separate display required */
369 You("notice some gold between your %s.", makeplural(body_part(FOOT
)));
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) {
384 } else if ((temp
= o_in(obj
, COIN_CLASS
)) != 0) {
391 if (temp
&& temp
->ox
== u
.ux
&& temp
->oy
== u
.uy
)
394 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
395 if (DEADMONSTER(mtmp
))
396 continue; /* probably overkill here */
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 */
404 map_object(&gold
, 1);
407 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
408 if (sobj
->blessed
&& (temp
= o_material(obj
, GOLD
)) != 0) {
413 } else if ((temp
= o_in(obj
, COIN_CLASS
)) != 0) {
420 if (temp
&& temp
->ox
== u
.ux
&& temp
->oy
== 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");
441 /* returns 1 if nothing was detected */
442 /* returns 0 if something was detected */
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
)
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 */
478 known
= stale
&& !confused
;
481 You("sense a lack of %s nearby.", what
);
482 if (sobj
&& sobj
->blessed
) {
484 Your("%s starts to tingle.", body_part(NOSE
));
490 Sprintf(buf
, "Your %s twitches%s.", body_part(NOSE
),
491 (sobj
->blessed
&& !u
.uedibility
)
492 ? " then starts to tingle"
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
;
502 strange_feeling(sobj
, buf
);
507 You("%s %s nearby.", sobj
? "smell" : "sense", what
);
508 if (sobj
&& sobj
->blessed
) {
510 pline("Your %s starts to tingle.", body_part(NOSE
));
515 int ter_typ
= TER_DETECT
| TER_OBJ
;
519 (void) unconstrain_map();
520 for (obj
= fobj
; obj
; obj
= obj
->nobj
)
521 if ((temp
= o_in(obj
, oclass
)) != 0) {
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) {
535 break; /* skip rest of this monster's inventory */
539 ter_typ
|= TER_MON
; /* for autodescribe of self */
543 Your("%s %s to tingle and you smell %s.", body_part(NOSE
),
544 u
.uedibility
? "continues" : "starts", what
);
547 Your("%s tingles and you smell %s.", body_part(NOSE
), what
);
549 You("sense %s.", what
);
550 exercise(A_WIS
, TRUE
);
552 browse_map(ter_typ
, "food");
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 */
577 int is_cursed
= (detector
&& detector
->cursed
);
578 int do_dknown
= (detector
&& (detector
->oclass
== POTION_CLASS
579 || detector
->oclass
== SPBOOK_CLASS
)
580 && detector
->blessed
);
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);
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
);
604 Strcpy(stuff
, class ? def_oc_syms
[class].name
: "objects");
605 if (boulder
&& class != ROCK_CLASS
)
606 Strcat(stuff
, " and/or large stones");
609 for (obj
= invent
; obj
; obj
= obj
->nobj
)
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
)
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
)
635 u
.usteed
->mx
= u
.ux
, u
.usteed
->my
= u
.uy
;
637 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
638 if (DEADMONSTER(mtmp
))
640 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
) {
641 if ((!class && !boulder
) || o_in(obj
, class)
642 || o_in(obj
, boulder
))
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
))) {
655 if (!clear_stale_map(!class ? ALL_CLASSES
: class, 0) && !ct
) {
658 strange_feeling(detector
, "You feel a lack of something.");
662 You("sense %s nearby.", stuff
);
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) {
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
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
) {
707 /* Objects in the monster's inventory override floor objects. */
708 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
709 if (DEADMONSTER(mtmp
))
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
)
716 otmp
->ox
= mtmp
->mx
; /* at monster location */
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
)) {
727 temp
.otyp
= mtmp
->mappearance
; /* needed for obj_to_glyph() */
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
)) {
737 gold
= zeroobj
; /* ensure oextra is cleared too */
738 gold
.otyp
= GOLD_PIECE
;
739 gold
.quan
= (long) rnd(10); /* usually more than 1 */
742 map_object(&gold
, 1);
745 if (!glyph_is_object(glyph_at(u
.ux
, u
.uy
))) {
749 You("detect the %s of %s.", ct
? "presence" : "absence", stuff
);
752 display_nhwindow(WIN_MAP
, TRUE
);
754 browse_map(ter_typ
, "object");
757 docrt(); /* this will correctly reset vision */
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
;
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
)) {
792 strange_feeling(otmp
, Hallucination
793 ? "You get the heebie jeebies."
794 : "You feel threatened.");
797 boolean unconstrained
, woken
= FALSE
;
798 unsigned swallowed
= u
.uswallow
; /* before unconstrain_map() */
801 unconstrained
= unconstrain_map();
802 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
803 if (DEADMONSTER(mtmp
))
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;
819 You("sense the presence of monsters.");
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
);
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
;
835 docrt(); /* redraw the screen to remove unseen monsters from map */
845 sense_trap(trap
, x
, y
, src_cursed
)
850 if (Hallucination
|| src_cursed
) {
851 struct obj obj
; /* fake object */
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 */
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
);
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 */
888 detect_obj_traps(objlist
, show_them
, how
)
891 int how
; /* 1 for misleading map feedback */
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
;
907 sense_trap((struct trap
*) 0, x
, y
, how
);
909 if (Has_contents(otmp
))
910 result
|= detect_obj_traps(otmp
->cobj
, show_them
, how
);
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
922 struct obj
*sobj
; /* null if crystal ball, *scroll if gold detection scroll */
924 register struct trap
*ttmp
;
926 int door
, glyph
, tr
, ter_typ
= TER_DETECT
| TER_TRP
;
927 int cursed_src
= sobj
&& sobj
->cursed
;
928 boolean found
= FALSE
;
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
)
941 /* chest traps (might be buried or carried) */
942 if ((tr
= detect_obj_traps(fobj
, FALSE
, 0)) != OTRAP_NONE
) {
943 if (tr
& OTRAP_THERE
)
948 if ((tr
= detect_obj_traps(level
.buriedobjlist
, FALSE
, 0)) != OTRAP_NONE
) {
949 if (tr
& OTRAP_THERE
)
954 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
955 if (DEADMONSTER(mon
))
957 if ((tr
= detect_obj_traps(mon
->minvent
, FALSE
, 0)) != OTRAP_NONE
) {
958 if (tr
& OTRAP_THERE
)
964 if (detect_obj_traps(invent
, FALSE
, 0) != OTRAP_NONE
)
967 for (door
= 0; door
< doorindex
; door
++) {
969 if (levl
[cc
.x
][cc
.y
].doormask
& D_TRAPPED
) {
970 if (cc
.x
!= u
.ux
|| cc
.y
!= u
.uy
)
979 Sprintf(buf
, "Your %s stop itching.", makeplural(body_part(TOE
)));
980 strange_feeling(sobj
, buf
);
983 /* traps exist, but only under me - no separate display required */
984 Your("%s itch.", makeplural(body_part(TOE
)));
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
))
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
++) {
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
))) {
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");
1022 docrt(); /* redraw the screen to remove unseen traps from the map */
1031 level_distance(where
)
1034 register schar ll
= depth(&u
.uz
) - depth(where
);
1035 register boolean indun
= (u
.uz
.dnum
== where
->dnum
);
1038 if (ll
< (-8 - rn2(3)))
1045 return "away below you";
1049 return "in the distance";
1051 return "just below";
1052 } else if (ll
> 0) {
1053 if (ll
> (8 + rn2(3)))
1060 return "away above you";
1064 return "in the distance";
1066 return "just above";
1068 return "in the distance";
1073 static const struct {
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
},
1084 use_crystal_ball(optr
)
1089 struct obj
*obj
= *optr
;
1092 pline("Too bad you can't see %s.", the(xname(obj
)));
1095 oops
= (rnd(20) > ACURR(A_INT
) || obj
->cursed
);
1096 if (oops
&& (obj
->spe
> 0)) {
1097 switch (rnd(obj
->oartifact
? 4 : 5)) {
1099 pline("%s too much to comprehend!", Tobjnam(obj
, "are"));
1102 pline("%s you!", Tobjnam(obj
, "confuse"));
1103 make_confused((HConfusion
& TIMEOUT
) + (long) rnd(100), FALSE
);
1106 if (!resists_blnd(&youmonst
)) {
1107 pline("%s your vision!", Tobjnam(obj
, "damage"));
1108 make_blinded((Blinded
& TIMEOUT
) + (long) rnd(100), FALSE
);
1110 Your1(vision_clears
);
1112 pline("%s your vision.", Tobjnam(obj
, "assault"));
1113 You("are unaffected!");
1117 pline("%s your mind!", Tobjnam(obj
, "zap"));
1118 (void) make_hallucinated(
1119 (HHallucination
& TIMEOUT
) + (long) rnd(100), FALSE
, 0L);
1122 pline("%s!", Tobjnam(obj
, "explode"));
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",
1131 consume_obj_charge(obj
, TRUE
);
1135 if (Hallucination
) {
1137 pline("All you see is funky %s haze.", hcolor((char *) 0));
1141 You("grok some groovy globs of incandescent lava.");
1144 pline("Whoa! Psychedelic colors, %s!",
1145 poly_gender() == 1 ? "babe" : "dude");
1148 pline_The("crystal pulses with sinister %s light!",
1149 hcolor((char *) 0));
1152 You_see("goldfish swimming above fluorescent rocks.");
1156 "tiny snowflakes spinning around a miniature farmhouse.");
1159 pline("Oh wow... like a kaleidoscope!");
1162 consume_obj_charge(obj
, TRUE
);
1167 /* read a single character */
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
)) {
1177 You("peer into %s...", the(xname(obj
)));
1179 multi_reason
= "gazing into a crystal ball";
1181 if (obj
->spe
<= 0) {
1182 pline_The("vision is unclear.");
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
)
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
);
1205 ret
= trap_detect((struct obj
*) 0);
1208 i
= rn2(SIZE(level_detects
));
1209 You_see("%s, %s.", level_detects
[i
].what
,
1210 level_distance(level_detects
[i
].where
));
1216 if (!rn2(100)) /* make them nervous */
1217 You_see("the Wizard of Yendor gazing out at you.");
1219 pline_The("vision is unclear.");
1233 if (Confusion
&& rn2(7))
1239 /* Secret corridors are found, but not secret doors. */
1240 if (lev
->typ
== SCORR
) {
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 */
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
) {
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
;
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");
1289 exercise(A_WIS
, TRUE
);
1294 do_vicinity_map(sobj
)
1295 struct obj
*sobj
; /* scroll--actually fake spellbook--object */
1297 register int zx
, zy
;
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
)
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
)))
1327 EDetect_monsters
|= I_SPECIAL
;
1328 browse_map(ter_typ
, "anything of interest");
1329 EDetect_monsters
&= ~I_SPECIAL
;
1337 /* convert a secret door into a normal door */
1339 cvt_sdoor_to_door(lev
)
1342 int newmask
= lev
->doormask
& ~WM_MASK
;
1344 if (Is_rogue_level(&u
.uz
))
1345 /* rogue didn't have doors, only doorways */
1348 /* newly exposed door is closed */
1349 if (!(newmask
& D_LOCKED
))
1350 newmask
|= D_CLOSED
;
1353 lev
->doormask
= newmask
;
1357 findone(zx
, zy
, 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);
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);
1375 } else if ((ttmp
= t_at(zx
, zy
)) != 0) {
1376 if (!ttmp
->tseen
&& ttmp
->ttyp
!= STATUE_TRAP
) {
1381 } else if ((mtmp
= m_at(zx
, zy
)) != 0) {
1382 if (mtmp
->m_ap_type
) {
1386 if (mtmp
->mundetected
1387 && (is_hider(mtmp
->data
) || mtmp
->data
->mlet
== S_EEL
)) {
1388 mtmp
->mundetected
= 0;
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
);
1402 openone(zx
, zy
, 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
) {
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);
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
;
1434 levl
[zx
][zy
].doormask
= D_ISOPEN
;
1435 unblock_point(zx
, zy
);
1438 } else if (levl
[zx
][zy
].typ
== SCORR
) {
1439 levl
[zx
][zy
].typ
= CORR
;
1440 unblock_point(zx
, zy
);
1443 } else if ((ttmp
= t_at(zx
, zy
)) != 0) {
1445 boolean dummy
; /* unneeded "you notice it arg" */
1447 if (!ttmp
->tseen
&& ttmp
->ttyp
!= STATUE_TRAP
) {
1452 mon
= (zx
== u
.ux
&& zy
== u
.uy
) ? &youmonst
: m_at(zx
, zy
);
1453 if (openholdingtrap(mon
, &dummy
)
1454 || openfallingtrap(mon
, TRUE
, &dummy
))
1456 } else if (find_drawbridge(&zx
, &zy
)) {
1457 /* make sure it isn't an open drawbridge */
1458 open_drawbridge(zx
, zy
);
1463 /* returns number of things found */
1471 do_clear_area(u
.ux
, u
.uy
, BOLT_LIM
, findone
, (genericptr_t
) &num
);
1475 /* returns number of things found and opened */
1482 if (is_animal(u
.ustuck
->data
)) {
1484 pline("Its mouth opens!");
1486 pline("%s opens its mouth!", Monnam(u
.ustuck
));
1488 expels(u
.ustuck
, u
.ustuck
->data
, TRUE
);
1492 do_clear_area(u
.ux
, u
.uy
, BOLT_LIM
, openone
, (genericptr_t
) &num
);
1496 /* callback hack for overriding vision in do_clear_area() */
1499 void FDECL((*func
), (int, int, genericptr_t
));
1501 return (func
== findone
|| func
== openone
);
1508 int tt
= what_trap(trap
->ttyp
);
1509 boolean cleared
= FALSE
;
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 */
1523 You("find %s.", an(defsyms
[trap_to_defsym(tt
)].explanation
));
1526 display_nhwindow(WIN_MAP
, TRUE
); /* wait */
1532 mfind0(mtmp
, via_warning
)
1534 boolean via_warning
;
1539 if (via_warning
&& !warning_of(mtmp
))
1542 if (mtmp
->m_ap_type
) {
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.
1555 You_feel("an unseen monster!");
1556 map_invisible(x
, y
);
1558 } else if (!sensemon(mtmp
))
1560 mtmp
->mtame
? y_monnam(mtmp
) : a_monnam(mtmp
));
1563 if (!canspotmon(mtmp
)) {
1564 if (mtmp
->mundetected
1565 && (is_hider(mtmp
->data
) || mtmp
->data
->mlet
== S_EEL
))
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;
1580 register int aflag
; /* intrinsic autosearch vs explicit searching */
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
1588 volatile xchar x
, y
;
1590 register xchar x
, y
;
1592 register struct trap
*trap
;
1593 register struct monst
*mtmp
;
1597 pline("What are you looking for? The exit?");
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 */
1606 for (x
= u
.ux
- 1; x
< u
.ux
+ 2; x
++)
1607 for (y
= u
.uy
- 1; y
< u
.uy
+ 2; y
++) {
1610 if (x
== u
.ux
&& y
== u
.uy
)
1613 if (Blind
&& !aflag
)
1614 feel_location(x
, y
);
1615 if (levl
[x
][y
].typ
== SDOOR
) {
1618 cvt_sdoor_to_door(&levl
[x
][y
]); /* .typ = DOOR */
1619 exercise(A_WIS
, TRUE
);
1621 feel_location(x
, y
); /* make sure it shows up */
1622 You("find a hidden door.");
1623 } else if (levl
[x
][y
].typ
== SCORR
) {
1626 levl
[x
][y
].typ
= CORR
;
1627 unblock_point(x
, y
); /* vision */
1628 exercise(A_WIS
, TRUE
);
1630 feel_newsym(x
, y
); /* make sure it shows up */
1631 You("find a hidden passage.");
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);
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
)) {
1652 if ((trap
= t_at(x
, y
)) && !trap
->tseen
&& !rnl(8)) {
1654 if (trap
->ttyp
== STATUE_TRAP
) {
1655 if (activate_statue_trap(trap
, x
, y
, FALSE
))
1656 exercise(A_WIS
, TRUE
);
1668 /* the 's' command -- explicit searching */
1672 return dosearch0(0);
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
))
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 */
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)
1710 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
) {
1713 /* set sokoban_rules when there is at least one pit or hole */
1714 if (ttmp
->ttyp
== PIT
|| ttmp
->ttyp
== HOLE
)
1720 reveal_terrain_getglyph(x
,y
, full
, swallowed
, default_glyph
, which_subset
)
1723 int default_glyph
, which_subset
;
1725 int glyph
, levl_glyph
;
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;
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;
1739 levl
[x
][y
].seenv
= SVALL
;
1740 glyph
= back_to_glyph(x
, y
);
1741 levl
[x
][y
].seenv
= seenv
;
1743 levl_glyph
= level
.flags
.hero_memory
1746 ? back_to_glyph(x
, y
)
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
))
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
)) {
1770 glyph
= default_glyph
;
1771 } else if (lastseentyp
[x
][y
] == levl
[x
][y
].typ
) {
1772 glyph
= back_to_glyph(x
, y
);
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
);
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 */
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
);
1810 for (y
= 0; y
< ROWNO
; y
++) {
1811 for (x
= 1; x
< COLNO
; x
++) {
1814 glyph
= reveal_terrain_getglyph(x
,y
, FALSE
, u
.uswallow
,
1815 default_glyph
, subset
);
1816 (void) mapglyph(glyph
, &ch
, &color
, &special
, x
, y
);
1824 /* idea from crawl; show known portion of map without any monsters,
1825 objects, or traps occluding the view of the underlying terrain */
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.");
1834 int x
, y
, glyph
, default_glyph
;
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())
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 */
1857 Strcpy(buf
, "underlying terrain");
1859 Strcpy(buf
, "known terrain");
1861 Sprintf(eos(buf
), "%s traps",
1862 (keep_objs
|| keep_mons
) ? "," : " and");
1864 Sprintf(eos(buf
), "%s%s objects",
1865 (keep_traps
|| keep_mons
) ? "," : "",
1866 keep_mons
? "" : " and");
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");
1879 docrt(); /* redraw the screen, restoring regular map */