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. */
6 * Detection routines, including crystal ball, magic mapping, and search
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
31 register struct obj
*otmp
;
34 if (obj
->oclass
== oclass
)
37 if (Has_contents(obj
)) {
38 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
39 if (otmp
->oclass
== oclass
)
41 else if (Has_contents(otmp
) && (temp
= o_in(otmp
, oclass
)))
44 return (struct obj
*) 0;
47 /* Recursively search obj for an object made of specified material.
51 o_material(obj
, material
)
55 register struct obj
*otmp
;
58 if (objects
[obj
->otyp
].oc_material
== material
)
61 if (Has_contents(obj
)) {
62 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
63 if (objects
[otmp
->otyp
].oc_material
== material
)
65 else if (Has_contents(otmp
)
66 && (temp
= o_material(otmp
, material
)))
69 return (struct obj
*) 0;
79 if (Has_contents(obj
)) {
80 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
85 /* Check whether the location has an outdated object displayed on it. */
87 check_map_spot(x
, y
, oclass
, material
)
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
));
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
))
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
))
115 /* detection indicates removal of this object from the map */
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
))
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
))
129 /* detection indicates removal of this object from the map */
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
144 clear_stale_map(oclass
, material
)
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
);
161 /* look for gold, on the floor or in monsters' possession */
164 register struct obj
*sobj
;
166 register struct obj
*obj
;
167 register struct monst
*mtmp
;
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
) {
180 goto outgoldmap
; /* skip further searching */
182 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
183 if (sobj
->blessed
&& o_material(obj
, GOLD
)) {
186 } else if (o_in(obj
, COIN_CLASS
)) {
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
)) {
196 if (obj
->ox
!= u
.ux
|| obj
->oy
!= u
.uy
)
198 } else if (o_in(obj
, COIN_CLASS
)) {
200 if (obj
->ox
!= u
.ux
|| obj
->oy
!= u
.uy
)
206 /* no gold found on floor or monster's inventory.
207 adjust message if you have gold in your inventory */
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
))
214 "You feel worried about your future financial situation.");
216 Strcpy(buf
, "You feel materially poor.");
217 strange_feeling(sobj
, buf
);
221 /* only under me - no separate display required */
224 You("notice some gold between your %s.", makeplural(body_part(FOOT
)));
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
))) {
240 } else if ((temp
= o_in(obj
, COIN_CLASS
))) {
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
) {
253 gold
= zeroobj
; /* ensure oextra is cleared too */
254 gold
.otyp
= GOLD_PIECE
;
257 map_object(&gold
, 1);
259 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
260 if (sobj
->blessed
&& (temp
= o_material(obj
, GOLD
))) {
265 } else if ((temp
= o_in(obj
, COIN_CLASS
))) {
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
);
285 /* returns 1 if nothing was detected */
286 /* returns 0 if something was detected */
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
)
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
)) {
318 known
= stale
&& !confused
;
321 You("sense a lack of %s nearby.", what
);
322 if (sobj
&& sobj
->blessed
) {
324 Your("%s starts to tingle.", body_part(NOSE
));
329 Sprintf(buf
, "Your %s twitches%s.", body_part(NOSE
),
330 (sobj
->blessed
&& !u
.uedibility
)
331 ? " then starts to tingle"
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
;
341 strange_feeling(sobj
, buf
);
346 You("%s %s nearby.", sobj
? "smell" : "sense", what
);
347 if (sobj
&& sobj
->blessed
) {
349 pline("Your %s starts to tingle.", body_part(NOSE
));
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) {
366 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
367 /* no DEADMONSTER(mtmp) check needed since dmons never have
369 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
370 if ((temp
= o_in(obj
, oclass
)) != 0) {
374 break; /* skip rest of this monster's inventory */
377 u
.uinwater
= iflags
.save_uinwater
, u
.uburied
= iflags
.save_uburied
;
380 Your("%s %s to tingle and you smell %s.", body_part(NOSE
),
381 u
.uedibility
? "continues" : "starts", what
);
384 Your("%s tingles and you smell %s.", body_part(NOSE
), what
);
386 You("sense %s.", what
);
387 display_nhwindow(WIN_MAP
, TRUE
);
388 exercise(A_WIS
, TRUE
);
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 */
411 int is_cursed
= (detector
&& detector
->cursed
);
412 int do_dknown
= (detector
&& (detector
->oclass
== POTION_CLASS
413 || detector
->oclass
== SPBOOK_CLASS
)
414 && detector
->blessed
);
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);
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
);
438 Strcpy(stuff
, class ? def_oc_syms
[class].name
: "objects");
439 if (boulder
&& class != ROCK_CLASS
)
440 Strcat(stuff
, " and/or large stones");
443 for (obj
= invent
; obj
; obj
= obj
->nobj
)
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
)
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
)
468 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
469 if (DEADMONSTER(mtmp
))
471 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
) {
472 if ((!class && !boulder
) || o_in(obj
, class)
473 || o_in(obj
, boulder
))
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
))) {
486 if (!clear_stale_map(!class ? ALL_CLASSES
: class, 0) && !ct
) {
489 strange_feeling(detector
, "You feel a lack of something.");
493 You("sense %s nearby.", stuff
);
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))) {
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
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
) {
539 /* Objects in the monster's inventory override floor objects. */
540 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
541 if (DEADMONSTER(mtmp
))
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
)
548 otmp
->ox
= mtmp
->mx
; /* at monster location */
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
)) {
558 temp
.oextra
= (struct oextra
*) 0;
559 temp
.otyp
= mtmp
->mappearance
; /* needed for obj_to_glyph() */
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
)) {
567 gold
= zeroobj
; /* ensure oextra is cleared too */
568 gold
.otyp
= GOLD_PIECE
;
571 map_object(&gold
, 1);
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 */
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
;
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
)) {
619 strange_feeling(otmp
, Hallucination
620 ? "You get the heebie jeebies."
621 : "You feel threatened.");
624 boolean woken
= FALSE
;
627 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
628 if (DEADMONSTER(mtmp
))
630 if (!mclass
|| mtmp
->data
->mlet
== mclass
631 || (mtmp
->data
== &mons
[PM_LONG_WORM
]
632 && mclass
== S_WORM_TAIL
))
634 if (mclass
&& def_monsyms
[mclass
].sym
== ' ')
635 show_glyph(mtmp
->mx
, mtmp
->my
,
636 detected_mon_to_glyph(mtmp
));
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;
652 You("sense the presence of monsters.");
654 pline("Monsters sense the presence of you.");
655 display_nhwindow(WIN_MAP
, TRUE
);
666 sense_trap(trap
, x
, y
, src_cursed
)
671 if (Hallucination
|| src_cursed
) {
672 struct obj obj
; /* fake object */
674 obj
.oextra
= (struct oextra
*) 0;
682 obj
.otyp
= (src_cursed
) ? GOLD_PIECE
: random_object();
683 obj
.corpsenm
= random_monster(); /* if otyp == CORPSE */
689 struct trap temp_trap
; /* fake trap */
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 */
705 detect_obj_traps(objlist
, show_them
, how
)
708 int how
; /* 1 for misleading map feedback */
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
;
719 sense_trap((struct trap
*) 0, x
, y
, how
);
721 if (Has_contents(otmp
))
722 result
|= detect_obj_traps(otmp
->cobj
, show_them
, how
);
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
734 register struct obj
*sobj
;
735 /* sobj is null if crystal ball, *scroll if gold detection scroll */
737 register struct trap
*ttmp
;
740 int cursed_src
= sobj
&& sobj
->cursed
;
741 boolean found
= FALSE
;
744 /* floor/ceiling traps */
745 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
) {
746 if (ttmp
->tx
!= u
.ux
|| ttmp
->ty
!= u
.uy
)
751 /* chest traps (might be buried or carried) */
752 if ((tr
= detect_obj_traps(fobj
, FALSE
, 0)) != OTRAP_NONE
) {
753 if (tr
& OTRAP_THERE
)
758 if ((tr
= detect_obj_traps(level
.buriedobjlist
, FALSE
, 0))
760 if (tr
& OTRAP_THERE
)
765 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
766 if (DEADMONSTER(mon
))
768 if ((tr
= detect_obj_traps(mon
->minvent
, FALSE
, 0)) != OTRAP_NONE
) {
769 if (tr
& OTRAP_THERE
)
775 if (detect_obj_traps(invent
, FALSE
, 0) != OTRAP_NONE
)
778 for (door
= 0; door
< doorindex
; door
++) {
780 if (levl
[cc
.x
][cc
.y
].doormask
& D_TRAPPED
) {
781 if (cc
.x
!= u
.ux
|| cc
.y
!= u
.uy
)
790 Sprintf(buf
, "Your %s stop itching.", makeplural(body_part(TOE
)));
791 strange_feeling(sobj
, buf
);
794 /* traps exist, but only under me - no separate display required */
795 Your("%s itch.", makeplural(body_part(TOE
)));
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
))
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
++) {
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
)))
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
);
841 level_distance(where
)
844 register schar ll
= depth(&u
.uz
) - depth(where
);
845 register boolean indun
= (u
.uz
.dnum
== where
->dnum
);
848 if (ll
< (-8 - rn2(3)))
855 return "away below you";
859 return "in the distance";
863 if (ll
> (8 + rn2(3)))
870 return "away above you";
874 return "in the distance";
878 return "in the distance";
883 static const struct {
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
},
894 use_crystal_ball(optr
)
899 struct obj
*obj
= *optr
;
902 pline("Too bad you can't see %s.", the(xname(obj
)));
905 oops
= (rnd(20) > ACURR(A_INT
) || obj
->cursed
);
906 if (oops
&& (obj
->spe
> 0)) {
907 switch (rnd(obj
->oartifact
? 4 : 5)) {
909 pline("%s too much to comprehend!", Tobjnam(obj
, "are"));
912 pline("%s you!", Tobjnam(obj
, "confuse"));
913 make_confused((HConfusion
& TIMEOUT
) + (long) rnd(100), FALSE
);
916 if (!resists_blnd(&youmonst
)) {
917 pline("%s your vision!", Tobjnam(obj
, "damage"));
918 make_blinded((Blinded
& TIMEOUT
) + (long) rnd(100), FALSE
);
920 Your1(vision_clears
);
922 pline("%s your vision.", Tobjnam(obj
, "assault"));
923 You("are unaffected!");
927 pline("%s your mind!", Tobjnam(obj
, "zap"));
928 (void) make_hallucinated(
929 (HHallucination
& TIMEOUT
) + (long) rnd(100), FALSE
, 0L);
932 pline("%s!", Tobjnam(obj
, "explode"));
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",
941 consume_obj_charge(obj
, TRUE
);
947 pline("All you see is funky %s haze.", hcolor((char *) 0));
951 You("grok some groovy globs of incandescent lava.");
954 pline("Whoa! Psychedelic colors, %s!",
955 poly_gender() == 1 ? "babe" : "dude");
958 pline_The("crystal pulses with sinister %s light!",
962 You_see("goldfish swimming above fluorescent rocks.");
966 "tiny snowflakes spinning around a miniature farmhouse.");
969 pline("Oh wow... like a kaleidoscope!");
972 consume_obj_charge(obj
, TRUE
);
977 /* read a single character */
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
)) {
987 You("peer into %s...", the(xname(obj
)));
989 multi_reason
= "gazing into a crystal ball";
992 pline_The("vision is unclear.");
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
)
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
);
1015 ret
= trap_detect((struct obj
*) 0);
1018 int i
= rn2(SIZE(level_detects
));
1019 You_see("%s, %s.", level_detects
[i
].what
,
1020 level_distance(level_detects
[i
].where
));
1027 if (!rn2(100)) /* make them nervous */
1028 You_see("the Wizard of Yendor gazing out at you.");
1030 pline_The("vision is unclear.");
1044 if (Confusion
&& rn2(7))
1050 /* Secret corridors are found, but not secret doors. */
1051 if (lev
->typ
== SCORR
) {
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 */
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
) {
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
;
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 */
1097 exercise(A_WIS
, TRUE
);
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 */
1120 /* convert a secret door into a normal door */
1122 cvt_sdoor_to_door(lev
)
1125 int newmask
= lev
->doormask
& ~WM_MASK
;
1127 if (Is_rogue_level(&u
.uz
))
1128 /* rogue didn't have doors, only doorways */
1131 /* newly exposed door is closed */
1132 if (!(newmask
& D_LOCKED
))
1133 newmask
|= D_CLOSED
;
1136 lev
->doormask
= newmask
;
1140 findone(zx
, zy
, 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);
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);
1158 } else if ((ttmp
= t_at(zx
, zy
)) != 0) {
1159 if (!ttmp
->tseen
&& ttmp
->ttyp
!= STATUE_TRAP
) {
1164 } else if ((mtmp
= m_at(zx
, zy
)) != 0) {
1165 if (mtmp
->m_ap_type
) {
1169 if (mtmp
->mundetected
1170 && (is_hider(mtmp
->data
) || mtmp
->data
->mlet
== S_EEL
)) {
1171 mtmp
->mundetected
= 0;
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
);
1185 openone(zx
, zy
, 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
) {
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);
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
;
1217 levl
[zx
][zy
].doormask
= D_ISOPEN
;
1218 unblock_point(zx
, zy
);
1221 } else if (levl
[zx
][zy
].typ
== SCORR
) {
1222 levl
[zx
][zy
].typ
= CORR
;
1223 unblock_point(zx
, zy
);
1226 } else if ((ttmp
= t_at(zx
, zy
)) != 0) {
1228 boolean dummy
; /* unneeded "you notice it arg" */
1230 if (!ttmp
->tseen
&& ttmp
->ttyp
!= STATUE_TRAP
) {
1235 mon
= (zx
== u
.ux
&& zy
== u
.uy
) ? &youmonst
: m_at(zx
, zy
);
1236 if (openholdingtrap(mon
, &dummy
)
1237 || openfallingtrap(mon
, TRUE
, &dummy
))
1239 } else if (find_drawbridge(&zx
, &zy
)) {
1240 /* make sure it isn't an open drawbridge */
1241 open_drawbridge(zx
, zy
);
1246 /* returns number of things found */
1254 do_clear_area(u
.ux
, u
.uy
, BOLT_LIM
, findone
, (genericptr_t
) &num
);
1258 /* returns number of things found and opened */
1265 if (is_animal(u
.ustuck
->data
)) {
1267 pline("Its mouth opens!");
1269 pline("%s opens its mouth!", Monnam(u
.ustuck
));
1271 expels(u
.ustuck
, u
.ustuck
->data
, TRUE
);
1275 do_clear_area(u
.ux
, u
.uy
, BOLT_LIM
, openone
, (genericptr_t
) &num
);
1279 /* callback hack for overriding vision in do_clear_area() */
1282 void FDECL((*func
), (int, int, genericptr_t
));
1284 return (func
== findone
|| func
== openone
);
1291 int tt
= what_trap(trap
->ttyp
);
1292 boolean cleared
= FALSE
;
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 */
1306 You("find %s.", an(defsyms
[trap_to_defsym(tt
)].explanation
));
1309 display_nhwindow(WIN_MAP
, TRUE
); /* wait */
1316 register int aflag
; /* intrinsic autosearch vs explicit searching */
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
1324 volatile xchar x
, y
;
1326 register xchar x
, y
;
1328 register struct trap
*trap
;
1329 register struct monst
*mtmp
;
1333 pline("What are you looking for? The exit?");
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 */
1342 for (x
= u
.ux
- 1; x
< u
.ux
+ 2; x
++)
1343 for (y
= u
.uy
- 1; y
< u
.uy
+ 2; y
++) {
1346 if (x
== u
.ux
&& y
== u
.uy
)
1349 if (Blind
&& !aflag
)
1350 feel_location(x
, y
);
1351 if (levl
[x
][y
].typ
== SDOOR
) {
1354 cvt_sdoor_to_door(&levl
[x
][y
]); /* .typ = DOOR */
1355 exercise(A_WIS
, TRUE
);
1357 feel_location(x
, y
); /* make sure it shows up */
1358 You("find a hidden door.");
1359 } else if (levl
[x
][y
].typ
== SCORR
) {
1362 levl
[x
][y
].typ
= CORR
;
1363 unblock_point(x
, y
); /* vision */
1364 exercise(A_WIS
, TRUE
);
1366 feel_newsym(x
, y
); /* make sure it shows up */
1367 You("find a hidden passage.");
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
) {
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.
1386 You_feel("an unseen monster!");
1387 map_invisible(x
, y
);
1389 } else if (!sensemon(mtmp
))
1390 You("find %s.", mtmp
->mtame
1395 if (!canspotmon(mtmp
)) {
1396 if (mtmp
->mundetected
1397 && (is_hider(mtmp
->data
)
1398 || mtmp
->data
->mlet
== S_EEL
))
1399 mtmp
->mundetected
= 0;
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
)) {
1414 if ((trap
= t_at(x
, y
)) && !trap
->tseen
&& !rnl(8)) {
1416 if (trap
->ttyp
== STATUE_TRAP
) {
1417 if (activate_statue_trap(trap
, x
, y
, FALSE
))
1418 exercise(A_WIS
, TRUE
);
1430 /* the 's' command -- explicit searching */
1434 return dosearch0(0);
1437 /* Pre-map the sokoban levels */
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)
1456 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
) {
1459 /* set sokoban_rules when there is at least one pit or hole */
1460 if (ttmp
->ttyp
== PIT
|| ttmp
->ttyp
== HOLE
)
1465 /* idea from crawl; show known portion of map without any monsters,
1466 objects, or traps occluding the view of the underlying terrain */
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.");
1475 int x
, y
, glyph
, levl_glyph
, default_glyph
;
1477 unsigned save_swallowed
;
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;
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;
1498 levl
[x
][y
].seenv
= SVALL
;
1499 glyph
= back_to_glyph(x
, y
);
1500 levl
[x
][y
].seenv
= seenv
;
1502 levl_glyph
= level
.flags
.hero_memory
1505 ? back_to_glyph(x
, y
)
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
))
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
)) {
1529 glyph
= default_glyph
;
1530 } else if (lastseentyp
[x
][y
] == levl
[x
][y
].typ
) {
1531 glyph
= back_to_glyph(x
, y
);
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
);
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
;
1567 Strcpy(buf
, "underlying terrain");
1569 Strcpy(buf
, "known terrain");
1571 Sprintf(eos(buf
), "%s traps",
1572 (keep_objs
|| keep_mons
) ? "," : " and");
1574 Sprintf(eos(buf
), "%s%s objects",
1575 (keep_traps
|| keep_mons
) ? "," : "",
1576 keep_mons
? "" : " and");
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 */