1 /* SCCS Id: @(#)detect.c 3.4 2003/08/13 */
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 void FDECL(show_map_spot, (int,int));
20 STATIC_PTR void FDECL(findone,(int,int,genericptr_t));
21 STATIC_PTR void FDECL(openone,(int,int,genericptr_t));
23 /* Recursively search obj for an object in class oclass and return 1st found */
29 register struct obj* otmp;
32 if (obj->oclass == oclass) return obj;
34 if (Has_contents(obj)) {
35 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
36 if (otmp->oclass == oclass) return otmp;
37 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)))
40 return (struct obj *) 0;
43 /* Recursively search obj for an object made of specified material and return 1st found */
45 o_material(obj, material)
49 register struct obj* otmp;
52 if (objects[obj->otyp].oc_material == material) return obj;
54 if (Has_contents(obj)) {
55 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
56 if (objects[otmp->otyp].oc_material == material) return otmp;
57 else if (Has_contents(otmp) && (temp = o_material(otmp, material)))
60 return (struct obj *) 0;
70 if (Has_contents(obj)) {
71 for(otmp = obj->cobj; otmp; otmp = otmp->nobj)
76 /* Check whether the location has an outdated object displayed on it. */
78 check_map_spot(x, y, oclass, material)
84 register struct obj *otmp;
85 register struct monst *mtmp;
87 glyph = glyph_at(x,y);
88 if (glyph_is_object(glyph)) {
89 /* there's some object shown here */
90 if (oclass == ALL_CLASSES) {
91 return((boolean)( !(level.objects[x][y] || /* stale if nothing here */
92 ((mtmp = m_at(x,y)) != 0 &&
99 if (material && objects[glyph_to_obj(glyph)].oc_material == material) {
100 /* the object shown here is of interest because material matches */
101 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
102 if (o_material(otmp, GOLD)) return FALSE;
103 /* didn't find it; perhaps a monster is carrying it */
104 if ((mtmp = m_at(x,y)) != 0) {
105 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
106 if (o_material(otmp, GOLD)) return FALSE;
108 /* detection indicates removal of this object from the map */
111 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
112 /* the object shown here is of interest because its class matches */
113 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
114 if (o_in(otmp, oclass)) return FALSE;
115 /* didn't find it; perhaps a monster is carrying it */
117 if ((mtmp = m_at(x,y)) != 0) {
118 if (oclass == COIN_CLASS && mtmp->mgold)
120 else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
121 if (o_in(otmp, oclass)) return FALSE;
124 if ((mtmp = m_at(x,y)) != 0) {
125 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
126 if (o_in(otmp, oclass)) return FALSE;
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)
145 register char oclass;
149 register 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 known = stale = clear_stale_map(COIN_CLASS,
173 (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)) continue; /* probably not needed in this case but... */
179 if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
181 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
184 goto outgoldmap; /* skip further searching */
185 } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
186 if (sobj->blessed && o_material(obj, GOLD)) {
189 } else if (o_in(obj, COIN_CLASS)) {
191 goto outgoldmap; /* skip further searching */
195 /* look for gold objects */
196 for (obj = fobj; obj; obj = obj->nobj) {
197 if (sobj->blessed && o_material(obj, GOLD)) {
199 if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
200 } else if (o_in(obj, COIN_CLASS)) {
202 if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
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!",
214 } else if (hidden_gold() ||
221 "You feel worried about your future financial situation.");
223 Strcpy(buf, "You feel materially poor.");
224 strange_feeling(sobj, buf);
228 /* only under me - no separate display required */
230 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
237 /* Discover gold locations. */
238 for (obj = fobj; obj; obj = obj->nobj) {
239 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
245 } else if ((temp = o_in(obj, COIN_CLASS))) {
253 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
254 if (DEADMONSTER(mtmp)) continue; /* probably overkill here */
256 if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
258 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
262 gold.otyp = GOLD_PIECE;
266 } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
267 if (sobj->blessed && (temp = o_material(obj, GOLD))) {
272 } else if ((temp = o_in(obj, COIN_CLASS))) {
281 You_feel("very greedy, and sense gold!");
282 exercise(A_WIS, TRUE);
283 display_nhwindow(WIN_MAP, TRUE);
286 if (Underwater) under_water(2);
287 if (u.uburied) under_ground(2);
291 /* returns 1 if nothing was detected */
292 /* returns 0 if something was detected */
295 register struct obj *sobj;
297 register struct obj *obj;
298 register struct monst *mtmp;
299 register int ct = 0, ctu = 0;
300 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
301 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
302 const char *what = confused ? something : "food";
305 stale = clear_stale_map(oclass, 0);
307 for (obj = fobj; obj; obj = obj->nobj)
308 if (o_in(obj, oclass)) {
309 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
312 for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
313 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */
314 for (obj = mtmp->minvent; obj; obj = obj->nobj)
315 if (o_in(obj, oclass)) {
322 known = stale && !confused;
325 You("sense a lack of %s nearby.", what);
326 if (sobj && sobj->blessed) {
327 if (!u.uedibility) Your("%s starts to tingle.", body_part(NOSE));
332 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
333 (sobj->blessed && !u.uedibility) ? " then starts to tingle" : "");
334 if (sobj->blessed && !u.uedibility) {
335 boolean savebeginner = flags.beginner; /* prevent non-delivery of */
336 flags.beginner = FALSE; /* 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) {
348 if (!u.uedibility) pline("Your %s starts to tingle.", body_part(NOSE));
356 for (obj = fobj; obj; obj = obj->nobj)
357 if ((temp = o_in(obj, oclass)) != 0) {
364 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
365 /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */
366 for (obj = mtmp->minvent; obj; obj = obj->nobj)
367 if ((temp = o_in(obj, oclass)) != 0) {
371 break; /* skip rest of this monster's inventory */
376 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
377 u.uedibility ? "continues" : "starts", what);
380 Your("%s tingles and you smell %s.", body_part(NOSE), what);
382 else You("sense %s.", what);
383 display_nhwindow(WIN_MAP, TRUE);
384 exercise(A_WIS, TRUE);
387 if (Underwater) under_water(2);
388 if (u.uburied) under_ground(2);
394 * Used for scrolls, potions, spells, and crystal balls. Returns:
396 * 1 - nothing was detected
397 * 0 - something was detected
400 object_detect(detector, class)
401 struct obj *detector; /* object doing the detecting */
402 int class; /* an object class, 0 for all */
406 int is_cursed = (detector && detector->cursed);
407 int do_dknown = (detector && (detector->oclass == POTION_CLASS ||
408 detector->oclass == SPBOOK_CLASS) &&
411 register struct obj *obj, *otmp = (struct obj *)0;
412 register struct monst *mtmp;
414 int sym, boulder = 0;
416 if (class < 0 || class >= MAXOCLASSES) {
417 impossible("object_detect: illegal class %d", class);
421 /* Special boulder symbol check - does the class symbol happen
422 * to match iflags.bouldersym which is a user-defined?
423 * If so, that means we aren't sure what they really wanted to
424 * detect. Rather than trump anything, show both possibilities.
425 * We can exclude checking the buried obj chain for boulders below.
427 sym = class ? def_oc_syms[class] : 0;
428 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
429 boulder = ROCK_CLASS;
431 if (Hallucination || (Confusion && class == SCROLL_CLASS))
432 Strcpy(stuff, something);
434 Strcpy(stuff, class ? oclass_names[class] : "objects");
435 if (boulder && class != ROCK_CLASS) Strcat(stuff, " and/or large stones");
437 if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj);
439 for (obj = fobj; obj; obj = obj->nobj) {
440 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
441 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
444 if (do_dknown) do_dknown_of(obj);
447 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
448 if (!class || o_in(obj, class)) {
449 if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
452 if (do_dknown) do_dknown_of(obj);
455 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
456 if (DEADMONSTER(mtmp)) continue;
457 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
458 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) ct++;
459 if (do_dknown) do_dknown_of(obj);
461 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
462 (!class || class == objects[mtmp->mappearance].oc_class)) ||
464 (mtmp->mgold && (!class || class == COIN_CLASS))) {
466 (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
473 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
476 strange_feeling(detector, "You feel a lack of something.");
480 You("sense %s nearby.", stuff);
488 * Map all buried objects first.
490 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
491 if (!class || (otmp = o_in(obj, class))) {
502 * If we are mapping all objects, map only the top object of a pile or
503 * the first object in a monster's inventory. Otherwise, go looking
504 * for a matching object class and display the first one encountered
507 * Objects on the floor override buried objects.
509 for (x = 1; x < COLNO; x++)
510 for (y = 0; y < ROWNO; y++)
511 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
512 if ((!class && !boulder) ||
513 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) {
514 if (class || boulder) {
525 /* Objects in the monster's inventory override floor objects. */
526 for (mtmp = fmon ; mtmp ; mtmp = mtmp->nmon) {
527 if (DEADMONSTER(mtmp)) continue;
528 for (obj = mtmp->minvent; obj; obj = obj->nobj)
529 if ((!class && !boulder) ||
530 (otmp = o_in(obj, class)) || (otmp = o_in(obj, boulder))) {
531 if (!class && !boulder) otmp = obj;
532 otmp->ox = mtmp->mx; /* at monster location */
537 /* Allow a mimic to override the detected objects it is carrying. */
538 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
539 (!class || class == objects[mtmp->mappearance].oc_class)) {
542 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
545 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
546 map_object(&temp, 1);
548 } else if (mtmp->mgold && (!class || class == COIN_CLASS)) {
550 } else if (findgold(mtmp->minvent) && (!class || class == COIN_CLASS)) {
554 gold.otyp = GOLD_PIECE;
557 map_object(&gold, 1);
562 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
563 display_nhwindow(WIN_MAP, TRUE);
565 * What are we going to do when the hero does an object detect while blind
566 * and the detected object covers a known pool?
568 docrt(); /* this will correctly reset vision */
571 if (Underwater) under_water(2);
572 if (u.uburied) under_ground(2);
577 * Used by: crystal balls, potions, fountains
579 * Returns 1 if nothing was detected.
580 * Returns 0 if something was detected.
583 monster_detect(otmp, mclass)
584 register struct obj *otmp; /* detecting object (if any) */
585 int mclass; /* monster class, 0 for all */
587 register struct monst *mtmp;
591 /* Note: This used to just check fmon for a non-zero value
592 * but in versions since 3.3.0 fmon can test TRUE due to the
593 * presence of dmons, so we have to find at least one
594 * with positive hit-points to know for sure.
596 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
597 if (!DEADMONSTER(mtmp)) {
604 strange_feeling(otmp, Hallucination ?
605 "You get the heebie jeebies." :
606 "You feel threatened.");
609 boolean woken = FALSE;
612 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
613 if (DEADMONSTER(mtmp)) continue;
614 if (!mclass || mtmp->data->mlet == mclass ||
615 (mtmp->data == &mons[PM_LONG_WORM] && mclass == S_WORM_TAIL))
617 if (mclass && def_monsyms[mclass] == ' ')
618 show_glyph(mtmp->mx,mtmp->my,
619 detected_mon_to_glyph(mtmp));
621 show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp));
622 /* don't be stingy - display entire worm */
623 if (mtmp->data == &mons[PM_LONG_WORM]) detect_wsegs(mtmp,0);
625 if (otmp && otmp->cursed &&
626 (mtmp->msleeping || !mtmp->mcanmove)) {
627 mtmp->msleeping = mtmp->mfrozen = 0;
633 You("sense the presence of monsters.");
635 pline("Monsters sense the presence of you.");
636 display_nhwindow(WIN_MAP, TRUE);
638 if (Underwater) under_water(2);
639 if (u.uburied) under_ground(2);
645 sense_trap(trap, x, y, src_cursed)
650 if (Hallucination || src_cursed) {
651 struct obj obj; /* fake object */
659 obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
660 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
666 struct trap temp_trap; /* fake trap */
669 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
670 map_trap(&temp_trap,1);
675 /* the detections are pulled out so they can */
676 /* also be used in the crystal ball routine */
677 /* returns 1 if nothing was detected */
678 /* returns 0 if something was detected */
681 register struct obj *sobj;
682 /* sobj is null if crystal ball, *scroll if gold detection scroll */
684 register struct trap *ttmp;
685 register struct obj *obj;
688 boolean found = FALSE;
691 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
692 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
696 for (obj = fobj; obj; obj = obj->nobj) {
697 if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) {
698 if (obj->ox != u.ux || obj->oy != u.uy)
703 for (door = 0; door < doorindex; door++) {
705 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
706 if (cc.x != u.ux || cc.y != u.uy)
713 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
714 strange_feeling(sobj,buf);
717 /* traps exist, but only under me - no separate display required */
718 Your("%s itch.", makeplural(body_part(TOE)));
724 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
725 sense_trap(ttmp, 0, 0, sobj && sobj->cursed);
727 for (obj = fobj; obj; obj = obj->nobj)
728 if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped)
729 sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed);
731 for (door = 0; door < doorindex; door++) {
733 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
734 sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed);
738 You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped");
739 display_nhwindow(WIN_MAP, TRUE);
742 if (Underwater) under_water(2);
743 if (u.uburied) under_ground(2);
748 level_distance(where)
751 register schar ll = depth(&u.uz) - depth(where);
752 register boolean indun = (u.uz.dnum == where->dnum);
755 if (ll < (-8 - rn2(3)))
756 if (!indun) return "far away";
757 else return "far below";
759 if (!indun) return "away below you";
760 else return "below you";
762 if (!indun) return "in the distance";
763 else return "just below";
765 if (ll > (8 + rn2(3)))
766 if (!indun) return "far away";
767 else return "far above";
769 if (!indun) return "away above you";
770 else return "above you";
772 if (!indun) return "in the distance";
773 else return "just above";
775 if (!indun) return "in the distance";
776 else return "near you";
779 static const struct {
782 } level_detects[] = {
783 { "Delphi", &oracle_level },
784 { "Medusa's lair", &medusa_level },
785 { "a castle", &stronghold_level },
786 { "the Wizard of Yendor's tower", &wiz1_level },
790 use_crystal_ball(obj)
797 pline("Too bad you can't see %s.", the(xname(obj)));
800 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
801 if (oops && (obj->spe > 0)) {
802 switch (rnd(obj->oartifact ? 4 : 5)) {
803 case 1 : pline("%s too much to comprehend!", Tobjnam(obj, "are"));
805 case 2 : pline("%s you!", Tobjnam(obj, "confuse"));
806 make_confused(HConfusion + rnd(100),FALSE);
808 case 3 : if (!resists_blnd(&youmonst)) {
809 pline("%s your vision!", Tobjnam(obj, "damage"));
810 make_blinded(Blinded + rnd(100),FALSE);
811 if (!Blind) Your(vision_clears);
813 pline("%s your vision.", Tobjnam(obj, "assault"));
814 You("are unaffected!");
817 case 4 : pline("%s your mind!", Tobjnam(obj, "zap"));
818 (void) make_hallucinated(HHallucination + rnd(100),FALSE,0L);
820 case 5 : pline("%s!", Tobjnam(obj, "explode"));
822 obj = 0; /* it's gone */
823 losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN);
826 if (obj) consume_obj_charge(obj, TRUE);
832 pline("All you see is funky %s haze.", hcolor((char *)0));
835 case 1 : You("grok some groovy globs of incandescent lava.");
837 case 2 : pline("Whoa! Psychedelic colors, %s!",
838 poly_gender() == 1 ? "babe" : "dude");
840 case 3 : pline_The("crystal pulses with sinister %s light!",
843 case 4 : You("see goldfish swimming above fluorescent rocks.");
845 case 5 : You("see tiny snowflakes spinning around a miniature farmhouse.");
847 default: pline("Oh wow... like a kaleidoscope!");
850 consume_obj_charge(obj, TRUE);
855 /* read a single character */
856 if (flags.verbose) You("may look for an object or monster symbol.");
857 ch = yn_function("What do you look for?", (char *)0, '\0');
858 /* Don't filter out ' ' here; it has a use */
859 if ((ch != def_monsyms[S_GHOST]) && index(quitchars,ch)) {
860 if (flags.verbose) pline(Never_mind);
863 You("peer into %s...", the(xname(obj)));
867 pline_The("vision is unclear.");
872 makeknown(CRYSTAL_BALL);
873 consume_obj_charge(obj, TRUE);
875 /* special case: accept ']' as synonym for mimic
876 * we have to do this before the def_char_to_objclass check
878 if (ch == DEF_MIMIC_DEF) ch = DEF_MIMIC;
880 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
881 ret = object_detect((struct obj *)0, class);
882 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
883 ret = monster_detect((struct obj *)0, class);
884 else if (iflags.bouldersym && (ch == iflags.bouldersym))
885 ret = object_detect((struct obj *)0, ROCK_CLASS);
888 ret = trap_detect((struct obj *)0);
892 int i = rn2(SIZE(level_detects));
894 level_detects[i].what,
895 level_distance(level_detects[i].where));
902 if (!rn2(100)) /* make them nervous */
903 You("see the Wizard of Yendor gazing out at you.");
904 else pline_The("vision is unclear.");
914 register struct rm *lev;
916 if (Confusion && rn2(7)) return;
921 /* Secret corridors are found, but not secret doors. */
922 if (lev->typ == SCORR) {
927 /* if we don't remember an object or trap there, map it */
928 if (lev->typ == ROOM ?
929 (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) &&
930 glyph_to_cmap(lev->glyph) != ROOM) :
931 (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) {
932 if (level.flags.hero_memory) {
933 magic_map_background(x,y,0);
934 newsym(x,y); /* show it, if not blocked */
936 magic_map_background(x,y,1); /* display it */
948 for (zx = 1; zx < COLNO; zx++)
949 for (zy = 0; zy < ROWNO; zy++)
950 show_map_spot(zx, zy);
951 exercise(A_WIS, TRUE);
953 if (!level.flags.hero_memory || Underwater) {
954 flush_screen(1); /* flush temp screen */
955 display_nhwindow(WIN_MAP, TRUE); /* wait */
964 int lo_y = (u.uy-5 < 0 ? 0 : u.uy-5),
965 hi_y = (u.uy+6 > ROWNO ? ROWNO : u.uy+6),
966 lo_x = (u.ux-9 < 1 ? 1 : u.ux-9), /* avoid column 0 */
967 hi_x = (u.ux+10 > COLNO ? COLNO : u.ux+10);
969 for (zx = lo_x; zx < hi_x; zx++)
970 for (zy = lo_y; zy < hi_y; zy++)
971 show_map_spot(zx, zy);
973 if (!level.flags.hero_memory || Underwater) {
974 flush_screen(1); /* flush temp screen */
975 display_nhwindow(WIN_MAP, TRUE); /* wait */
980 /* convert a secret door into a normal door */
982 cvt_sdoor_to_door(lev)
985 int newmask = lev->doormask & ~WM_MASK;
988 if (Is_rogue_level(&u.uz))
989 /* rogue didn't have doors, only doorways */
993 /* newly exposed door is closed */
994 if (!(newmask & D_LOCKED)) newmask |= D_CLOSED;
997 lev->doormask = newmask;
1006 register struct trap *ttmp;
1007 register struct monst *mtmp;
1009 if(levl[zx][zy].typ == SDOOR) {
1010 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1011 magic_map_background(zx, zy, 0);
1014 } else if(levl[zx][zy].typ == SCORR) {
1015 levl[zx][zy].typ = CORR;
1016 unblock_point(zx,zy);
1017 magic_map_background(zx, zy, 0);
1020 } else if ((ttmp = t_at(zx, zy)) != 0) {
1021 if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1026 } else if ((mtmp = m_at(zx, zy)) != 0) {
1027 if(mtmp->m_ap_type) {
1031 if (mtmp->mundetected &&
1032 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1033 mtmp->mundetected = 0;
1037 if (!canspotmon(mtmp) &&
1038 !glyph_is_invisible(levl[zx][zy].glyph))
1039 map_invisible(zx, zy);
1040 } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
1041 unmap_object(zx, zy);
1052 register struct trap *ttmp;
1053 register struct obj *otmp;
1055 if(OBJ_AT(zx, zy)) {
1056 for(otmp = level.objects[zx][zy];
1057 otmp; otmp = otmp->nexthere) {
1058 if(Is_box(otmp) && otmp->olocked) {
1063 /* let it fall to the next cases. could be on trap. */
1065 if(levl[zx][zy].typ == SDOOR || (levl[zx][zy].typ == DOOR &&
1066 (levl[zx][zy].doormask & (D_CLOSED|D_LOCKED)))) {
1067 if(levl[zx][zy].typ == SDOOR)
1068 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1069 if(levl[zx][zy].doormask & D_TRAPPED) {
1070 if(distu(zx, zy) < 3) b_trapped("door", 0);
1071 else Norep("You %s an explosion!",
1072 cansee(zx, zy) ? "see" :
1073 (flags.soundok ? "hear" :
1074 "feel the shock of"));
1075 wake_nearto(zx, zy, 11*11);
1076 levl[zx][zy].doormask = D_NODOOR;
1078 levl[zx][zy].doormask = D_ISOPEN;
1079 unblock_point(zx, zy);
1082 } else if(levl[zx][zy].typ == SCORR) {
1083 levl[zx][zy].typ = CORR;
1084 unblock_point(zx, zy);
1087 } else if ((ttmp = t_at(zx, zy)) != 0) {
1088 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1093 } else if (find_drawbridge(&zx, &zy)) {
1094 /* make sure it isn't an open drawbridge */
1095 open_drawbridge(zx, zy);
1101 findit() /* returns number of things found */
1105 if(u.uswallow) return(0);
1106 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1111 openit() /* returns number of things found and opened */
1116 if (is_animal(u.ustuck->data)) {
1117 if (Blind) pline("Its mouth opens!");
1118 else pline("%s opens its mouth!", Monnam(u.ustuck));
1120 expels(u.ustuck, u.ustuck->data, TRUE);
1124 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1132 int tt = what_trap(trap->ttyp);
1133 boolean cleared = FALSE;
1136 exercise(A_WIS, TRUE);
1138 feel_location(trap->tx, trap->ty);
1140 newsym(trap->tx, trap->ty);
1142 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1143 /* There's too much clutter to see your find otherwise */
1150 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1153 display_nhwindow(WIN_MAP, TRUE); /* wait */
1163 /* some versions of gcc seriously muck up nested loops. if you get strange
1164 crashes while searching in a version compiled with gcc, try putting
1165 #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
1168 volatile xchar x, y;
1170 register xchar x, y;
1172 register struct trap *trap;
1173 register struct monst *mtmp;
1177 pline("What are you looking for? The exit?");
1179 int fund = (uwep && uwep->oartifact &&
1180 spec_ability(uwep, SPFX_SEARCH)) ?
1182 if (ublindf && ublindf->otyp == LENSES && !Blind)
1183 fund += 2; /* JDS: lenses help searching */
1184 if (fund > 5) fund = 5;
1185 for(x = u.ux-1; x < u.ux+2; x++)
1186 for(y = u.uy-1; y < u.uy+2; y++) {
1187 if(!isok(x,y)) continue;
1188 if(x != u.ux || y != u.uy) {
1189 if (Blind && !aflag) feel_location(x,y);
1190 if(levl[x][y].typ == SDOOR) {
1191 if(rnl(7-fund)) continue;
1192 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1193 exercise(A_WIS, TRUE);
1195 if (Blind && !aflag)
1196 feel_location(x,y); /* make sure it shows up */
1199 } else if(levl[x][y].typ == SCORR) {
1200 if(rnl(7-fund)) continue;
1201 levl[x][y].typ = CORR;
1202 unblock_point(x,y); /* vision */
1203 exercise(A_WIS, TRUE);
1207 /* Be careful not to find anything in an SCORR or SDOOR */
1208 if((mtmp = m_at(x, y)) && !aflag) {
1209 if(mtmp->m_ap_type) {
1211 find: exercise(A_WIS, TRUE);
1212 if (!canspotmon(mtmp)) {
1213 if (glyph_is_invisible(levl[x][y].glyph)) {
1214 /* found invisible monster in a square
1215 * which already has an 'I' in it.
1216 * Logically, this should still take
1217 * time and lead to a return(1), but if
1218 * we did that the player would keep
1219 * finding the same monster every turn.
1223 You_feel("an unseen monster!");
1224 map_invisible(x, y);
1226 } else if (!sensemon(mtmp))
1227 You("find %s.", a_monnam(mtmp));
1230 if(!canspotmon(mtmp)) {
1231 if (mtmp->mundetected &&
1232 (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1233 mtmp->mundetected = 0;
1239 /* see if an invisible monster has moved--if Blind,
1240 * feel_location() already did it
1242 if (!aflag && !mtmp && !Blind &&
1243 glyph_is_invisible(levl[x][y].glyph)) {
1248 if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) {
1251 if (trap->ttyp == STATUE_TRAP) {
1252 if (activate_statue_trap(trap, x, y, FALSE))
1253 exercise(A_WIS, TRUE);
1269 return(dosearch0(0));
1272 /* Pre-map the sokoban levels */
1277 register struct trap *ttmp;
1278 register struct obj *obj;
1280 /* Map the background and boulders */
1281 for (x = 1; x < COLNO; x++)
1282 for (y = 0; y < ROWNO; y++) {
1283 levl[x][y].seenv = SVALL;
1284 levl[x][y].waslit = TRUE;
1285 map_background(x, y, 1);
1286 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
1287 if (obj->otyp == BOULDER)
1292 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {