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
));
23 STATIC_DCL
int FDECL(mfind0
, (struct monst
*, BOOLEAN_P
));
25 /* Recursively search obj for an object in class oclass and return 1st found
32 register struct obj
*otmp
;
35 if (obj
->oclass
== oclass
)
38 if (Has_contents(obj
)) {
39 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
40 if (otmp
->oclass
== oclass
)
42 else if (Has_contents(otmp
) && (temp
= o_in(otmp
, oclass
)))
45 return (struct obj
*) 0;
48 /* Recursively search obj for an object made of specified material.
52 o_material(obj
, material
)
56 register struct obj
*otmp
;
59 if (objects
[obj
->otyp
].oc_material
== material
)
62 if (Has_contents(obj
)) {
63 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
64 if (objects
[otmp
->otyp
].oc_material
== material
)
66 else if (Has_contents(otmp
)
67 && (temp
= o_material(otmp
, material
)))
70 return (struct obj
*) 0;
80 if (Has_contents(obj
)) {
81 for (otmp
= obj
->cobj
; otmp
; otmp
= otmp
->nobj
)
86 /* Check whether the location has an outdated object displayed on it. */
88 check_map_spot(x
, y
, oclass
, material
)
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
));
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
))
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
))
116 /* detection indicates removal of this object from the map */
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
))
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
))
130 /* detection indicates removal of this object from the map */
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
145 clear_stale_map(oclass
, material
)
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
);
162 /* look for gold, on the floor or in monsters' possession */
165 register struct obj
*sobj
;
167 register struct obj
*obj
;
168 register struct monst
*mtmp
;
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
) {
181 goto outgoldmap
; /* skip further searching */
183 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
184 if (sobj
->blessed
&& o_material(obj
, GOLD
)) {
187 } else if (o_in(obj
, COIN_CLASS
)) {
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
)) {
197 if (obj
->ox
!= u
.ux
|| obj
->oy
!= u
.uy
)
199 } else if (o_in(obj
, COIN_CLASS
)) {
201 if (obj
->ox
!= u
.ux
|| obj
->oy
!= u
.uy
)
207 /* no gold found on floor or monster's inventory.
208 adjust message if you have gold in your inventory */
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
))
215 "You feel worried about your future financial situation.");
217 Strcpy(buf
, "You feel materially poor.");
218 strange_feeling(sobj
, buf
);
222 /* only under me - no separate display required */
225 You("notice some gold between your %s.", makeplural(body_part(FOOT
)));
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
))) {
241 } else if ((temp
= o_in(obj
, COIN_CLASS
))) {
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
) {
254 gold
= zeroobj
; /* ensure oextra is cleared too */
255 gold
.otyp
= GOLD_PIECE
;
258 map_object(&gold
, 1);
260 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
261 if (sobj
->blessed
&& (temp
= o_material(obj
, GOLD
))) {
266 } else if ((temp
= o_in(obj
, COIN_CLASS
))) {
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
);
286 /* returns 1 if nothing was detected */
287 /* returns 0 if something was detected */
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
)
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
)) {
319 known
= stale
&& !confused
;
322 You("sense a lack of %s nearby.", what
);
323 if (sobj
&& sobj
->blessed
) {
325 Your("%s starts to tingle.", body_part(NOSE
));
330 Sprintf(buf
, "Your %s twitches%s.", body_part(NOSE
),
331 (sobj
->blessed
&& !u
.uedibility
)
332 ? " then starts to tingle"
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
;
342 strange_feeling(sobj
, buf
);
347 You("%s %s nearby.", sobj
? "smell" : "sense", what
);
348 if (sobj
&& sobj
->blessed
) {
350 pline("Your %s starts to tingle.", body_part(NOSE
));
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) {
367 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
)
368 /* no DEADMONSTER(mtmp) check needed since dmons never have
370 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
)
371 if ((temp
= o_in(obj
, oclass
)) != 0) {
375 break; /* skip rest of this monster's inventory */
378 u
.uinwater
= iflags
.save_uinwater
, u
.uburied
= iflags
.save_uburied
;
381 Your("%s %s to tingle and you smell %s.", body_part(NOSE
),
382 u
.uedibility
? "continues" : "starts", what
);
385 Your("%s tingles and you smell %s.", body_part(NOSE
), what
);
387 You("sense %s.", what
);
388 display_nhwindow(WIN_MAP
, TRUE
);
389 exercise(A_WIS
, TRUE
);
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 */
412 int is_cursed
= (detector
&& detector
->cursed
);
413 int do_dknown
= (detector
&& (detector
->oclass
== POTION_CLASS
414 || detector
->oclass
== SPBOOK_CLASS
)
415 && detector
->blessed
);
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);
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
);
439 Strcpy(stuff
, class ? def_oc_syms
[class].name
: "objects");
440 if (boulder
&& class != ROCK_CLASS
)
441 Strcat(stuff
, " and/or large stones");
444 for (obj
= invent
; obj
; obj
= obj
->nobj
)
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
)
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
)
469 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
470 if (DEADMONSTER(mtmp
))
472 for (obj
= mtmp
->minvent
; obj
; obj
= obj
->nobj
) {
473 if ((!class && !boulder
) || o_in(obj
, class)
474 || o_in(obj
, boulder
))
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
))) {
487 if (!clear_stale_map(!class ? ALL_CLASSES
: class, 0) && !ct
) {
490 strange_feeling(detector
, "You feel a lack of something.");
494 You("sense %s nearby.", stuff
);
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))) {
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
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
) {
540 /* Objects in the monster's inventory override floor objects. */
541 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
542 if (DEADMONSTER(mtmp
))
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
)
549 otmp
->ox
= mtmp
->mx
; /* at monster location */
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
)) {
559 temp
.oextra
= (struct oextra
*) 0;
560 temp
.otyp
= mtmp
->mappearance
; /* needed for obj_to_glyph() */
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
)) {
568 gold
= zeroobj
; /* ensure oextra is cleared too */
569 gold
.otyp
= GOLD_PIECE
;
572 map_object(&gold
, 1);
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 */
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
;
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
)) {
620 strange_feeling(otmp
, Hallucination
621 ? "You get the heebie jeebies."
622 : "You feel threatened.");
625 boolean woken
= FALSE
;
628 for (mtmp
= fmon
; mtmp
; mtmp
= mtmp
->nmon
) {
629 if (DEADMONSTER(mtmp
))
631 if (!mclass
|| mtmp
->data
->mlet
== mclass
632 || (mtmp
->data
== &mons
[PM_LONG_WORM
]
633 && mclass
== S_WORM_TAIL
))
635 if (mclass
&& def_monsyms
[mclass
].sym
== ' ')
636 show_glyph(mtmp
->mx
, mtmp
->my
,
637 detected_mon_to_glyph(mtmp
));
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;
653 You("sense the presence of monsters.");
655 pline("Monsters sense the presence of you.");
656 display_nhwindow(WIN_MAP
, TRUE
);
667 sense_trap(trap
, x
, y
, src_cursed
)
672 if (Hallucination
|| src_cursed
) {
673 struct obj obj
; /* fake object */
675 obj
.oextra
= (struct oextra
*) 0;
683 obj
.otyp
= (src_cursed
) ? GOLD_PIECE
: random_object();
684 obj
.corpsenm
= random_monster(); /* if otyp == CORPSE */
690 struct trap temp_trap
; /* fake trap */
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 */
706 detect_obj_traps(objlist
, show_them
, how
)
709 int how
; /* 1 for misleading map feedback */
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
;
720 sense_trap((struct trap
*) 0, x
, y
, how
);
722 if (Has_contents(otmp
))
723 result
|= detect_obj_traps(otmp
->cobj
, show_them
, how
);
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
735 register struct obj
*sobj
;
736 /* sobj is null if crystal ball, *scroll if gold detection scroll */
738 register struct trap
*ttmp
;
741 int cursed_src
= sobj
&& sobj
->cursed
;
742 boolean found
= FALSE
;
745 /* floor/ceiling traps */
746 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
) {
747 if (ttmp
->tx
!= u
.ux
|| ttmp
->ty
!= u
.uy
)
752 /* chest traps (might be buried or carried) */
753 if ((tr
= detect_obj_traps(fobj
, FALSE
, 0)) != OTRAP_NONE
) {
754 if (tr
& OTRAP_THERE
)
759 if ((tr
= detect_obj_traps(level
.buriedobjlist
, FALSE
, 0))
761 if (tr
& OTRAP_THERE
)
766 for (mon
= fmon
; mon
; mon
= mon
->nmon
) {
767 if (DEADMONSTER(mon
))
769 if ((tr
= detect_obj_traps(mon
->minvent
, FALSE
, 0)) != OTRAP_NONE
) {
770 if (tr
& OTRAP_THERE
)
776 if (detect_obj_traps(invent
, FALSE
, 0) != OTRAP_NONE
)
779 for (door
= 0; door
< doorindex
; door
++) {
781 if (levl
[cc
.x
][cc
.y
].doormask
& D_TRAPPED
) {
782 if (cc
.x
!= u
.ux
|| cc
.y
!= u
.uy
)
791 Sprintf(buf
, "Your %s stop itching.", makeplural(body_part(TOE
)));
792 strange_feeling(sobj
, buf
);
795 /* traps exist, but only under me - no separate display required */
796 Your("%s itch.", makeplural(body_part(TOE
)));
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
))
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
++) {
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
)))
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
);
842 level_distance(where
)
845 register schar ll
= depth(&u
.uz
) - depth(where
);
846 register boolean indun
= (u
.uz
.dnum
== where
->dnum
);
849 if (ll
< (-8 - rn2(3)))
856 return "away below you";
860 return "in the distance";
864 if (ll
> (8 + rn2(3)))
871 return "away above you";
875 return "in the distance";
879 return "in the distance";
884 static const struct {
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
},
895 use_crystal_ball(optr
)
900 struct obj
*obj
= *optr
;
903 pline("Too bad you can't see %s.", the(xname(obj
)));
906 oops
= (rnd(20) > ACURR(A_INT
) || obj
->cursed
);
907 if (oops
&& (obj
->spe
> 0)) {
908 switch (rnd(obj
->oartifact
? 4 : 5)) {
910 pline("%s too much to comprehend!", Tobjnam(obj
, "are"));
913 pline("%s you!", Tobjnam(obj
, "confuse"));
914 make_confused((HConfusion
& TIMEOUT
) + (long) rnd(100), FALSE
);
917 if (!resists_blnd(&youmonst
)) {
918 pline("%s your vision!", Tobjnam(obj
, "damage"));
919 make_blinded((Blinded
& TIMEOUT
) + (long) rnd(100), FALSE
);
921 Your1(vision_clears
);
923 pline("%s your vision.", Tobjnam(obj
, "assault"));
924 You("are unaffected!");
928 pline("%s your mind!", Tobjnam(obj
, "zap"));
929 (void) make_hallucinated(
930 (HHallucination
& TIMEOUT
) + (long) rnd(100), FALSE
, 0L);
933 pline("%s!", Tobjnam(obj
, "explode"));
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",
942 consume_obj_charge(obj
, TRUE
);
948 pline("All you see is funky %s haze.", hcolor((char *) 0));
952 You("grok some groovy globs of incandescent lava.");
955 pline("Whoa! Psychedelic colors, %s!",
956 poly_gender() == 1 ? "babe" : "dude");
959 pline_The("crystal pulses with sinister %s light!",
963 You_see("goldfish swimming above fluorescent rocks.");
967 "tiny snowflakes spinning around a miniature farmhouse.");
970 pline("Oh wow... like a kaleidoscope!");
973 consume_obj_charge(obj
, TRUE
);
978 /* read a single character */
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
)) {
988 You("peer into %s...", the(xname(obj
)));
990 multi_reason
= "gazing into a crystal ball";
993 pline_The("vision is unclear.");
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
)
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
);
1016 ret
= trap_detect((struct obj
*) 0);
1019 int i
= rn2(SIZE(level_detects
));
1020 You_see("%s, %s.", level_detects
[i
].what
,
1021 level_distance(level_detects
[i
].where
));
1028 if (!rn2(100)) /* make them nervous */
1029 You_see("the Wizard of Yendor gazing out at you.");
1031 pline_The("vision is unclear.");
1045 if (Confusion
&& rn2(7))
1051 /* Secret corridors are found, but not secret doors. */
1052 if (lev
->typ
== SCORR
) {
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 */
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
) {
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
;
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 */
1098 exercise(A_WIS
, TRUE
);
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 */
1121 /* convert a secret door into a normal door */
1123 cvt_sdoor_to_door(lev
)
1126 int newmask
= lev
->doormask
& ~WM_MASK
;
1128 if (Is_rogue_level(&u
.uz
))
1129 /* rogue didn't have doors, only doorways */
1132 /* newly exposed door is closed */
1133 if (!(newmask
& D_LOCKED
))
1134 newmask
|= D_CLOSED
;
1137 lev
->doormask
= newmask
;
1141 findone(zx
, zy
, 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);
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);
1159 } else if ((ttmp
= t_at(zx
, zy
)) != 0) {
1160 if (!ttmp
->tseen
&& ttmp
->ttyp
!= STATUE_TRAP
) {
1165 } else if ((mtmp
= m_at(zx
, zy
)) != 0) {
1166 if (mtmp
->m_ap_type
) {
1170 if (mtmp
->mundetected
1171 && (is_hider(mtmp
->data
) || mtmp
->data
->mlet
== S_EEL
)) {
1172 mtmp
->mundetected
= 0;
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
);
1186 openone(zx
, zy
, 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
) {
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);
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
;
1218 levl
[zx
][zy
].doormask
= D_ISOPEN
;
1219 unblock_point(zx
, zy
);
1222 } else if (levl
[zx
][zy
].typ
== SCORR
) {
1223 levl
[zx
][zy
].typ
= CORR
;
1224 unblock_point(zx
, zy
);
1227 } else if ((ttmp
= t_at(zx
, zy
)) != 0) {
1229 boolean dummy
; /* unneeded "you notice it arg" */
1231 if (!ttmp
->tseen
&& ttmp
->ttyp
!= STATUE_TRAP
) {
1236 mon
= (zx
== u
.ux
&& zy
== u
.uy
) ? &youmonst
: m_at(zx
, zy
);
1237 if (openholdingtrap(mon
, &dummy
)
1238 || openfallingtrap(mon
, TRUE
, &dummy
))
1240 } else if (find_drawbridge(&zx
, &zy
)) {
1241 /* make sure it isn't an open drawbridge */
1242 open_drawbridge(zx
, zy
);
1247 /* returns number of things found */
1255 do_clear_area(u
.ux
, u
.uy
, BOLT_LIM
, findone
, (genericptr_t
) &num
);
1259 /* returns number of things found and opened */
1266 if (is_animal(u
.ustuck
->data
)) {
1268 pline("Its mouth opens!");
1270 pline("%s opens its mouth!", Monnam(u
.ustuck
));
1272 expels(u
.ustuck
, u
.ustuck
->data
, TRUE
);
1276 do_clear_area(u
.ux
, u
.uy
, BOLT_LIM
, openone
, (genericptr_t
) &num
);
1280 /* callback hack for overriding vision in do_clear_area() */
1283 void FDECL((*func
), (int, int, genericptr_t
));
1285 return (func
== findone
|| func
== openone
);
1292 int tt
= what_trap(trap
->ttyp
);
1293 boolean cleared
= FALSE
;
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 */
1307 You("find %s.", an(defsyms
[trap_to_defsym(tt
)].explanation
));
1310 display_nhwindow(WIN_MAP
, TRUE
); /* wait */
1316 mfind0(mtmp
, via_warning
)
1318 boolean via_warning
;
1323 if (via_warning
&& !warning_of(mtmp
))
1326 if (mtmp
->m_ap_type
) {
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.
1341 You_feel("an unseen monster!");
1342 map_invisible(x
, y
);
1344 } else if (!sensemon(mtmp
))
1345 You("find %s.", mtmp
->mtame
1350 if (!canspotmon(mtmp
)) {
1351 if (mtmp
->mundetected
1352 && (is_hider(mtmp
->data
)
1353 || mtmp
->data
->mlet
== S_EEL
))
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;
1368 register int aflag
; /* intrinsic autosearch vs explicit searching */
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
1376 volatile xchar x
, y
;
1378 register xchar x
, y
;
1380 register struct trap
*trap
;
1381 register struct monst
*mtmp
;
1385 pline("What are you looking for? The exit?");
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 */
1394 for (x
= u
.ux
- 1; x
< u
.ux
+ 2; x
++)
1395 for (y
= u
.uy
- 1; y
< u
.uy
+ 2; y
++) {
1398 if (x
== u
.ux
&& y
== u
.uy
)
1401 if (Blind
&& !aflag
)
1402 feel_location(x
, y
);
1403 if (levl
[x
][y
].typ
== SDOOR
) {
1406 cvt_sdoor_to_door(&levl
[x
][y
]); /* .typ = DOOR */
1407 exercise(A_WIS
, TRUE
);
1409 feel_location(x
, y
); /* make sure it shows up */
1410 You("find a hidden door.");
1411 } else if (levl
[x
][y
].typ
== SCORR
) {
1414 levl
[x
][y
].typ
= CORR
;
1415 unblock_point(x
, y
); /* vision */
1416 exercise(A_WIS
, TRUE
);
1418 feel_newsym(x
, y
); /* make sure it shows up */
1419 You("find a hidden passage.");
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);
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
)) {
1440 if ((trap
= t_at(x
, y
)) && !trap
->tseen
&& !rnl(8)) {
1442 if (trap
->ttyp
== STATUE_TRAP
) {
1443 if (activate_statue_trap(trap
, x
, y
, FALSE
))
1444 exercise(A_WIS
, TRUE
);
1456 /* the 's' command -- explicit searching */
1460 return dosearch0(0);
1469 for (x
= u
.ux
- 1; x
< u
.ux
+ 2; x
++)
1470 for (y
= u
.uy
- 1; y
< u
.uy
+ 2; y
++) {
1473 if (x
== u
.ux
&& y
== u
.uy
)
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 */
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)
1501 for (ttmp
= ftrap
; ttmp
; ttmp
= ttmp
->ntrap
) {
1504 /* set sokoban_rules when there is at least one pit or hole */
1505 if (ttmp
->ttyp
== PIT
|| ttmp
->ttyp
== HOLE
)
1510 /* idea from crawl; show known portion of map without any monsters,
1511 objects, or traps occluding the view of the underlying terrain */
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.");
1520 int x
, y
, glyph
, levl_glyph
, default_glyph
;
1522 unsigned save_swallowed
;
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;
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;
1543 levl
[x
][y
].seenv
= SVALL
;
1544 glyph
= back_to_glyph(x
, y
);
1545 levl
[x
][y
].seenv
= seenv
;
1547 levl_glyph
= level
.flags
.hero_memory
1550 ? back_to_glyph(x
, y
)
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
))
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
)) {
1574 glyph
= default_glyph
;
1575 } else if (lastseentyp
[x
][y
] == levl
[x
][y
].typ
) {
1576 glyph
= back_to_glyph(x
, y
);
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
);
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
;
1612 Strcpy(buf
, "underlying terrain");
1614 Strcpy(buf
, "known terrain");
1616 Sprintf(eos(buf
), "%s traps",
1617 (keep_objs
|| keep_mons
) ? "," : " and");
1619 Sprintf(eos(buf
), "%s%s objects",
1620 (keep_traps
|| keep_mons
) ? "," : "",
1621 keep_mons
? "" : " and");
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 */