1 | /* SCCS Id: @(#)detect.c 3.3 1999/12/06 */
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));
17 | STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P));
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 | STATIC_OVL void
44 | do_dknown_of(obj)
45 | struct obj *obj;
46 | {
47 | struct obj *otmp;
48 |
49 | obj->dknown = 1;
50 | if (Has_contents(obj)) {
51 | for(otmp = obj->cobj; otmp; otmp = otmp->nobj)
52 | do_dknown_of(otmp);
53 | }
54 | }
55 |
56 | /* Check whether the location has an outdated object displayed on it. */
57 | STATIC_OVL boolean
58 | check_map_spot(x, y, oclass)
59 | int x, y;
60 | register char oclass;
61 | {
62 | register int glyph;
63 | register struct obj *otmp;
64 | register struct monst *mtmp;
65 |
66 | glyph = glyph_at(x,y);
67 | if (glyph_is_object(glyph)) {
68 | /* there's some object shown here */
69 | if (oclass == ALL_CLASSES) {
70 | return((boolean)( !(level.objects[x][y] || /* stale if nothing here */
71 | ((mtmp = m_at(x,y)) != 0 &&
72 | (mtmp->mgold || mtmp->minvent)))));
73 | } else if (objects[glyph_to_obj(glyph)].oc_class == oclass) {
74 | /* the object shown here is of interest */
75 | for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
76 | if (o_in(otmp, oclass)) return FALSE;
77 | /* didn't find it; perhaps a monster is carrying it */
78 | if ((mtmp = m_at(x,y)) != 0) {
79 | if (oclass == GOLD_CLASS && mtmp->mgold)
80 | return FALSE;
81 | else for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
82 | if (o_in(otmp, oclass)) return FALSE;
83 | }
84 | /* detection indicates removal of this object from the map */
85 | return TRUE;
86 | }
87 | }
88 | return FALSE;
89 | }
90 |
91 | /*
92 | When doing detection, remove stale data from the map display (corpses
93 | rotted away, objects carried away by monsters, etc) so that it won't
94 | reappear after the detection has completed. Return true if noticeable
95 | change occurs.
96 | */
97 | STATIC_OVL boolean
98 | clear_stale_map(oclass)
99 | register char oclass;
100 | {
101 | register int zx, zy;
102 | register boolean change_made = FALSE;
103 |
104 | for (zx = 1; zx < COLNO; zx++)
105 | for (zy = 0; zy < ROWNO; zy++)
106 | if (check_map_spot(zx, zy, oclass)) {
107 | unmap_object(zx, zy);
108 | change_made = TRUE;
109 | }
110 |
111 | return change_made;
112 | }
113 |
114 | /* look for gold, on the floor or in monsters' possession */
115 | int
116 | gold_detect(sobj)
117 | register struct obj *sobj;
118 | {
119 | register struct obj *obj;
120 | register struct monst *mtmp;
121 | int uw = u.uinwater;
122 | struct obj *temp;
123 | boolean stale;
124 |
125 | known = stale = clear_stale_map(GOLD_CLASS);
126 |
127 | /* look for gold carried by monsters (might be in a container) */
128 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
129 | if (DEADMONSTER(mtmp)) continue; /* probably not needed in this case but... */
130 | if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
131 | known = TRUE;
132 | goto outgoldmap; /* skip further searching */
133 | } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
134 | if (o_in(obj, GOLD_CLASS)) {
135 | known = TRUE;
136 | goto outgoldmap; /* skip further searching */
137 | }
138 | }
139 |
140 | /* look for gold objects */
141 | for (obj = fobj; obj; obj = obj->nobj)
142 | if (o_in(obj, GOLD_CLASS)) {
143 | known = TRUE;
144 | if (obj->ox != u.ux || obj->oy != u.uy) goto outgoldmap;
145 | }
146 |
147 | if (!known) {
148 | /* no gold found */
149 | if (sobj) strange_feeling(sobj, "You feel materially poor.");
150 | return(1);
151 | }
152 | /* only under me - no separate display required */
153 | if (stale) docrt();
154 | You("notice some gold between your %s.", makeplural(body_part(FOOT)));
155 | return(0);
156 |
157 | outgoldmap:
158 | cls();
159 |
160 | u.uinwater = 0;
161 | /* Discover gold locations. */
162 | for (obj = fobj; obj; obj = obj->nobj)
163 | if ((temp = o_in(obj, GOLD_CLASS))) {
164 | if (temp != obj) {
165 | temp->ox = obj->ox;
166 | temp->oy = obj->oy;
167 | }
168 | map_object(temp,1);
169 | }
170 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
171 | if (DEADMONSTER(mtmp)) continue; /* probably overkill here */
172 | if (mtmp->mgold || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
173 | struct obj gold;
174 |
175 | gold.otyp = GOLD_PIECE;
176 | gold.ox = mtmp->mx;
177 | gold.oy = mtmp->my;
178 | map_object(&gold,1);
179 | } else for (obj = mtmp->minvent; obj; obj = obj->nobj)
180 | if ((temp = o_in(obj, GOLD_CLASS))) {
181 | temp->ox = mtmp->mx;
182 | temp->oy = mtmp->my;
183 | map_object(temp,1);
184 | break;
185 | }
186 | }
187 |
188 | newsym(u.ux,u.uy);
189 | You_feel("very greedy, and sense gold!");
190 | exercise(A_WIS, TRUE);
191 | display_nhwindow(WIN_MAP, TRUE);
192 | docrt();
193 | u.uinwater = uw;
194 | if (Underwater) under_water(2);
195 | if (u.uburied) under_ground(2);
196 | return(0);
197 | }
198 |
199 | /* returns 1 if nothing was detected */
200 | /* returns 0 if something was detected */
201 | int
202 | food_detect(sobj)
203 | register struct obj *sobj;
204 | {
205 | register struct obj *obj;
206 | register struct monst *mtmp;
207 | register int ct = 0, ctu = 0;
208 | boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
209 | char oclass = confused ? POTION_CLASS : FOOD_CLASS;
210 | const char *what = confused ? something : "food";
211 | int uw = u.uinwater;
212 |
213 | stale = clear_stale_map(oclass);
214 |
215 | for (obj = fobj; obj; obj = obj->nobj)
216 | if (o_in(obj, oclass)) {
217 | if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
218 | else ct++;
219 | }
220 | for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
221 | /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */
222 | for (obj = mtmp->minvent; obj; obj = obj->nobj)
223 | if (o_in(obj, oclass)) {
224 | ct++;
225 | break;
226 | }
227 | }
228 |
229 | if (!ct && !ctu) {
230 | known = stale && !confused;
231 | if (stale) {
232 | docrt();
233 | You("sense a lack of %s nearby.", what);
234 | } else if (sobj)
235 | strange_feeling(sobj, "Your nose twitches.");
236 | return !stale;
237 | } else if (!ct) {
238 | known = TRUE;
239 | You("%s %s nearby.", sobj ? "smell" : "sense", what);
240 | } else {
241 | struct obj *temp;
242 | known = TRUE;
243 | cls();
244 | u.uinwater = 0;
245 | for (obj = fobj; obj; obj = obj->nobj)
246 | if ((temp = o_in(obj, oclass)) != 0) {
247 | if (temp != obj) {
248 | temp->ox = obj->ox;
249 | temp->oy = obj->oy;
250 | }
251 | map_object(temp,1);
252 | }
253 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
254 | /* no DEADMONSTER(mtmp) check needed since dmons never have inventory */
255 | for (obj = mtmp->minvent; obj; obj = obj->nobj)
256 | if ((temp = o_in(obj, oclass)) != 0) {
257 | temp->ox = mtmp->mx;
258 | temp->oy = mtmp->my;
259 | map_object(temp,1);
260 | break; /* skip rest of this monster's inventory */
261 | }
262 | newsym(u.ux,u.uy);
263 | if (sobj) Your("nose tingles and you smell %s.", what);
264 | else You("sense %s.", what);
265 | display_nhwindow(WIN_MAP, TRUE);
266 | exercise(A_WIS, TRUE);
267 | docrt();
268 | u.uinwater = uw;
269 | if (Underwater) under_water(2);
270 | if (u.uburied) under_ground(2);
271 | }
272 | return(0);
273 | }
274 |
275 | /*
276 | * Used for scrolls, potions, and crystal balls. Returns:
277 | *
278 | * 1 - nothing was detected
279 | * 0 - something was detected
280 | */
281 | int
282 | object_detect(detector, class)
283 | struct obj *detector; /* object doing the detecting */
284 | int class; /* an object class, 0 for all */
285 | {
286 | register int x, y;
287 | int is_cursed = (detector && detector->cursed);
288 | int do_dknown =
289 | (detector && detector->oclass == POTION_CLASS && detector->blessed);
290 | int ct = 0, ctu = 0;
291 | register struct obj *obj, *otmp = (struct obj *)0;
292 | register struct monst *mtmp;
293 | int uw = u.uinwater;
294 | const char *stuff;
295 |
296 | if (class < 0 || class >= MAXOCLASSES) {
297 | impossible("object_detect: illegal class %d", class);
298 | class = 0;
299 | }
300 |
301 | if (Hallucination || (Confusion && class == SCROLL_CLASS))
302 | stuff = something;
303 | else
304 | stuff = class ? oclass_names[class] : "objects";
305 |
306 | if (do_dknown) for(obj = invent; obj; obj = obj->nobj) do_dknown_of(obj);
307 |
308 | for (obj = fobj; obj; obj = obj->nobj) {
309 | if (!class || o_in(obj, class)) {
310 | if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
311 | else ct++;
312 | }
313 | if (do_dknown) do_dknown_of(obj);
314 | }
315 |
316 | for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
317 | if (!class || o_in(obj, class)) {
318 | if (obj->ox == u.ux && obj->oy == u.uy) ctu++;
319 | else ct++;
320 | }
321 | if (do_dknown) do_dknown_of(obj);
322 | }
323 |
324 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
325 | if (DEADMONSTER(mtmp)) continue;
326 | for (obj = mtmp->minvent; obj; obj = obj->nobj) {
327 | if (!class || o_in(obj, class)) ct++;
328 | if (do_dknown) do_dknown_of(obj);
329 | }
330 | if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
331 | (!class || class == objects[mtmp->mappearance].oc_class)) ||
332 | (mtmp->mgold && (!class || class == GOLD_CLASS))) {
333 | ct++;
334 | break;
335 | }
336 | }
337 |
338 | if (!clear_stale_map(!class ? ALL_CLASSES : class) && !ct) {
339 | if (!ctu) {
340 | if (detector)
341 | strange_feeling(detector, "You feel a lack of something.");
342 | return 1;
343 | }
344 |
345 | You("sense %s nearby.", stuff);
346 | return 0;
347 | }
348 |
349 | cls();
350 |
351 | u.uinwater = 0;
352 | /*
353 | * Map all buried objects first.
354 | */
355 | for (obj = level.buriedobjlist; obj; obj = obj->nobj)
356 | if (!class || (otmp = o_in(obj, class))) {
357 | if (class) {
358 | if (otmp != obj) {
359 | otmp->ox = obj->ox;
360 | otmp->oy = obj->oy;
361 | }
362 | map_object(otmp, 1);
363 | } else
364 | map_object(obj, 1);
365 | }
366 | /*
367 | * If we are mapping all objects, map only the top object of a pile or
368 | * the first object in a monster's inventory. Otherwise, go looking
369 | * for a matching object class and display the first one encountered
370 | * at each location.
371 | *
372 | * Objects on the floor override buried objects.
373 | */
374 | for (x = 1; x < COLNO; x++)
375 | for (y = 0; y < ROWNO; y++)
376 | for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
377 | if (!class || (otmp = o_in(obj, class))) {
378 | if (class) {
379 | if (otmp != obj) {
380 | otmp->ox = obj->ox;
381 | otmp->oy = obj->oy;
382 | }
383 | map_object(otmp, 1);
384 | } else
385 | map_object(obj, 1);
386 | break;
387 | }
388 |
389 | /* Objects in the monster's inventory override floor objects. */
390 | for (mtmp = fmon ; mtmp ; mtmp = mtmp->nmon) {
391 | if (DEADMONSTER(mtmp)) continue;
392 | for (obj = mtmp->minvent; obj; obj = obj->nobj)
393 | if (!class || (otmp = o_in(obj, class))) {
394 | if (!class) otmp = obj;
395 | otmp->ox = mtmp->mx; /* at monster location */
396 | otmp->oy = mtmp->my;
397 | map_object(otmp, 1);
398 | break;
399 | }
400 | /* Allow a mimic to override the detected objects it is carrying. */
401 | if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT &&
402 | (!class || class == objects[mtmp->mappearance].oc_class)) {
403 | struct obj temp;
404 |
405 | temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
406 | temp.ox = mtmp->mx;
407 | temp.oy = mtmp->my;
408 | temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
409 | map_object(&temp, 1);
410 | } else if (mtmp->mgold && (!class || class == GOLD_CLASS)) {
411 | struct obj gold;
412 |
413 | gold.otyp = GOLD_PIECE;
414 | gold.ox = mtmp->mx;
415 | gold.oy = mtmp->my;
416 | map_object(&gold, 1);
417 | }
418 | }
419 |
420 | newsym(u.ux,u.uy);
421 | You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
422 | display_nhwindow(WIN_MAP, TRUE);
423 | /*
424 | * What are we going to do when the hero does an object detect while blind
425 | * and the detected object covers a known pool?
426 | */
427 | docrt(); /* this will correctly reset vision */
428 |
429 | u.uinwater = uw;
430 | if (Underwater) under_water(2);
431 | if (u.uburied) under_ground(2);
432 | return 0;
433 | }
434 |
435 | /*
436 | * Used by: crystal balls, potions, fountains
437 | *
438 | * Returns 1 if nothing was detected.
439 | * Returns 0 if something was detected.
440 | */
441 | int
442 | monster_detect(otmp, mclass)
443 | register struct obj *otmp; /* detecting object (if any) */
444 | int mclass; /* monster class, 0 for all */
445 | {
446 | register struct monst *mtmp;
447 | int mcnt = 0;
448 |
449 |
450 | /* Note: This used to just check fmon for a non-zero value
451 | * but in versions since 3.3.0 fmon can test TRUE due to the
452 | * presence of dmons, so we have to find at least one
453 | * with positive hit-points to know for sure.
454 | */
455 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
456 | if (!DEADMONSTER(mtmp)) {
457 | mcnt++;
458 | break;
459 | }
460 |
461 | if (!mcnt) {
462 | if (otmp)
463 | strange_feeling(otmp, Hallucination ?
464 | "You get the heebie jeebies." :
465 | "You feel threatened.");
466 | return 1;
467 | } else {
468 | boolean woken = FALSE;
469 |
470 | cls();
471 | for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
472 | if (DEADMONSTER(mtmp)) continue;
473 | if (!mclass || mtmp->data->mlet == mclass)
474 | if (mtmp->mx > 0)
475 | show_glyph(mtmp->mx,mtmp->my,mon_to_glyph(mtmp));
476 | if (otmp && otmp->cursed &&
477 | (mtmp->msleeping || !mtmp->mcanmove)) {
478 | mtmp->msleeping = mtmp->mfrozen = 0;
479 | mtmp->mcanmove = 1;
480 | woken = TRUE;
481 | }
482 | }
483 | display_self();
484 | You("sense the presence of monsters.");
485 | if (woken)
486 | pline("Monsters sense the presence of you.");
487 | display_nhwindow(WIN_MAP, TRUE);
488 | docrt();
489 | if (Underwater) under_water(2);
490 | if (u.uburied) under_ground(2);
491 | }
492 | return 0;
493 | }
494 |
495 | STATIC_OVL void
496 | sense_trap(trap, x, y, src_cursed)
497 | struct trap *trap;
498 | xchar x, y;
499 | int src_cursed;
500 | {
501 | if (Hallucination || src_cursed) {
502 | struct obj obj; /* fake object */
503 | if (trap) {
504 | obj.ox = trap->tx;
505 | obj.oy = trap->ty;
506 | } else {
507 | obj.ox = x;
508 | obj.oy = y;
509 | }
510 | obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
511 | obj.corpsenm = random_monster(); /* if otyp == CORPSE */
512 | map_object(&obj,1);
513 | } else if (trap) {
514 | map_trap(trap,1);
515 | trap->tseen = 1;
516 | } else {
517 | struct trap temp_trap; /* fake trap */
518 | temp_trap.tx = x;
519 | temp_trap.ty = y;
520 | temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
521 | map_trap(&temp_trap,1);
522 | }
523 |
524 | }
525 |
526 | /* the detections are pulled out so they can */
527 | /* also be used in the crystal ball routine */
528 | /* returns 1 if nothing was detected */
529 | /* returns 0 if something was detected */
530 | int
531 | trap_detect(sobj)
532 | register struct obj *sobj;
533 | /* sobj is null if crystal ball, *scroll if gold detection scroll */
534 | {
535 | register struct trap *ttmp;
536 | register struct obj *obj;
537 | register int door;
538 | int uw = u.uinwater;
539 | boolean found = FALSE;
540 | coord cc;
541 |
542 | for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
543 | if (ttmp->tx != u.ux || ttmp->ty != u.uy)
544 | goto outtrapmap;
545 | else found = TRUE;
546 | }
547 | for (obj = fobj; obj; obj = obj->nobj) {
548 | if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped) {
549 | if (obj->ox != u.ux || obj->oy != u.uy)
550 | goto outtrapmap;
551 | else found = TRUE;
552 | }
553 | }
554 | for (door = 0; door <= doorindex; door++) {
555 | cc = doors[door];
556 | if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
557 | if (cc.x != u.ux || cc.x != u.uy)
558 | goto outtrapmap;
559 | else found = TRUE;
560 | }
561 | }
562 | if (!found) {
563 | char buf[42];
564 | Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
565 | strange_feeling(sobj,buf);
566 | return(1);
567 | }
568 | /* traps exist, but only under me - no separate display required */
569 | Your("%s itch.", makeplural(body_part(TOE)));
570 | return(0);
571 | outtrapmap:
572 | cls();
573 |
574 | u.uinwater = 0;
575 | for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
576 | sense_trap(ttmp, 0, 0, sobj && sobj->cursed);
577 |
578 | for (obj = fobj; obj; obj = obj->nobj)
579 | if ((obj->otyp==LARGE_BOX || obj->otyp==CHEST) && obj->otrapped)
580 | sense_trap((struct trap *)0, obj->ox, obj->oy, sobj && sobj->cursed);
581 |
582 | for (door = 0; door <= doorindex; door++) {
583 | cc = doors[door];
584 | if (levl[cc.x][cc.y].doormask & D_TRAPPED)
585 | sense_trap((struct trap *)0, cc.x, cc.y, sobj && sobj->cursed);
586 | }
587 |
588 | newsym(u.ux,u.uy);
589 | You_feel("%s.", sobj && sobj->cursed ? "very greedy" : "entrapped");
590 | display_nhwindow(WIN_MAP, TRUE);
591 | docrt();
592 | u.uinwater = uw;
593 | if (Underwater) under_water(2);
594 | if (u.uburied) under_ground(2);
595 | return(0);
596 | }
597 |
598 | const char *
599 | level_distance(where)
600 | d_level *where;
601 | {
602 | register schar ll = depth(&u.uz) - depth(where);
603 | register boolean indun = (u.uz.dnum == where->dnum);
604 |
605 | if (ll < 0) {
606 | if (ll < (-8 - rn2(3)))
607 | if (!indun) return "far away";
608 | else return "far below";
609 | else if (ll < -1)
610 | if (!indun) return "away below you";
611 | else return "below you";
612 | else
613 | if (!indun) return "in the distance";
614 | else return "just below";
615 | } else if (ll > 0) {
616 | if (ll > (8 + rn2(3)))
617 | if (!indun) return "far away";
618 | else return "far above";
619 | else if (ll > 1)
620 | if (!indun) return "away above you";
621 | else return "above you";
622 | else
623 | if (!indun) return "in the distance";
624 | else return "just above";
625 | } else
626 | if (!indun) return "in the distance";
627 | else return "near you";
628 | }
629 |
630 | static struct {
631 | const char *what;
632 | d_level *where;
633 | } level_detects[] = {
634 | { "Delphi", &oracle_level },
635 | { "Medusa's lair", &medusa_level },
636 | { "a castle", &stronghold_level },
637 | { "the Wizard of Yendor's tower", &wiz1_level },
638 | };
639 |
640 | void
641 | use_crystal_ball(obj)
642 | struct obj *obj;
643 | {
644 | char ch;
645 | int oops;
646 | const char *bname = xname(obj);
647 |
648 | if (Blind) {
649 | pline("Too bad you can't see %s", the(bname));
650 | return;
651 | }
652 | oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
653 | if (oops && (obj->spe > 0)) {
654 | switch (rnd(obj->oartifact ? 4 : 5)) {
655 | case 1 : pline("%s is too much to comprehend!", The(bname));
656 | break;
657 | case 2 : pline("%s confuses you!", The(bname));
658 | make_confused(HConfusion + rnd(100),FALSE);
659 | break;
660 | case 3 : if (!resists_blnd(&youmonst)) {
661 | pline("%s damages your vision!", The(bname));
662 | make_blinded(Blinded + rnd(100),FALSE);
663 | } else {
664 | pline("%s assaults your vision.", The(bname));
665 | You("are unaffected!");
666 | }
667 | break;
668 | case 4 : pline("%s zaps your mind!", The(bname));
669 | make_hallucinated(HHallucination + rnd(100),FALSE,0L);
670 | break;
671 | case 5 : pline("%s explodes!", The(bname));
672 | useup(obj);
673 | losehp(rnd(30), "exploding crystal ball", KILLED_BY_AN);
674 | break;
675 | }
676 | check_unpaid(obj);
677 | obj->spe--;
678 | return;
679 | }
680 |
681 | if (Hallucination) {
682 | if (!obj->spe) {
683 | pline("All you see is funky %s haze.", hcolor((char *)0));
684 | } else {
685 | switch(rnd(6)) {
686 | case 1 : You("grok some groovy globs of incandescent lava.");
687 | break;
688 | case 2 : pline("Whoa! Psychedelic colors, %s!",
689 | poly_gender() == 1 ? "babe" : "dude");
690 | break;
691 | case 3 : pline_The("crystal pulses with sinister %s light!",
692 | hcolor((char *)0));
693 | break;
694 | case 4 : You("see goldfish swimming above fluorescent rocks.");
695 | break;
696 | case 5 : You("see tiny snowflakes spinning around a miniature farmhouse.");
697 | break;
698 | default: pline("Oh wow... like a kaleidoscope!");
699 | break;
700 | }
701 | check_unpaid(obj);
702 | obj->spe--;
703 | }
704 | return;
705 | }
706 |
707 | /* read a single character */
708 | if (flags.verbose) You("may look for an object or monster symbol.");
709 | ch = yn_function("What do you look for?", (char *)0, '\0');
710 | if (index(quitchars,ch)) {
711 | if (flags.verbose) pline(Never_mind);
712 | return;
713 | }
714 | You("peer into %s...", the(bname));
715 | nomul(-rnd(10));
716 | nomovemsg = "";
717 | if (obj->spe <= 0)
718 | pline_The("vision is unclear.");
719 | else {
720 | int class;
721 | int ret = 0;
722 |
723 | makeknown(CRYSTAL_BALL);
724 | check_unpaid(obj);
725 | obj->spe--;
726 |
727 | if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
728 | ret = object_detect((struct obj *)0, class);
729 | else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
730 | ret = monster_detect((struct obj *)0, class);
731 | else switch(ch) {
732 | case '^':
733 | ret = trap_detect((struct obj *)0);
734 | break;
735 | default:
736 | {
737 | int i = rn2(SIZE(level_detects));
738 | You("see %s, %s.",
739 | level_detects[i].what,
740 | level_distance(level_detects[i].where));
741 | }
742 | ret = 0;
743 | break;
744 | }
745 |
746 | if (ret) {
747 | if (!rn2(100)) /* make them nervous */
748 | You("see the Wizard of Yendor gazing out at you.");
749 | else pline_The("vision is unclear.");
750 | }
751 | }
752 | return;
753 | }
754 |
755 | STATIC_OVL void
756 | show_map_spot(x, y)
757 | register int x, y;
758 | {
759 | register struct rm *lev;
760 |
761 | if (Confusion && rn2(7)) return;
762 | lev = &levl[x][y];
763 |
764 | lev->seenv = SVALL;
765 |
766 | /* Secret corridors are found, but not secret doors. */
767 | if (lev->typ == SCORR) {
768 | lev->typ = CORR;
769 | unblock_point(x,y);
770 | }
771 |
772 | /* if we don't remember an object or trap there, map it */
773 | if (lev->typ == ROOM ?
774 | (glyph_is_cmap(lev->glyph) && !glyph_is_trap(lev->glyph) &&
775 | glyph_to_cmap(lev->glyph) != ROOM) :
776 | (!glyph_is_object(lev->glyph) && !glyph_is_trap(lev->glyph))) {
777 | if (level.flags.hero_memory) {
778 | magic_map_background(x,y,0);
779 | newsym(x,y); /* show it, if not blocked */
780 | } else {
781 | magic_map_background(x,y,1); /* display it */
782 | }
783 | }
784 | }
785 |
786 | void
787 | do_mapping()
788 | {
789 | register int zx, zy;
790 | int uw = u.uinwater;
791 |
792 | u.uinwater = 0;
793 | for (zx = 1; zx < COLNO; zx++)
794 | for (zy = 0; zy < ROWNO; zy++)
795 | show_map_spot(zx, zy);
796 | exercise(A_WIS, TRUE);
797 | u.uinwater = uw;
798 | if (!level.flags.hero_memory || Underwater) {
799 | flush_screen(1); /* flush temp screen */
800 | display_nhwindow(WIN_MAP, TRUE); /* wait */
801 | docrt();
802 | }
803 | }
804 |
805 | void
806 | do_vicinity_map()
807 | {
808 | register int zx, zy;
809 | int lo_y = (u.uy-5 < 0 ? 0 : u.uy-5),
810 | hi_y = (u.uy+6 > ROWNO ? ROWNO : u.uy+6),
811 | lo_x = (u.ux-9 < 1 ? 1 : u.ux-9), /* avoid column 0 */
812 | hi_x = (u.ux+10 > COLNO ? COLNO : u.ux+10);
813 |
814 | for (zx = lo_x; zx < hi_x; zx++)
815 | for (zy = lo_y; zy < hi_y; zy++)
816 | show_map_spot(zx, zy);
817 |
818 | if (!level.flags.hero_memory || Underwater) {
819 | flush_screen(1); /* flush temp screen */
820 | display_nhwindow(WIN_MAP, TRUE); /* wait */
821 | docrt();
822 | }
823 | }
824 |
825 | /* convert a secret door into a normal door */
826 | void
827 | cvt_sdoor_to_door(lev)
828 | struct rm *lev;
829 | {
830 | int newmask = lev->doormask & ~WM_MASK;
831 |
832 | #ifdef REINCARNATION
833 | if (Is_rogue_level(&u.uz))
834 | /* rogue didn't have doors, only doorways */
835 | newmask = D_NODOOR;
836 | else
837 | #endif
838 | /* newly exposed door is closed */
839 | if (!(newmask & D_LOCKED)) newmask |= D_CLOSED;
840 |
841 | lev->typ = DOOR;
842 | lev->doormask = newmask;
843 | }
844 |
845 |
846 | STATIC_PTR void
847 | findone(zx,zy,num)
848 | int zx,zy;
849 | genericptr_t num;
850 | {
851 | register struct trap *ttmp;
852 | register struct monst *mtmp;
853 |
854 | if(levl[zx][zy].typ == SDOOR) {
855 | cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
856 | newsym(zx, zy);
857 | (*(int*)num)++;
858 | } else if(levl[zx][zy].typ == SCORR) {
859 | levl[zx][zy].typ = CORR;
860 | newsym(zx, zy);
861 | (*(int*)num)++;
862 | } else if ((ttmp = t_at(zx, zy)) != 0) {
863 | if(!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
864 | ttmp->tseen = 1;
865 | newsym(zx,zy);
866 | (*(int*)num)++;
867 | }
868 | } else if ((mtmp = m_at(zx, zy)) != 0) {
869 | if(mtmp->m_ap_type) {
870 | seemimic(mtmp);
871 | (*(int*)num)++;
872 | }
873 | if (mtmp->mundetected &&
874 | (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
875 | mtmp->mundetected = 0;
876 | newsym(zx, zy);
877 | (*(int*)num)++;
878 | }
879 | if (!canspotmon(mtmp) &&
880 | !glyph_is_invisible(levl[zx][zy].glyph))
881 | map_invisible(zx, zy);
882 | } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
883 | unmap_object(zx, zy);
884 | newsym(zx, zy);
885 | (*(int*)num)++;
886 | }
887 | }
888 |
889 | STATIC_PTR void
890 | openone(zx,zy,num)
891 | int zx,zy;
892 | genericptr_t num;
893 | {
894 | register struct trap *ttmp;
895 | register struct obj *otmp;
896 |
897 | if(OBJ_AT(zx, zy)) {
898 | for(otmp = level.objects[zx][zy];
899 | otmp; otmp = otmp->nexthere) {
900 | if(Is_box(otmp) && otmp->olocked) {
901 | otmp->olocked = 0;
902 | (*(int*)num)++;
903 | }
904 | }
905 | /* let it fall to the next cases. could be on trap. */
906 | }
907 | if(levl[zx][zy].typ == SDOOR || (levl[zx][zy].typ == DOOR &&
908 | (levl[zx][zy].doormask & (D_CLOSED|D_LOCKED)))) {
909 | if(levl[zx][zy].typ == SDOOR)
910 | cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
911 | if(levl[zx][zy].doormask & D_TRAPPED) {
912 | if(distu(zx, zy) < 3) b_trapped("door", 0);
913 | else Norep("You %s an explosion!",
914 | cansee(zx, zy) ? "see" :
915 | (flags.soundok ? "hear" :
916 | "feel the shock of"));
917 | wake_nearto(zx, zy, 11*11);
918 | levl[zx][zy].doormask = D_NODOOR;
919 | } else
920 | levl[zx][zy].doormask = D_ISOPEN;
921 | newsym(zx, zy);
922 | (*(int*)num)++;
923 | } else if(levl[zx][zy].typ == SCORR) {
924 | levl[zx][zy].typ = CORR;
925 | newsym(zx, zy);
926 | (*(int*)num)++;
927 | } else if ((ttmp = t_at(zx, zy)) != 0) {
928 | if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
929 | ttmp->tseen = 1;
930 | newsym(zx,zy);
931 | (*(int*)num)++;
932 | }
933 | } else if (find_drawbridge(&zx, &zy)) {
934 | /* make sure it isn't an open drawbridge */
935 | open_drawbridge(zx, zy);
936 | (*(int*)num)++;
937 | }
938 | }
939 |
940 | int
941 | findit() /* returns number of things found */
942 | {
943 | int num = 0;
944 |
945 | if(u.uswallow) return(0);
946 | do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
947 | return(num);
948 | }
949 |
950 | int
951 | openit() /* returns number of things found and opened */
952 | {
953 | int num = 0;
954 |
955 | if(u.uswallow) {
956 | if (is_animal(u.ustuck->data)) {
957 | if (Blind) pline("Its mouth opens!");
958 | else pline("%s opens its mouth!", Monnam(u.ustuck));
959 | }
960 | expels(u.ustuck, u.ustuck->data, TRUE);
961 | return(-1);
962 | }
963 |
964 | do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
965 | return(num);
966 | }
967 |
968 | void
969 | find_trap(trap)
970 | struct trap *trap;
971 | {
972 | int tt = what_trap(trap->ttyp);
973 |
974 | You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
975 | trap->tseen = 1;
976 | exercise(A_WIS, TRUE);
977 | if (Blind)
978 | feel_location(trap->tx, trap->ty);
979 | else
980 | newsym(trap->tx, trap->ty);
981 | }
982 |
983 | int
984 | dosearch0(aflag)
985 | register int aflag;
986 | {
987 | #ifdef GCC_BUG
988 | /* some versions of gcc seriously muck up nested loops. if you get strange
989 | crashes while searching in a version compiled with gcc, try putting
990 | #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
991 | makefile).
992 | */
993 | volatile xchar x, y;
994 | #else
995 | register xchar x, y;
996 | #endif
997 | register struct trap *trap;
998 | register struct monst *mtmp;
999 |
1000 | if(u.uswallow) {
1001 | if (!aflag)
1002 | pline("What are you looking for? The exit?");
1003 | } else {
1004 | int fund = (uwep && uwep->oartifact &&
1005 | spec_ability(uwep, SPFX_SEARCH)) ?
1006 | ((uwep->spe > 5) ? 5 : uwep->spe) : 0;
1007 | for(x = u.ux-1; x < u.ux+2; x++)
1008 | for(y = u.uy-1; y < u.uy+2; y++) {
1009 | if(!isok(x,y)) continue;
1010 | if(x != u.ux || y != u.uy) {
1011 | if (Blind && !aflag) feel_location(x,y);
1012 | if(levl[x][y].typ == SDOOR) {
1013 | if(rnl(7-fund)) continue;
1014 | cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1015 | exercise(A_WIS, TRUE);
1016 | nomul(0);
1017 | if (Blind && !aflag)
1018 | feel_location(x,y); /* make sure it shows up */
1019 | else
1020 | newsym(x,y);
1021 | } else if(levl[x][y].typ == SCORR) {
1022 | if(rnl(7-fund)) continue;
1023 | levl[x][y].typ = CORR;
1024 | unblock_point(x,y); /* vision */
1025 | exercise(A_WIS, TRUE);
1026 | nomul(0);
1027 | newsym(x,y);
1028 | } else {
1029 | /* Be careful not to find anything in an SCORR or SDOOR */
1030 | if((mtmp = m_at(x, y)) && !aflag) {
1031 | if(mtmp->m_ap_type) {
1032 | seemimic(mtmp);
1033 | find: exercise(A_WIS, TRUE);
1034 | if (!canspotmon(mtmp)) {
1035 | if (glyph_is_invisible(levl[x][y].glyph)) {
1036 | /* found invisible monster in a square
1037 | * which already has an 'I' in it.
1038 | * Logically, this should still take
1039 | * time and lead to a return(1), but if
1040 | * we did that the player would keep
1041 | * finding the same monster every turn.
1042 | */
1043 | continue;
1044 | } else {
1045 | You_feel("an unseen monster!");
1046 | map_invisible(x, y);
1047 | }
1048 | } else
1049 | You("find %s.", a_monnam(mtmp));
1050 | return(1);
1051 | }
1052 | if(mtmp->mundetected &&
1053 | (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1054 | mtmp->mundetected = 0;
1055 | newsym(x,y);
1056 | goto find;
1057 | }
1058 | if (!canspotmon(mtmp))
1059 | goto find;
1060 | }
1061 |
1062 | /* see if an invisible monster has moved--if Blind,
1063 | * feel_location() already did it
1064 | */
1065 | if (!aflag && !mtmp && !Blind &&
1066 | glyph_is_invisible(levl[x][y].glyph)) {
1067 | unmap_object(x,y);
1068 | newsym(x,y);
1069 | }
1070 |
1071 | if ((trap = t_at(x,y)) && !trap->tseen && !rnl(8)) {
1072 | nomul(0);
1073 |
1074 | if (trap->ttyp == STATUE_TRAP) {
1075 | if (activate_statue_trap(trap, x, y, FALSE))
1076 | exercise(A_WIS, TRUE);
1077 | return(1);
1078 | } else {
1079 | find_trap(trap);
1080 | }
1081 | }
1082 | }
1083 | }
1084 | }
1085 | }
1086 | return(1);
1087 | }
1088 |
1089 | int
1090 | dosearch()
1091 | {
1092 | return(dosearch0(0));
1093 | }
1094 |
1095 | /* Pre-map the sokoban levels */
1096 | void
1097 | sokoban_detect()
1098 | {
1099 | register int x, y;
1100 | register struct trap *ttmp;
1101 | register struct obj *obj;
1102 |
1103 | /* Map the background and boulders */
1104 | for (x = 1; x < COLNO; x++)
1105 | for (y = 0; y < ROWNO; y++) {
1106 | levl[x][y].seenv = SVALL;
1107 | levl[x][y].waslit = TRUE;
1108 | map_background(x, y, 1);
1109 | for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
1110 | if (obj->otyp == BOULDER)
1111 | map_object(obj, 1);
1112 | }
1113 |
1114 | /* Map the traps */
1115 | for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1116 | ttmp->tseen = 1;
1117 | map_trap(ttmp, 1);
1118 | }
1119 | }
1120 |
1121 |
1122 | /*detect.c*/