OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / src / detect.c
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. */
4
5 /*
6  * Detection routines, including crystal ball, magic mapping, and search
7  * command.
8  */
9
10 #include "hack.h"
11 #include "artifact.h"
12
13 extern boolean known;   /* from read.c */
14
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));
22
23 /* Recursively search obj for an object in class oclass and return 1st found */
24 struct obj *
25 o_in(obj, oclass)
26 struct obj* obj;
27 char oclass;
28 {
29     register struct obj* otmp;
30     struct obj *temp;
31
32     if (obj->oclass == oclass) return obj;
33
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)))
38                 return temp;
39     }
40     return (struct obj *) 0;
41 }
42
43 /* Recursively search obj for an object made of specified material and return 1st found */
44 struct obj *
45 o_material(obj, material)
46 struct obj* obj;
47 unsigned material;
48 {
49     register struct obj* otmp;
50     struct obj *temp;
51
52     if (objects[obj->otyp].oc_material == material) return obj;
53
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)))
58                 return temp;
59     }
60     return (struct obj *) 0;
61 }
62
63 STATIC_OVL void
64 do_dknown_of(obj)
65 struct obj *obj;
66 {
67     struct obj *otmp;
68
69     obj->dknown = 1;
70     if (Has_contents(obj)) {
71         for(otmp = obj->cobj; otmp; otmp = otmp->nobj)
72             do_dknown_of(otmp);
73     }
74 }
75
76 /* Check whether the location has an outdated object displayed on it. */
77 STATIC_OVL boolean
78 check_map_spot(x, y, oclass, material)
79 int x, y;
80 register char oclass;
81 unsigned material;
82 {
83         register int glyph;
84         register struct obj *otmp;
85         register struct monst *mtmp;
86
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 &&
93                                 (
94 #ifndef GOLDOBJ
95                                  mtmp->mgold ||
96 #endif
97                                                  mtmp->minvent)))));
98             } else {
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;
107                         }
108                         /* detection indicates removal of this object from the map */
109                         return TRUE;
110                 }
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 */
116 #ifndef GOLDOBJ
117                         if ((mtmp = m_at(x,y)) != 0) {
118                                 if (oclass == COIN_CLASS && mtmp->mgold)
119                                         return FALSE;
120                                 else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
121                                         if (o_in(otmp, oclass)) return FALSE;
122                         }
123 #else
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;
127                         }
128 #endif
129                         /* detection indicates removal of this object from the map */
130                         return TRUE;
131                 }
132             }
133         }
134         return FALSE;
135 }
136
137 /*
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
141    change occurs.
142  */
143 STATIC_OVL boolean
144 clear_stale_map(oclass, material)
145 register char oclass;
146 unsigned material;
147 {
148         register int zx, zy;
149         register boolean change_made = FALSE;
150
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);
155                     change_made = TRUE;
156                 }
157
158         return change_made;
159 }
160
161 /* look for gold, on the floor or in monsters' possession */
162 int
163 gold_detect(sobj)
164 register struct obj *sobj;
165 {
166     register struct obj *obj;
167     register struct monst *mtmp;
168     int uw = u.uinwater;
169     struct obj *temp;
170     boolean stale;
171
172     known = stale = clear_stale_map(COIN_CLASS,
173                                 (unsigned)(sobj->blessed ? GOLD : 0));
174
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... */
178 #ifndef GOLDOBJ
179         if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
180 #else
181         if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
182 #endif
183             known = TRUE;
184             goto outgoldmap;    /* skip further searching */
185         } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
186             if (sobj->blessed && o_material(obj, GOLD)) {
187                 known = TRUE;
188                 goto outgoldmap;
189             } else if (o_in(obj, COIN_CLASS)) {
190                 known = TRUE;
191                 goto outgoldmap;        /* skip further searching */
192             }
193     }
194     
195     /* look for gold objects */
196     for (obj = fobj; obj; obj = obj->nobj) {
197         if (sobj->blessed && o_material(obj, GOLD)) {
198             known = TRUE;
199             if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
200         } else if (o_in(obj, COIN_CLASS)) {
201             known = TRUE;
202             if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
203         }
204     }
205
206     if (!known) {
207         /* no gold found on floor or monster's inventory.
208            adjust message if you have gold in your inventory */
209         if (sobj) {
210                 char buf[BUFSZ];
211                 if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
212                         Sprintf(buf, "You feel like a million %s!",
213                                 currency(2L));
214                 } else if (hidden_gold() ||
215 #ifndef GOLDOBJ
216                                 u.ugold)
217 #else
218                                 money_cnt(invent))
219 #endif
220                         Strcpy(buf,
221                                 "You feel worried about your future financial situation.");
222                 else
223                         Strcpy(buf, "You feel materially poor.");
224                 strange_feeling(sobj, buf);
225         }
226         return(1);
227     }
228     /* only under me - no separate display required */
229     if (stale) docrt();
230     You("notice some gold between your %s.", makeplural(body_part(FOOT)));
231     return(0);
232
233 outgoldmap:
234     cls();
235
236     u.uinwater = 0;
237     /* Discover gold locations. */
238     for (obj = fobj; obj; obj = obj->nobj) {
239         if (sobj->blessed && (temp = o_material(obj, GOLD))) {
240             if (temp != obj) {
241                 temp->ox = obj->ox;
242                 temp->oy = obj->oy;
243             }
244             map_object(temp,1);
245         } else if ((temp = o_in(obj, COIN_CLASS))) {
246             if (temp != obj) {
247                 temp->ox = obj->ox;
248                 temp->oy = obj->oy;
249             }
250             map_object(temp,1);
251         }
252     }
253     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
254         if (DEADMONSTER(mtmp)) continue;        /* probably overkill here */
255 #ifndef GOLDOBJ
256         if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
257 #else
258         if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
259 #endif
260             struct obj gold;
261
262             gold.otyp = GOLD_PIECE;
263             gold.ox = mtmp->mx;
264             gold.oy = mtmp->my;
265             map_object(&gold,1);
266         } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
267             if (sobj->blessed && (temp = o_material(obj, GOLD))) {
268                 temp->ox = mtmp->mx;
269                 temp->oy = mtmp->my;
270                 map_object(temp,1);
271                 break;
272             } else if ((temp = o_in(obj, COIN_CLASS))) {
273                 temp->ox = mtmp->mx;
274                 temp->oy = mtmp->my;
275                 map_object(temp,1);
276                 break;
277             }
278     }
279     
280     newsym(u.ux,u.uy);
281     You_feel("very greedy, and sense gold!");
282     exercise(A_WIS, TRUE);
283     display_nhwindow(WIN_MAP, TRUE);
284     docrt();
285     u.uinwater = uw;
286     if (Underwater) under_water(2);
287     if (u.uburied) under_ground(2);
288     return(0);
289 }
290
291 /* returns 1 if nothing was detected            */
292 /* returns 0 if something was detected          */
293 int
294 food_detect(sobj)
295 register struct obj     *sobj;
296 {
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";
303     int uw = u.uinwater;
304
305     stale = clear_stale_map(oclass, 0);
306
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++;
310             else ct++;
311         }
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)) {
316                 ct++;
317                 break;
318             }
319     }
320     
321     if (!ct && !ctu) {
322         known = stale && !confused;
323         if (stale) {
324             docrt();
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));
328                 u.uedibility = 1;
329             }
330         } else if (sobj) {
331             char buf[BUFSZ];
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;
339                 u.uedibility = 1;
340             } else
341                 strange_feeling(sobj, buf);
342         }
343         return !stale;
344     } else if (!ct) {
345         known = TRUE;
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));
349                 u.uedibility = 1;
350         }
351     } else {
352         struct obj *temp;
353         known = TRUE;
354         cls();
355         u.uinwater = 0;
356         for (obj = fobj; obj; obj = obj->nobj)
357             if ((temp = o_in(obj, oclass)) != 0) {
358                 if (temp != obj) {
359                     temp->ox = obj->ox;
360                     temp->oy = obj->oy;
361                 }
362                 map_object(temp,1);
363             }
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) {
368                     temp->ox = mtmp->mx;
369                     temp->oy = mtmp->my;
370                     map_object(temp,1);
371                     break;      /* skip rest of this monster's inventory */
372                 }
373         newsym(u.ux,u.uy);
374         if (sobj) {
375             if (sobj->blessed) {
376                 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
377                         u.uedibility ? "continues" : "starts", what);
378                 u.uedibility = 1;
379             } else
380                 Your("%s tingles and you smell %s.", body_part(NOSE), what);
381         }
382         else You("sense %s.", what);
383         display_nhwindow(WIN_MAP, TRUE);
384         exercise(A_WIS, TRUE);
385         docrt();
386         u.uinwater = uw;
387         if (Underwater) under_water(2);
388         if (u.uburied) under_ground(2);
389     }
390     return(0);
391 }
392
393 /*
394  * Used for scrolls, potions, spells, and crystal balls.  Returns:
395  *
396  *      1 - nothing was detected
397  *      0 - something was detected
398  */
399 int
400 object_detect(detector, class)
401 struct obj      *detector;      /* object doing the detecting */
402 int             class;          /* an object class, 0 for all */
403 {
404     register int x, y;
405     char stuff[BUFSZ];
406     int is_cursed = (detector && detector->cursed);
407     int do_dknown = (detector && (detector->oclass == POTION_CLASS ||
408                                     detector->oclass == SPBOOK_CLASS) &&
409                         detector->blessed);
410     int ct = 0, ctu = 0;
411     register struct obj *obj, *otmp = (struct obj *)0;
412     register struct monst *mtmp;
413     int uw = u.uinwater;
414     int sym, boulder = 0;
415
416     if (class < 0 || class >= MAXOCLASSES) {
417         impossible("object_detect:  illegal class %d", class);
418         class = 0;
419     }
420
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.
426      */
427     sym = class ? def_oc_syms[class] : 0;
428     if (sym && iflags.bouldersym && sym == iflags.bouldersym)
429         boulder = ROCK_CLASS;
430
431     if (Hallucination || (Confusion && class == SCROLL_CLASS))
432         Strcpy(stuff, something);
433     else
434         Strcpy(stuff, class ? oclass_names[class] : "objects");
435     if (boulder && class != ROCK_CLASS) Strcat(stuff, " and/or large stones");
436
437     if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj);
438
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++;
442             else ct++;
443         }
444         if (do_dknown) do_dknown_of(obj);
445     }
446
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++;
450             else ct++;
451         }
452         if (do_dknown) do_dknown_of(obj);
453     }
454
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);
460         }
461         if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
462             (!class || class == objects[mtmp->mappearance].oc_class)) ||
463 #ifndef GOLDOBJ
464             (mtmp->mgold && (!class || class == COIN_CLASS))) {
465 #else
466             (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
467 #endif
468             ct++;
469             break;
470         }
471     }
472
473     if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
474         if (!ctu) {
475             if (detector)
476                 strange_feeling(detector, "You feel a lack of something.");
477             return 1;
478         }
479
480         You("sense %s nearby.", stuff);
481         return 0;
482     }
483
484     cls();
485
486     u.uinwater = 0;
487 /*
488  *      Map all buried objects first.
489  */
490     for (obj = level.buriedobjlist; obj; obj = obj->nobj)
491         if (!class || (otmp = o_in(obj, class))) {
492             if (class) {
493                 if (otmp != obj) {
494                     otmp->ox = obj->ox;
495                     otmp->oy = obj->oy;
496                 }
497                 map_object(otmp, 1);
498             } else
499                 map_object(obj, 1);
500         }
501     /*
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
505      * at each location.
506      *
507      * Objects on the floor override buried objects.
508      */
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) {
515                         if (otmp != obj) {
516                             otmp->ox = obj->ox;
517                             otmp->oy = obj->oy;
518                         }
519                         map_object(otmp, 1);
520                     } else
521                         map_object(obj, 1);
522                     break;
523                 }
524
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 */
533                 otmp->oy = mtmp->my;
534                 map_object(otmp, 1);
535                 break;
536             }
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)) {
540             struct obj temp;
541
542             temp.otyp = mtmp->mappearance;      /* needed for obj_to_glyph() */
543             temp.ox = mtmp->mx;
544             temp.oy = mtmp->my;
545             temp.corpsenm = PM_TENGU;           /* if mimicing a corpse */
546             map_object(&temp, 1);
547 #ifndef GOLDOBJ
548         } else if (mtmp->mgold && (!class || class == COIN_CLASS)) {
549 #else
550         } else if (findgold(mtmp->minvent) && (!class || class == COIN_CLASS)) {
551 #endif
552             struct obj gold;
553
554             gold.otyp = GOLD_PIECE;
555             gold.ox = mtmp->mx;
556             gold.oy = mtmp->my;
557             map_object(&gold, 1);
558         }
559     }
560
561     newsym(u.ux,u.uy);
562     You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
563     display_nhwindow(WIN_MAP, TRUE);
564     /*
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?
567      */
568     docrt();    /* this will correctly reset vision */
569
570     u.uinwater = uw;
571     if (Underwater) under_water(2);
572     if (u.uburied) under_ground(2);
573     return 0;
574 }
575
576 /*
577  * Used by: crystal balls, potions, fountains
578  *
579  * Returns 1 if nothing was detected.
580  * Returns 0 if something was detected.
581  */
582 int
583 monster_detect(otmp, mclass)
584 register struct obj *otmp;      /* detecting object (if any) */
585 int mclass;                     /* monster class, 0 for all */
586 {
587     register struct monst *mtmp;
588     int mcnt = 0;
589
590
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.
595      */
596     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
597         if (!DEADMONSTER(mtmp)) {
598                 mcnt++;
599                 break;
600         }
601
602     if (!mcnt) {
603         if (otmp)
604             strange_feeling(otmp, Hallucination ?
605                             "You get the heebie jeebies." :
606                             "You feel threatened.");
607         return 1;
608     } else {
609         boolean woken = FALSE;
610
611         cls();
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))
616                     if (mtmp->mx > 0) {
617                         if (mclass && def_monsyms[mclass] == ' ')
618                                 show_glyph(mtmp->mx,mtmp->my,
619                                         detected_mon_to_glyph(mtmp));
620                         else
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);
624                     }
625             if (otmp && otmp->cursed &&
626                 (mtmp->msleeping || !mtmp->mcanmove)) {
627                 mtmp->msleeping = mtmp->mfrozen = 0;
628                 mtmp->mcanmove = 1;
629                 woken = TRUE;
630             }
631         }
632         display_self();
633         You("sense the presence of monsters.");
634         if (woken)
635             pline("Monsters sense the presence of you.");
636         display_nhwindow(WIN_MAP, TRUE);
637         docrt();
638         if (Underwater) under_water(2);
639         if (u.uburied) under_ground(2);
640     }
641     return 0;
642 }
643
644 STATIC_OVL void
645 sense_trap(trap, x, y, src_cursed)
646 struct trap *trap;
647 xchar x, y;
648 int src_cursed;
649 {
650     if (Hallucination || src_cursed) {
651         struct obj obj;                 /* fake object */
652         if (trap) {
653             obj.ox = trap->tx;
654             obj.oy = trap->ty;
655         } else {
656             obj.ox = x;
657             obj.oy = y;
658         }
659         obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
660         obj.corpsenm = random_monster();        /* if otyp == CORPSE */
661         map_object(&obj,1);
662     } else if (trap) {
663         map_trap(trap,1);
664         trap->tseen = 1;
665     } else {
666         struct trap temp_trap;          /* fake trap */
667         temp_trap.tx = x;
668         temp_trap.ty = y;
669         temp_trap.ttyp = BEAR_TRAP;     /* some kind of trap */
670         map_trap(&temp_trap,1);
671     }
672
673 }
674
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          */
679 int
680 trap_detect(sobj)
681 register struct obj *sobj;
682 /* sobj is null if crystal ball, *scroll if gold detection scroll */
683 {
684     register struct trap *ttmp;
685     register struct obj *obj;
686     register int door;
687     int uw = u.uinwater;
688     boolean found = FALSE;
689     coord cc;
690
691     for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
692         if (ttmp->tx != u.ux || ttmp->ty != u.uy)
693             goto outtrapmap;
694         else found = TRUE;
695     }
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)
699                 goto outtrapmap;
700             else found = TRUE;
701         }
702     }
703     for (door = 0; door < doorindex; door++) {
704         cc = doors[door];
705         if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
706             if (cc.x != u.ux || cc.y != u.uy)
707                 goto outtrapmap;
708             else found = TRUE;
709         }
710     }
711     if (!found) {
712         char buf[42];
713         Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
714         strange_feeling(sobj,buf);
715         return(1);
716     }
717     /* traps exist, but only under me - no separate display required */
718     Your("%s itch.", makeplural(body_part(TOE)));
719     return(0);
720 outtrapmap:
721     cls();
722
723     u.uinwater = 0;
724     for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
725         sense_trap(ttmp, 0, 0, sobj && sobj->cursed);
726
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);
730
731     for (door = 0; door < doorindex; door++) {
732         cc = doors[door];
733         if (levl[cc.x][cc.y].doormask & D_TRAPPED)
734         sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed);
735     }
736
737     newsym(u.ux,u.uy);
738     You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped");
739     display_nhwindow(WIN_MAP, TRUE);
740     docrt();
741     u.uinwater = uw;
742     if (Underwater) under_water(2);
743     if (u.uburied) under_ground(2);
744     return(0);
745 }
746
747 const char *
748 level_distance(where)
749 d_level *where;
750 {
751     register schar ll = depth(&u.uz) - depth(where);
752     register boolean indun = (u.uz.dnum == where->dnum);
753
754     if (ll < 0) {
755         if (ll < (-8 - rn2(3)))
756             if (!indun) return "far away";
757             else        return "far below";
758         else if (ll < -1)
759             if (!indun) return "away below you";
760             else        return "below you";
761         else
762             if (!indun) return "in the distance";
763             else        return "just below";
764     } else if (ll > 0) {
765         if (ll > (8 + rn2(3)))
766             if (!indun) return "far away";
767             else        return "far above";
768         else if (ll > 1)
769             if (!indun) return "away above you";
770             else        return "above you";
771         else
772             if (!indun) return "in the distance";
773             else        return "just above";
774     } else
775             if (!indun) return "in the distance";
776             else        return "near you";
777 }
778
779 static const struct {
780     const char *what;
781     d_level *where;
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 },
787 };
788
789 void
790 use_crystal_ball(obj)
791 struct obj *obj;
792 {
793     char ch;
794     int oops;
795
796     if (Blind) {
797         pline("Too bad you can't see %s.", the(xname(obj)));
798         return;
799     }
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"));
804             break;
805         case 2 : pline("%s you!", Tobjnam(obj, "confuse"));
806             make_confused(HConfusion + rnd(100),FALSE);
807             break;
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);
812             } else {
813                 pline("%s your vision.", Tobjnam(obj, "assault"));
814                 You("are unaffected!");
815             }
816             break;
817         case 4 : pline("%s your mind!", Tobjnam(obj, "zap"));
818             (void) make_hallucinated(HHallucination + rnd(100),FALSE,0L);
819             break;
820         case 5 : pline("%s!", Tobjnam(obj, "explode"));
821             useup(obj);
822             obj = 0;    /* it's gone */
823             losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN);
824             break;
825         }
826         if (obj) consume_obj_charge(obj, TRUE);
827         return;
828     }
829
830     if (Hallucination) {
831         if (!obj->spe) {
832             pline("All you see is funky %s haze.", hcolor((char *)0));
833         } else {
834             switch(rnd(6)) {
835             case 1 : You("grok some groovy globs of incandescent lava.");
836                 break;
837             case 2 : pline("Whoa!  Psychedelic colors, %s!",
838                            poly_gender() == 1 ? "babe" : "dude");
839                 break;
840             case 3 : pline_The("crystal pulses with sinister %s light!",
841                                 hcolor((char *)0));
842                 break;
843             case 4 : You("see goldfish swimming above fluorescent rocks.");
844                 break;
845             case 5 : You("see tiny snowflakes spinning around a miniature farmhouse.");
846                 break;
847             default: pline("Oh wow... like a kaleidoscope!");
848                 break;
849             }
850             consume_obj_charge(obj, TRUE);
851         }
852         return;
853     }
854
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);
861         return;
862     }
863     You("peer into %s...", the(xname(obj)));
864     nomul(-rnd(10));
865     nomovemsg = "";
866     if (obj->spe <= 0)
867         pline_The("vision is unclear.");
868     else {
869         int class;
870         int ret = 0;
871
872         makeknown(CRYSTAL_BALL);
873         consume_obj_charge(obj, TRUE);
874
875         /* special case: accept ']' as synonym for mimic
876          * we have to do this before the def_char_to_objclass check
877          */
878         if (ch == DEF_MIMIC_DEF) ch = DEF_MIMIC;
879
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);
886         else switch(ch) {
887                 case '^':
888                     ret = trap_detect((struct obj *)0);
889                     break;
890                 default:
891                     {
892                     int i = rn2(SIZE(level_detects));
893                     You("see %s, %s.",
894                         level_detects[i].what,
895                         level_distance(level_detects[i].where));
896                     }
897                     ret = 0;
898                     break;
899         }
900
901         if (ret) {
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.");
905         }
906     }
907     return;
908 }
909
910 STATIC_OVL void
911 show_map_spot(x, y)
912 register int x, y;
913 {
914     register struct rm *lev;
915
916     if (Confusion && rn2(7)) return;
917     lev = &levl[x][y];
918
919     lev->seenv = SVALL;
920
921     /* Secret corridors are found, but not secret doors. */
922     if (lev->typ == SCORR) {
923         lev->typ = CORR;
924         unblock_point(x,y);
925     }
926
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 */
935         } else {
936             magic_map_background(x,y,1);        /* display it */
937         }
938     }
939 }
940
941 void
942 do_mapping()
943 {
944     register int zx, zy;
945     int uw = u.uinwater;
946
947     u.uinwater = 0;
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);
952     u.uinwater = uw;
953     if (!level.flags.hero_memory || Underwater) {
954         flush_screen(1);                        /* flush temp screen */
955         display_nhwindow(WIN_MAP, TRUE);        /* wait */
956         docrt();
957     }
958 }
959
960 void
961 do_vicinity_map()
962 {
963     register int zx, zy;
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);
968
969     for (zx = lo_x; zx < hi_x; zx++)
970         for (zy = lo_y; zy < hi_y; zy++)
971             show_map_spot(zx, zy);
972
973     if (!level.flags.hero_memory || Underwater) {
974         flush_screen(1);                        /* flush temp screen */
975         display_nhwindow(WIN_MAP, TRUE);        /* wait */
976         docrt();
977     }
978 }
979
980 /* convert a secret door into a normal door */
981 void
982 cvt_sdoor_to_door(lev)
983 struct rm *lev;
984 {
985         int newmask = lev->doormask & ~WM_MASK;
986
987 #ifdef REINCARNATION
988         if (Is_rogue_level(&u.uz))
989             /* rogue didn't have doors, only doorways */
990             newmask = D_NODOOR;
991         else
992 #endif
993             /* newly exposed door is closed */
994             if (!(newmask & D_LOCKED)) newmask |= D_CLOSED;
995
996         lev->typ = DOOR;
997         lev->doormask = newmask;
998 }
999
1000
1001 STATIC_PTR void
1002 findone(zx,zy,num)
1003 int zx,zy;
1004 genericptr_t num;
1005 {
1006         register struct trap *ttmp;
1007         register struct monst *mtmp;
1008
1009         if(levl[zx][zy].typ == SDOOR) {
1010                 cvt_sdoor_to_door(&levl[zx][zy]);       /* .typ = DOOR */
1011                 magic_map_background(zx, zy, 0);
1012                 newsym(zx, zy);
1013                 (*(int*)num)++;
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);
1018                 newsym(zx, zy);
1019                 (*(int*)num)++;
1020         } else if ((ttmp = t_at(zx, zy)) != 0) {
1021                 if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1022                         ttmp->tseen = 1;
1023                         newsym(zx,zy);
1024                         (*(int*)num)++;
1025                 }
1026         } else if ((mtmp = m_at(zx, zy)) != 0) {
1027                 if(mtmp->m_ap_type) {
1028                         seemimic(mtmp);
1029                         (*(int*)num)++;
1030                 }
1031                 if (mtmp->mundetected &&
1032                     (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1033                         mtmp->mundetected = 0;
1034                         newsym(zx, zy);
1035                         (*(int*)num)++;
1036                 }
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);
1042                 newsym(zx, zy);
1043                 (*(int*)num)++;
1044         }
1045 }
1046
1047 STATIC_PTR void
1048 openone(zx,zy,num)
1049 int zx,zy;
1050 genericptr_t num;
1051 {
1052         register struct trap *ttmp;
1053         register struct obj *otmp;
1054
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) {
1059                         otmp->olocked = 0;
1060                         (*(int*)num)++;
1061                     }
1062                 }
1063                 /* let it fall to the next cases. could be on trap. */
1064         }
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;
1077                 } else
1078                     levl[zx][zy].doormask = D_ISOPEN;
1079                 unblock_point(zx, zy);
1080                 newsym(zx, zy);
1081                 (*(int*)num)++;
1082         } else if(levl[zx][zy].typ == SCORR) {
1083                 levl[zx][zy].typ = CORR;
1084                 unblock_point(zx, zy);
1085                 newsym(zx, zy);
1086                 (*(int*)num)++;
1087         } else if ((ttmp = t_at(zx, zy)) != 0) {
1088                 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1089                     ttmp->tseen = 1;
1090                     newsym(zx,zy);
1091                     (*(int*)num)++;
1092                 }
1093         } else if (find_drawbridge(&zx, &zy)) {
1094                 /* make sure it isn't an open drawbridge */
1095                 open_drawbridge(zx, zy);
1096                 (*(int*)num)++;
1097         }
1098 }
1099
1100 int
1101 findit()        /* returns number of things found */
1102 {
1103         int num = 0;
1104
1105         if(u.uswallow) return(0);
1106         do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1107         return(num);
1108 }
1109
1110 int
1111 openit()        /* returns number of things found and opened */
1112 {
1113         int num = 0;
1114
1115         if(u.uswallow) {
1116                 if (is_animal(u.ustuck->data)) {
1117                         if (Blind) pline("Its mouth opens!");
1118                         else pline("%s opens its mouth!", Monnam(u.ustuck));
1119                 }
1120                 expels(u.ustuck, u.ustuck->data, TRUE);
1121                 return(-1);
1122         }
1123
1124         do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1125         return(num);
1126 }
1127
1128 void
1129 find_trap(trap)
1130 struct trap *trap;
1131 {
1132     int tt = what_trap(trap->ttyp);
1133     boolean cleared = FALSE;
1134
1135     trap->tseen = 1;
1136     exercise(A_WIS, TRUE);
1137     if (Blind)
1138         feel_location(trap->tx, trap->ty);
1139     else
1140         newsym(trap->tx, trap->ty);
1141
1142     if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1143         /* There's too much clutter to see your find otherwise */
1144         cls();
1145         map_trap(trap, 1);
1146         display_self();
1147         cleared = TRUE;
1148     }
1149
1150     You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1151
1152     if (cleared) {
1153         display_nhwindow(WIN_MAP, TRUE);        /* wait */
1154         docrt();
1155     }
1156 }
1157
1158 int
1159 dosearch0(aflag)
1160 register int aflag;
1161 {
1162 #ifdef GCC_BUG
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
1166    makefile).
1167  */
1168         volatile xchar x, y;
1169 #else
1170         register xchar x, y;
1171 #endif
1172         register struct trap *trap;
1173         register struct monst *mtmp;
1174
1175         if(u.uswallow) {
1176                 if (!aflag)
1177                         pline("What are you looking for?  The exit?");
1178         } else {
1179             int fund = (uwep && uwep->oartifact &&
1180                     spec_ability(uwep, SPFX_SEARCH)) ?
1181                     uwep->spe : 0;
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);
1194                         nomul(0);
1195                         if (Blind && !aflag)
1196                             feel_location(x,y); /* make sure it shows up */
1197                         else
1198                             newsym(x,y);
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);
1204                         nomul(0);
1205                         newsym(x,y);
1206                     } else {
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) {
1210                                 seemimic(mtmp);
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.
1220                                          */
1221                                         continue;
1222                                     } else {
1223                                         You_feel("an unseen monster!");
1224                                         map_invisible(x, y);
1225                                     }
1226                                 } else if (!sensemon(mtmp))
1227                                     You("find %s.", a_monnam(mtmp));
1228                                 return(1);
1229                             }
1230                             if(!canspotmon(mtmp)) {
1231                                 if (mtmp->mundetected &&
1232                                    (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1233                                         mtmp->mundetected = 0;
1234                                 newsym(x,y);
1235                                 goto find;
1236                             }
1237                         }
1238
1239                         /* see if an invisible monster has moved--if Blind,
1240                          * feel_location() already did it
1241                          */
1242                         if (!aflag && !mtmp && !Blind &&
1243                                     glyph_is_invisible(levl[x][y].glyph)) {
1244                             unmap_object(x,y);
1245                             newsym(x,y);
1246                         }
1247
1248                         if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) {
1249                             nomul(0);
1250
1251                             if (trap->ttyp == STATUE_TRAP) {
1252                                 if (activate_statue_trap(trap, x, y, FALSE))
1253                                     exercise(A_WIS, TRUE);
1254                                 return(1);
1255                             } else {
1256                                 find_trap(trap);
1257                             }
1258                         }
1259                     }
1260                 }
1261             }
1262         }
1263         return(1);
1264 }
1265
1266 int
1267 dosearch()
1268 {
1269         return(dosearch0(0));
1270 }
1271
1272 /* Pre-map the sokoban levels */
1273 void
1274 sokoban_detect()
1275 {
1276         register int x, y;
1277         register struct trap *ttmp;
1278         register struct obj *obj;
1279
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)
1288                         map_object(obj, 1);
1289             }
1290
1291         /* Map the traps */
1292         for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1293             ttmp->tseen = 1;
1294             map_trap(ttmp, 1);
1295         }
1296 }
1297
1298
1299 /*detect.c*/