1 /* NetHack 3.6 detect.c $NHDT-Date: 1575245054 2019/12/02 00:04:14 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.100 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2018. */
4 /* NetHack may be freely redistributed. See license for details. */
7 * Detection routines, including crystal ball, magic mapping, and search
11 /* JNetHack Copyright */
12 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
13 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
14 /* JNetHack may be freely redistributed. See license for details. */
19 extern boolean known; /* from read.c */
21 STATIC_DCL boolean NDECL(unconstrain_map);
22 STATIC_DCL void NDECL(reconstrain_map);
23 STATIC_DCL void FDECL(browse_map, (int, const char *));
24 STATIC_DCL void FDECL(map_monst, (struct monst *, BOOLEAN_P));
25 STATIC_DCL void FDECL(do_dknown_of, (struct obj *));
26 STATIC_DCL boolean FDECL(check_map_spot, (int, int, CHAR_P, unsigned));
27 STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P, unsigned));
28 STATIC_DCL void FDECL(sense_trap, (struct trap *, XCHAR_P, XCHAR_P, int));
29 STATIC_DCL int FDECL(detect_obj_traps, (struct obj *, BOOLEAN_P, int));
30 STATIC_DCL void FDECL(show_map_spot, (int, int));
31 STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
32 STATIC_PTR void FDECL(openone, (int, int, genericptr_t));
33 STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P));
34 STATIC_DCL int FDECL(reveal_terrain_getglyph, (int, int, int,
37 /* bring hero out from underwater or underground or being engulfed;
38 return True iff any change occurred */
42 boolean res = u.uinwater || u.uburied || u.uswallow;
44 /* bring Underwater, buried, or swallowed hero to normal map */
45 iflags.save_uinwater = u.uinwater, u.uinwater = 0;
46 iflags.save_uburied = u.uburied, u.uburied = 0;
47 iflags.save_uswallow = u.uswallow, u.uswallow = 0;
52 /* put hero back underwater or underground or engulfed */
56 u.uinwater = iflags.save_uinwater, iflags.save_uinwater = 0;
57 u.uburied = iflags.save_uburied, iflags.save_uburied = 0;
58 u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0;
61 /* use getpos()'s 'autodescribe' to view whatever is currently shown on map */
63 browse_map(ter_typ, ter_explain)
65 const char *ter_explain;
67 coord dummy_pos; /* don't care whether player actually picks a spot */
68 boolean save_autodescribe;
70 dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */
71 save_autodescribe = iflags.autodescribe;
72 iflags.autodescribe = TRUE;
73 iflags.terrainmode = ter_typ;
74 getpos(&dummy_pos, FALSE, ter_explain);
75 iflags.terrainmode = 0;
76 iflags.autodescribe = save_autodescribe;
79 /* extracted from monster_detection() so can be shared by do_vicinity_map() */
81 map_monst(mtmp, showtail)
85 if (def_monsyms[(int) mtmp->data->mlet].sym == ' ')
86 show_glyph(mtmp->mx, mtmp->my,
87 detected_mon_to_glyph(mtmp, newsym_rn2));
89 show_glyph(mtmp->mx, mtmp->my, mtmp->mtame
90 ? pet_to_glyph(mtmp, newsym_rn2)
91 : mon_to_glyph(mtmp, newsym_rn2));
93 if (showtail && mtmp->data == &mons[PM_LONG_WORM])
94 detect_wsegs(mtmp, 0);
97 /* this is checking whether a trap symbol represents a trapped chest,
98 not whether a trapped chest is actually present */
100 trapped_chest_at(ttyp, x, y)
107 if (!glyph_is_trap(glyph_at(x, y)))
109 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
113 * TODO? We should check containers recursively like the trap
114 * detecting routine does. Chests and large boxes do not nest in
115 * themselves or each other, but could be contained inside statues.
117 * For farlook, we should also check for buried containers, but
118 * for '^' command to examine adjacent trap glyph, we shouldn't.
121 /* on map, presence of any trappable container will do */
122 if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y))
124 /* in inventory, we need to find one which is actually trapped */
125 if (x == u.ux && y == u.uy) {
126 for (otmp = invent; otmp; otmp = otmp->nobj)
127 if (Is_box(otmp) && otmp->otrapped)
129 if (u.usteed) { /* steed isn't on map so won't be found by m_at() */
130 for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj)
131 if (Is_box(otmp) && otmp->otrapped)
135 if ((mtmp = m_at(x, y)) != 0)
136 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
137 if (Is_box(otmp) && otmp->otrapped)
142 /* this is checking whether a trap symbol represents a trapped door,
143 not whether the door here is actually trapped */
145 trapped_door_at(ttyp, x, y)
151 if (!glyph_is_trap(glyph_at(x, y)))
153 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
156 if (!IS_DOOR(lev->typ))
158 if ((lev->doormask & (D_NODOOR | D_BROKEN | D_ISOPEN)) != 0
159 && trapped_chest_at(ttyp, x, y))
164 /* recursively search obj for an object in class oclass, return 1st found */
170 register struct obj *otmp;
173 if (obj->oclass == oclass)
176 * Note: we exclude SchroedingersBox because the corpse it contains
177 * isn't necessarily a corpse yet. Resolving the status would lead
178 * to complications if it turns out to be a live cat. We know that
179 * that Box can't contain anything else because putting something in
180 * would resolve the cat/corpse situation and convert to ordinary box.
182 if (Has_contents(obj) && !SchroedingersBox(obj)) {
183 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
184 if (otmp->oclass == oclass)
186 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)) != 0)
189 return (struct obj *) 0;
192 /* Recursively search obj for an object made of specified material.
193 * Return first found.
196 o_material(obj, material)
200 register struct obj *otmp;
203 if (objects[obj->otyp].oc_material == material)
206 if (Has_contents(obj)) {
207 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
208 if (objects[otmp->otyp].oc_material == material)
210 else if (Has_contents(otmp)
211 && (temp = o_material(otmp, material)) != 0)
214 return (struct obj *) 0;
224 if (Has_contents(obj)) {
225 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
230 /* Check whether the location has an outdated object displayed on it. */
232 check_map_spot(x, y, oclass, material)
238 register struct obj *otmp;
239 register struct monst *mtmp;
241 glyph = glyph_at(x, y);
242 if (glyph_is_object(glyph)) {
243 /* there's some object shown here */
244 if (oclass == ALL_CLASSES) {
245 return (boolean) !(level.objects[x][y] /* stale if nothing here */
246 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
249 && objects[glyph_to_obj(glyph)].oc_material == material) {
250 /* object shown here is of interest because material matches */
251 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
252 if (o_material(otmp, GOLD))
254 /* didn't find it; perhaps a monster is carrying it */
255 if ((mtmp = m_at(x, y)) != 0) {
256 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
257 if (o_material(otmp, GOLD))
260 /* detection indicates removal of this object from the map */
263 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
264 /* obj shown here is of interest because its class matches */
265 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
266 if (o_in(otmp, oclass))
268 /* didn't find it; perhaps a monster is carrying it */
269 if ((mtmp = m_at(x, y)) != 0) {
270 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
271 if (o_in(otmp, oclass))
274 /* detection indicates removal of this object from the map */
283 * When doing detection, remove stale data from the map display (corpses
284 * rotted away, objects carried away by monsters, etc) so that it won't
285 * reappear after the detection has completed. Return true if noticeable
289 clear_stale_map(oclass, material)
294 boolean change_made = FALSE;
296 for (zx = 1; zx < COLNO; zx++)
297 for (zy = 0; zy < ROWNO; zy++)
298 if (check_map_spot(zx, zy, oclass, material)) {
299 unmap_object(zx, zy);
306 /* look for gold, on the floor or in monsters' possession */
309 register struct obj *sobj;
311 register struct obj *obj;
312 register struct monst *mtmp;
313 struct obj gold, *temp = 0;
314 boolean stale, ugold = FALSE, steedgold = FALSE;
315 int ter_typ = TER_DETECT | TER_OBJ;
317 known = stale = clear_stale_map(COIN_CLASS,
318 (unsigned) (sobj->blessed ? GOLD : 0));
320 /* look for gold carried by monsters (might be in a container) */
321 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
322 if (DEADMONSTER(mtmp))
323 continue; /* probably not needed in this case but... */
324 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
325 if (mtmp == u.usteed) {
329 goto outgoldmap; /* skip further searching */
332 for (obj = mtmp->minvent; obj; obj = obj->nobj)
333 if ((sobj->blessed && o_material(obj, GOLD))
334 || o_in(obj, COIN_CLASS)) {
335 if (mtmp == u.usteed) {
339 goto outgoldmap; /* skip further searching */
345 /* look for gold objects */
346 for (obj = fobj; obj; obj = obj->nobj) {
347 if (sobj->blessed && o_material(obj, GOLD)) {
349 if (obj->ox != u.ux || obj->oy != u.uy)
351 } else if (o_in(obj, COIN_CLASS)) {
353 if (obj->ox != u.ux || obj->oy != u.uy)
359 /* no gold found on floor or monster's inventory.
360 adjust message if you have gold in your inventory */
364 if (youmonst.data == &mons[PM_GOLD_GOLEM])
366 Sprintf(buf, "You feel like a million %s!", currency(2L));
368 Strcpy(buf, "
\82 \82È
\82½
\82Í
\8bà
\8e\9d\82¿
\82É
\82È
\82Á
\82½
\82æ
\82¤
\82É
\8a´
\82¶
\82½
\81I");
369 else if (money_cnt(invent) || hidden_gold())
372 "You feel worried about your future financial situation.");
374 "
\82 \82È
\82½
\82Í
\8f«
\97\88\82Ì
\8co
\8dÏ
\8fó
\8bµ
\82ª
\90S
\94z
\82É
\82È
\82Á
\82½
\81D");
377 Sprintf(buf, "You feel interested in %s financial situation.",
378 s_suffix(x_monnam(u.usteed,
379 u.usteed->mtame ? ARTICLE_YOUR
382 SUPPRESS_SADDLE, FALSE)));
384 Sprintf(buf, "
\82 \82È
\82½
\82Í%s
\82Ì
\8co
\8dÏ
\8fó
\8bµ
\82É
\8b»
\96¡
\82ª
\8fo
\82Ä
\82«
\82½
\81D",
386 u.usteed->mtame ? ARTICLE_YOUR
389 SUPPRESS_SADDLE, FALSE));
393 Strcpy(buf, "You feel materially poor.");
395 Strcpy(buf, "
\82 \82È
\82½
\82Í
\82Ð
\82à
\82¶
\82³
\82ð
\8a´
\82¶
\82½
\81D");
397 strange_feeling(sobj, buf);
401 /* only under me - no separate display required */
405 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
407 You("%s
\82Ì
\8aÔ
\82É
\8bà
\89Ý
\82ª
\97\8e\82¿
\82Ä
\82¢
\82é
\82±
\82Æ
\82É
\8bC
\82ª
\82Â
\82¢
\82½
\81D", body_part(FOOT));
413 (void) unconstrain_map();
414 /* Discover gold locations. */
415 for (obj = fobj; obj; obj = obj->nobj) {
416 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
422 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
429 if (temp && temp->ox == u.ux && temp->oy == u.uy)
432 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
433 if (DEADMONSTER(mtmp))
434 continue; /* probably overkill here */
436 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
437 gold = zeroobj; /* ensure oextra is cleared too */
438 gold.otyp = GOLD_PIECE;
439 gold.quan = (long) rnd(10); /* usually more than 1 */
442 map_object(&gold, 1);
445 for (obj = mtmp->minvent; obj; obj = obj->nobj)
446 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
451 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
458 if (temp && temp->ox == u.ux && temp->oy == u.uy)
463 ter_typ |= TER_MON; /* so autodescribe will recognize hero */
466 You_feel("very greedy, and sense gold!");
468 You("
\82Ç
\82ñ
\97~
\82É
\82È
\82Á
\82½
\82æ
\82¤
\82È
\8bC
\82ª
\82µ
\82½
\81C
\82»
\82µ
\82Ä
\8bà
\89Ý
\82Ì
\88Ê
\92u
\82ð
\8a´
\92m
\82µ
\82½
\81I");
469 exercise(A_WIS, TRUE);
472 browse_map(ter_typ, "gold");
474 browse_map(ter_typ, "
\8bà");
485 /* returns 1 if nothing was detected, 0 if something was detected */
488 register struct obj *sobj;
490 register struct obj *obj;
491 register struct monst *mtmp;
492 register int ct = 0, ctu = 0;
493 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
494 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
496 const char *what = confused ? something : "food";
498 const char *what = confused ? "
\83n
\83\89\83w
\83\8a" : "
\90H
\82×
\95¨";
500 stale = clear_stale_map(oclass, 0);
501 if (u.usteed) /* some situations leave steed with stale coordinates */
502 u.usteed->mx = u.ux, u.usteed->my = u.uy;
504 for (obj = fobj; obj; obj = obj->nobj)
505 if (o_in(obj, oclass)) {
506 if (obj->ox == u.ux && obj->oy == u.uy)
511 for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) {
512 /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */
513 for (obj = mtmp->minvent; obj; obj = obj->nobj)
514 if (o_in(obj, oclass)) {
515 if (mtmp->mx == u.ux && mtmp->my == u.uy)
516 ctu++; /* steed or an engulfer with inventory */
524 known = stale && !confused;
528 You("sense a lack of %s nearby.", what);
530 You("%s
\82ª
\8c¸
\82Á
\82Ä
\82¢
\82é
\82Ì
\82É
\8bC
\82ª
\82Â
\82¢
\82½
\81D",what);
531 if (sobj && sobj->blessed) {
534 Your("%s starts to tingle.", body_part(NOSE));
536 Your("%s
\82ª
\82Ò
\82
\82Á
\82Æ
\93®
\82¢
\82½
\81D", body_part(NOSE));
543 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
544 (sobj->blessed && !u.uedibility)
545 ? " then starts to tingle"
548 Sprintf(buf, "
\82 \82È
\82½
\82Ì%s
\82ª
\82Ð
\82
\82Ð
\82
\82Æ
\93®
\82¢%s
\81D", body_part(NOSE),
549 (sobj->blessed && !u.uedibility)
550 ? "
\82Ä
\81C
\82¤
\82¸
\82¤
\82¸
\82µ
\82¾
\82µ
\82½"
553 if (sobj->blessed && !u.uedibility) {
554 boolean savebeginner = flags.beginner;
556 flags.beginner = FALSE; /* prevent non-delivery of message */
557 strange_feeling(sobj, buf);
558 flags.beginner = savebeginner;
561 strange_feeling(sobj, buf);
567 You("%s %s nearby.", sobj ? "smell" : "sense", what);
569 You("
\8bß
\82
\82Å%s%s
\81D", what, sobj ? "
\82Ì
\82É
\82¨
\82¢
\82ð
\82©
\82¢
\82¾" : "
\82ð
\8a´
\92m
\82µ
\82½");
570 if (sobj && sobj->blessed) {
573 pline("Your %s starts to tingle.", body_part(NOSE));
575 pline("
\82 \82È
\82½
\82Ì%s
\82ª
\82¤
\82¸
\82¤
\82¸
\82µ
\82¾
\82µ
\82½
\81D", body_part(NOSE));
580 int ter_typ = TER_DETECT | TER_OBJ;
584 (void) unconstrain_map();
585 for (obj = fobj; obj; obj = obj->nobj)
586 if ((temp = o_in(obj, oclass)) != 0) {
593 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
594 /* no DEADMONSTER() check needed -- dmons never have inventory */
595 for (obj = mtmp->minvent; obj; obj = obj->nobj)
596 if ((temp = o_in(obj, oclass)) != 0) {
600 break; /* skip rest of this monster's inventory */
604 ter_typ |= TER_MON; /* for autodescribe of self */
609 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
610 u.uedibility ? "continues" : "starts", what);
612 Your("%s
\82Í
\82¤
\82¸
\82¤
\82¸
\82µ%s
\81C%s
\82Ì
\93õ
\82¢
\82ð
\9ak
\82¬
\82Æ
\82Á
\82½
\81D", body_part(NOSE),
613 u.uedibility ? "
\91±
\82¯" : "
\8en
\82ß", what);
618 Your("%s tingles and you smell %s.", body_part(NOSE), what);
620 Your("%s
\82Í
\82¤
\82¸
\82¤
\82¸
\82µ
\81C%s
\82Ì
\93õ
\82¢
\82ð
\9ak
\82¬
\82Æ
\82Á
\82½
\81D", body_part(NOSE), what);
623 You("sense %s.", what);
625 You("%s
\82ð
\8a´
\92m
\82µ
\82½
\81D", what);
626 exercise(A_WIS, TRUE);
629 browse_map(ter_typ, "food");
631 browse_map(ter_typ, "
\90H
\97¿");
644 * Used for scrolls, potions, spells, and crystal balls. Returns:
646 * 1 - nothing was detected
647 * 0 - something was detected
650 object_detect(detector, class)
651 struct obj *detector; /* object doing the detecting */
652 int class; /* an object class, 0 for all */
656 int is_cursed = (detector && detector->cursed);
657 int do_dknown = (detector && (detector->oclass == POTION_CLASS
658 || detector->oclass == SPBOOK_CLASS)
659 && detector->blessed);
661 register struct obj *obj, *otmp = (struct obj *) 0;
662 register struct monst *mtmp;
663 int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ;
665 if (class < 0 || class >= MAXOCLASSES) {
666 impossible("object_detect: illegal class %d", class);
670 /* Special boulder symbol check - does the class symbol happen
671 * to match showsyms[SYM_BOULDER + SYM_OFF_X] which is user-defined.
672 * If so, that means we aren't sure what they really wanted to
673 * detect. Rather than trump anything, show both possibilities.
674 * We can exclude checking the buried obj chain for boulders below.
676 sym = class ? def_oc_syms[class].sym : 0;
677 if (sym && showsyms[SYM_BOULDER + SYM_OFF_X] && sym == showsyms[SYM_BOULDER + SYM_OFF_X])
678 boulder = ROCK_CLASS;
680 if (Hallucination || (Confusion && class == SCROLL_CLASS))
681 Strcpy(stuff, something);
684 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
686 Strcpy(stuff, class ? def_oc_syms[class].name : "
\95¨
\91Ì");
687 if (boulder && class != ROCK_CLASS)
689 Strcat(stuff, " and/or large stones");
691 Strcat(stuff, "
\82Æ
\8b\90\8aâ");
694 for (obj = invent; obj; obj = obj->nobj)
697 for (obj = fobj; obj; obj = obj->nobj) {
698 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
699 if (obj->ox == u.ux && obj->oy == u.uy)
708 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
709 if (!class || o_in(obj, class)) {
710 if (obj->ox == u.ux && obj->oy == u.uy)
720 u.usteed->mx = u.ux, u.usteed->my = u.uy;
722 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
723 if (DEADMONSTER(mtmp))
725 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
726 if ((!class && !boulder) || o_in(obj, class)
727 || o_in(obj, boulder))
732 if ((is_cursed && M_AP_TYPE(mtmp) == M_AP_OBJECT
733 && (!class || class == objects[mtmp->mappearance].oc_class))
734 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
740 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
744 strange_feeling(detector, "You feel a lack of something.");
746 strange_feeling(detector, "
\82 \82È
\82½
\82Í
\89½
\82©
\82ª
\8c\87\96R
\82µ
\82Ä
\82¢
\82é
\82æ
\82¤
\82È
\8bC
\82ª
\82µ
\82½
\81D");
751 You("sense %s nearby.", stuff);
753 You("
\8bß
\82
\82Ì%s
\82ð
\8a´
\92m
\82µ
\82½
\81D", stuff);
759 (void) unconstrain_map();
761 * Map all buried objects first.
763 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
764 if (!class || (otmp = o_in(obj, class)) != 0) {
775 * If we are mapping all objects, map only the top object of a pile or
776 * the first object in a monster's inventory. Otherwise, go looking
777 * for a matching object class and display the first one encountered
780 * Objects on the floor override buried objects.
782 for (x = 1; x < COLNO; x++)
783 for (y = 0; y < ROWNO; y++)
784 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
785 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
786 || (otmp = o_in(obj, boulder)) != 0) {
787 if (class || boulder) {
798 /* Objects in the monster's inventory override floor objects. */
799 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
800 if (DEADMONSTER(mtmp))
802 for (obj = mtmp->minvent; obj; obj = obj->nobj)
803 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
804 || (otmp = o_in(obj, boulder)) != 0) {
805 if (!class && !boulder)
807 otmp->ox = mtmp->mx; /* at monster location */
812 /* Allow a mimic to override the detected objects it is carrying. */
813 if (is_cursed && M_AP_TYPE(mtmp) == M_AP_OBJECT
814 && (!class || class == objects[mtmp->mappearance].oc_class)) {
818 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
822 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
823 map_object(&temp, 1);
824 } else if (findgold(mtmp->minvent)
825 && (!class || class == COIN_CLASS)) {
828 gold = zeroobj; /* ensure oextra is cleared too */
829 gold.otyp = GOLD_PIECE;
830 gold.quan = (long) rnd(10); /* usually more than 1 */
833 map_object(&gold, 1);
836 if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
841 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
843 You("%s%s
\81D", stuff, ct ? "
\82ð
\94
\8c©
\82µ
\82½" : "
\82Í
\89½
\82à
\82È
\82¢
\82±
\82Æ
\82ª
\82í
\82©
\82Á
\82½" );
846 display_nhwindow(WIN_MAP, TRUE);
849 browse_map(ter_typ, "object");
851 browse_map(ter_typ, "
\95¨
\91Ì");
854 docrt(); /* this will correctly reset vision */
863 * Used by: crystal balls, potions, fountains
865 * Returns 1 if nothing was detected.
866 * Returns 0 if something was detected.
869 monster_detect(otmp, mclass)
870 register struct obj *otmp; /* detecting object (if any) */
871 int mclass; /* monster class, 0 for all */
873 register struct monst *mtmp;
876 /* Note: This used to just check fmon for a non-zero value
877 * but in versions since 3.3.0 fmon can test TRUE due to the
878 * presence of dmons, so we have to find at least one
879 * with positive hit-points to know for sure.
881 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
882 if (!DEADMONSTER(mtmp)) {
889 strange_feeling(otmp, Hallucination
891 ? "You get the heebie jeebies."
893 ? "
\82 \82È
\82½
\82Í
\8bà
\92¹
\82Ì
\89Ä
\82Å
\83L
\83\93\83`
\83\87\81[
\82µ
\82½
\81D"
895 : "You feel threatened.");
897 : "
\82 \82È
\82½
\82Í
\8b°
\95|
\82Å
\82¼
\82
\82Á
\82Æ
\82µ
\82½
\81D");
900 boolean unconstrained, woken = FALSE;
901 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
904 unconstrained = unconstrain_map();
905 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
906 if (DEADMONSTER(mtmp))
908 if (!mclass || mtmp->data->mlet == mclass
909 || (mtmp->data == &mons[PM_LONG_WORM]
910 && mclass == S_WORM_TAIL))
911 map_monst(mtmp, TRUE);
913 if (otmp && otmp->cursed
914 && (mtmp->msleeping || !mtmp->mcanmove)) {
915 mtmp->msleeping = mtmp->mfrozen = 0;
923 You("sense the presence of monsters.");
925 You("
\89ö
\95¨
\82Ì
\91¶
\8dÝ
\82ð
\9ak
\82¬
\82Â
\82¯
\82½
\81D");
928 pline("Monsters sense the presence of you.");
930 pline("
\89ö
\95¨
\82Í
\82 \82È
\82½
\82Ì
\91¶
\8dÝ
\82ð
\9ak
\82¬
\82Â
\82¯
\82½
\81D");
932 if ((otmp && otmp->blessed) && !unconstrained) {
933 /* persistent detection--just show updated map */
934 display_nhwindow(WIN_MAP, TRUE);
936 /* one-shot detection--allow player to move cursor around and
937 get autodescribe feedback */
938 EDetect_monsters |= I_SPECIAL;
940 browse_map(TER_DETECT | TER_MON, "monster of interest");
942 browse_map(TER_DETECT | TER_MON, "
\8aÖ
\90S
\82Ì
\82 \82é
\89ö
\95¨");
943 EDetect_monsters &= ~I_SPECIAL;
947 docrt(); /* redraw the screen to remove unseen monsters from map */
957 sense_trap(trap, x, y, src_cursed)
962 if (Hallucination || src_cursed) {
963 struct obj obj; /* fake object */
973 obj.otyp = !Hallucination ? GOLD_PIECE : random_object(rn2);
974 obj.quan = (long) ((obj.otyp == GOLD_PIECE) ? rnd(10)
975 : objects[obj.otyp].oc_merge ? rnd(2) : 1);
976 obj.corpsenm = random_monster(rn2); /* if otyp == CORPSE */
981 } else { /* trapped door or trapped chest */
982 struct trap temp_trap; /* fake trap */
984 (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap);
987 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
988 map_trap(&temp_trap, 1);
992 #define OTRAP_NONE 0 /* nothing found */
993 #define OTRAP_HERE 1 /* found at hero's location */
994 #define OTRAP_THERE 2 /* found at any other location */
996 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
997 2 if found at some other spot, 3 if both, 0 otherwise; optionally
998 update the map to show where such traps were found */
1000 detect_obj_traps(objlist, show_them, how)
1001 struct obj *objlist;
1003 int how; /* 1 for misleading map feedback */
1007 int result = OTRAP_NONE;
1010 * TODO? Display locations of unarmed land mine and beartrap objects.
1011 * If so, should they be displayed as objects or as traps?
1014 for (otmp = objlist; otmp; otmp = otmp->nobj) {
1015 if (Is_box(otmp) && otmp->otrapped
1016 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
1017 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
1019 sense_trap((struct trap *) 0, x, y, how);
1021 if (Has_contents(otmp))
1022 result |= detect_obj_traps(otmp->cobj, show_them, how);
1027 /* the detections are pulled out so they can
1028 * also be used in the crystal ball routine
1029 * returns 1 if nothing was detected
1030 * returns 0 if something was detected
1034 struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
1036 register struct trap *ttmp;
1038 int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP;
1039 int cursed_src = sobj && sobj->cursed;
1040 boolean found = FALSE;
1044 u.usteed->mx = u.ux, u.usteed->my = u.uy;
1046 /* floor/ceiling traps */
1047 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1048 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
1053 /* chest traps (might be buried or carried) */
1054 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
1055 if (tr & OTRAP_THERE)
1060 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0)) != OTRAP_NONE) {
1061 if (tr & OTRAP_THERE)
1066 for (mon = fmon; mon; mon = mon->nmon) {
1067 if (DEADMONSTER(mon))
1069 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
1070 if (tr & OTRAP_THERE)
1076 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
1079 for (door = 0; door < doorindex; door++) {
1081 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
1082 if (cc.x != u.ux || cc.y != u.uy)
1092 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
1094 Sprintf(buf, "
\82 \82È
\82½
\82Ì%s
\82Ì
\82Þ
\82¸
\82Þ
\82¸
\82Í
\82¨
\82³
\82Ü
\82Á
\82½
\81D", makeplural(body_part(TOE)));
1095 strange_feeling(sobj, buf);
1098 /* traps exist, but only under me - no separate display required */
1100 Your("%s itch.", makeplural(body_part(TOE)));
1102 Your("%s
\82Í
\82Þ
\82¸
\82Þ
\82¸
\82µ
\82½
\81D", makeplural(body_part(TOE)));
1108 (void) unconstrain_map();
1109 /* show chest traps first, so that subsequent floor trap display
1110 will override if both types are present at the same location */
1111 (void) detect_obj_traps(fobj, TRUE, cursed_src);
1112 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
1113 for (mon = fmon; mon; mon = mon->nmon) {
1114 if (DEADMONSTER(mon))
1116 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
1118 (void) detect_obj_traps(invent, TRUE, cursed_src);
1120 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
1121 sense_trap(ttmp, 0, 0, cursed_src);
1123 for (door = 0; door < doorindex; door++) {
1125 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
1126 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
1129 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
1130 glyph = glyph_at(u.ux, u.uy);
1131 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) {
1133 ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
1136 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
1138 You("%s
\8bC
\8e\9d\82É
\82È
\82Á
\82½
\81D", cursed_src ? "
\82Æ
\82Ä
\82à
\82Ç
\82ñ
\97~
\82È" : "
\82¾
\82Ü
\82³
\82ê
\82Ä
\82¢
\82é
\82æ
\82¤
\82È");
1141 browse_map(ter_typ, "trap of interest");
1143 browse_map(ter_typ, "
\8aÖ
\90S
\82Ì
\82 \82éã©");
1146 docrt(); /* redraw the screen to remove unseen traps from the map */
1155 level_distance(where)
1158 register schar ll = depth(&u.uz) - depth(where);
1159 register boolean indun = (u.uz.dnum == where->dnum);
1162 if (ll < (-8 - rn2(3)))
1167 return "
\82Í
\82é
\82©
\94Þ
\95û
\82É";
1172 return "
\82Í
\82é
\82©
\89º
\95û
\82É";
1176 return "away below you";
1178 return "
\82¸
\82Á
\82Æ
\89º
\95û
\82É";
1183 return "
\89º
\95û
\82É";
1186 return "in the distance";
1188 return "
\89\93\82
\82É";
1191 return "just below";
1193 return "
\90^
\89º
\82É";
1194 } else if (ll > 0) {
1195 if (ll > (8 + rn2(3)))
1200 return "
\82Í
\82é
\82©
\94Þ
\95û
\82É";
1205 return "
\82Í
\82é
\82©
\8fã
\95û
\82É";
1209 return "away above you";
1211 return "
\82¸
\82Á
\82Æ
\8fã
\95û
\82É";
1216 return "
\8fã
\95û
\82É";
1219 return "in the distance";
1221 return "
\89\93\82
\82É";
1224 return "just above";
1226 return "
\90^
\8fã
\82É";
1229 return "in the distance";
1231 return "
\89\93\82
\82É";
1236 return "
\8bß
\82
\82É";
1239 static const struct {
1242 } level_detects[] = {
1244 { "Delphi", &oracle_level },
1246 { "
\83f
\83\8b\83t
\83@
\83C", &oracle_level },
1248 { "Medusa's lair", &medusa_level },
1250 { "
\83\81\83f
\83\85\81[
\83T
\82Ì
\8fZ
\82Ý
\82©", &medusa_level },
1252 { "a castle", &stronghold_level },
1254 { "
\8fé", &stronghold_level },
1256 { "the Wizard of Yendor's tower", &wiz1_level },
1258 { "
\83C
\83F
\83\93\83_
\81[
\82Ì
\96\82\96@
\8eg
\82¢
\82Ì
\93\83", &wiz1_level },
1262 use_crystal_ball(optr)
1267 struct obj *obj = *optr;
1271 pline("Too bad you can't see %s.", the(xname(obj)));
1273 pline("
\82È
\82ñ
\82Ä
\82±
\82Æ
\82¾
\81D%s
\82ð
\8c©
\82é
\82±
\82Æ
\82ª
\82Å
\82«
\82È
\82¢
\81D", the(xname(obj)));
1276 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
1277 if (oops && (obj->spe > 0)) {
1278 switch (rnd(obj->oartifact ? 4 : 5)) {
1281 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
1283 pline("%s
\82ð
\94`
\82¢
\82½
\82ª
\89½
\82Ì
\82±
\82Æ
\82¾
\82©
\82³
\82Á
\82Ï
\82è
\82í
\82©
\82ç
\82È
\82©
\82Á
\82½
\81I", xname(obj));
1287 pline("%s you!", Tobjnam(obj, "confuse"));
1289 pline("%s
\82ð
\94`
\82¢
\82Ä
\82é
\82Æ
\82Ó
\82ç
\82Â
\82¢
\82Ä
\82«
\82½
\81I", xname(obj));
1290 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
1293 if (!resists_blnd(&youmonst)) {
1295 pline("%s your vision!", Tobjnam(obj, "damage"));
1297 pline("%s
\82ð
\94`
\82¢
\82Ä
\82¢
\82é
\82Æ
\8e\8b\8ao
\82ª
\82¨
\82©
\82µ
\82
\82È
\82Á
\82Ä
\82«
\82½
\81I", xname(obj));
1298 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
1300 Your1(vision_clears);
1303 pline("%s your vision.", Tobjnam(obj, "assault"));
1305 pline("%s
\82ª
\82 \82È
\82½
\82Ì
\8e\8b\8aE
\82É
\94\97\82Á
\82Ä
\82«
\82½
\81D", xname(obj));
1307 You("are unaffected!");
1309 pline("
\82µ
\82©
\82µ
\81C
\82 \82È
\82½
\82Í
\89e
\8b¿
\82ð
\8eó
\82¯
\82È
\82©
\82Á
\82½
\81I");
1314 pline("%s your mind!", Tobjnam(obj, "zap"));
1316 pline("%s
\82ð
\94`
\82¢
\82Ä
\82¢
\82é
\82Æ
\8cÜ
\8a´
\82ª
\82¨
\82©
\82µ
\82
\82È
\82Á
\82Ä
\82«
\82½
\81I", xname(obj));
1317 (void) make_hallucinated(
1318 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
1322 pline("%s!", Tobjnam(obj, "explode"));
1324 pline("%s
\82Í
\94\9a\94
\82µ
\82½
\81I", xname(obj));
1326 *optr = obj = 0; /* it's gone */
1327 /* physical damage cause by the shards and force */
1329 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
1331 losehp(Maybe_Half_Phys(rnd(30)), "
\90\85\8f»
\8bÊ
\82Ì
\94\9a\94
\82Å",
1336 consume_obj_charge(obj, TRUE);
1340 if (Hallucination) {
1343 pline("All you see is funky %s haze.", hcolor((char *) 0));
1345 pline("
\82¨
\82¨
\81I
\83t
\83@
\83\93\83L
\81[
\83\82\83\93\83L
\81[
\82È%s
\82à
\82â
\82ª
\8c©
\82¦
\82é
\81D", hcolor((char *)0));
1350 You("grok some groovy globs of incandescent lava.");
1352 You("
\90\85\96å
\82Ì
\8c®
\82ð
\82à
\82Á
\82½
\90\85\8cË
\89©
\96å
\82ª
\95Ç
\82Ì
\89e
\82É
\89B
\82ê
\82Ä
\82¢
\82é
\82Ì
\82ª
\8c©
\82¦
\82½
\81D");
1356 pline("Whoa! Psychedelic colors, %s!",
1357 poly_gender() == 1 ? "babe" : "dude");
1359 pline("
\83\8f\81[
\83I
\81I
\83\89\83\8a\82Á
\82Ä
\82é
\82©
\82¢
\81H%s
\81I",
1360 poly_gender() == 1 ? "
\83x
\83C
\83r
\81[" : "
\83\86\81[");
1365 pline_The("crystal pulses with sinister %s light!",
1367 pline("
\90\85\8f»
\82Í
\95s
\8bg
\82È%s
\83p
\83\8b\83X
\82ð
\94
\82µ
\82½
\81I",
1368 hcolor((char *) 0));
1372 You_see("goldfish swimming above fluorescent rocks.");
1374 You("
\8cu
\8cõ
\8aâ
\82Ì
\8fã
\82ð
\8bà
\8b\9b\82ª
\89j
\82¢
\82Å
\82¢
\82é
\82Ì
\82ð
\8c©
\82½
\81D");
1379 "tiny snowflakes spinning around a miniature farmhouse.");
1381 You("
\8f¬
\82³
\82¢
\90á
\95Ð
\82ª
\83~
\83j
\83`
\83\85\83A
\82Ì
\94_
\89Æ
\82Ì
\89Æ
\82Ì
\82Ü
\82í
\82è
\82ð
\95\91\82Á
\82Ä
\82é
\82Ì
\82ð
\8c©
\82½
\81D");
1386 pline("Oh wow... like a kaleidoscope!");
1388 pline("
\83\8f\81[
\83I
\81D
\96\9c\89Ø
\8b¾
\82Ì
\82æ
\82¤
\82¾
\81I");
1391 consume_obj_charge(obj, TRUE);
1396 /* read a single character */
1399 You("may look for an object or monster symbol.");
1401 You("
\95¨
\91Ì
\82â
\89ö
\95¨
\82Ì
\8bL
\8d\86\82ð
\92T
\82¹
\82é
\81D");
1403 ch = yn_function("What do you look for?", (char *) 0, '\0');
1405 ch = yn_function("
\89½
\82ð
\92T
\82µ
\82Ü
\82·
\82©
\81H", (char *)0, '\0');
1406 /* Don't filter out ' ' here; it has a use */
1407 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
1413 You("peer into %s...", the(xname(obj)));
1415 You("%s
\82ð
\94`
\82«
\82±
\82ñ
\82¾
\81D
\81D
\81D", the(xname(obj)));
1418 multi_reason = "gazing into a crystal ball";
1420 multi_reason = "
\90\85\8f»
\8b\85\82ð
\94`
\82«
\8d\9e\82ñ
\82Å
\82¢
\82é
\8e\9e\82É";
1422 if (obj->spe <= 0) {
1424 pline_The("vision is unclear.");
1426 pline("
\89f
\91\9c\82Í
\95s
\91N
\96¾
\82¾
\82Á
\82½
\81D");
1431 makeknown(CRYSTAL_BALL);
1432 consume_obj_charge(obj, TRUE);
1434 /* special case: accept ']' as synonym for mimic
1435 * we have to do this before the def_char_to_objclass check
1437 if (ch == DEF_MIMIC_DEF)
1440 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1441 ret = object_detect((struct obj *) 0, class);
1442 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1443 ret = monster_detect((struct obj *) 0, class);
1444 else if (showsyms[SYM_BOULDER + SYM_OFF_X]
1445 && (ch == showsyms[SYM_BOULDER + SYM_OFF_X]))
1446 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1450 ret = trap_detect((struct obj *) 0);
1453 i = rn2(SIZE(level_detects));
1455 You_see("%s, %s.", level_detects[i].what,
1456 level_distance(level_detects[i].where));
1458 You_see("%s
\82ð%s
\8c©
\82½
\81D", level_detects[i].what,
1459 level_distance(level_detects[i].where));
1466 if (!rn2(100)) /* make them nervous */
1468 You_see("the Wizard of Yendor gazing out at you.");
1470 You("
\83C
\83F
\83\93\83_
\81[
\82Ì
\96\82\96@
\8eg
\82¢
\82ª
\82 \82È
\82½
\82ð
\82É
\82ç
\82ñ
\82Å
\82¢
\82é
\82Ì
\82ð
\8c©
\82½
\81D");
1473 pline_The("vision is unclear.");
1475 pline("
\89f
\91\9c\82Í
\95s
\91N
\96¾
\82É
\82È
\82Á
\82½
\81D");
1489 if (Confusion && rn2(7))
1495 /* Secret corridors are found, but not secret doors. */
1496 if (lev->typ == SCORR) {
1498 unblock_point(x, y);
1502 * Force the real background, then if it's not furniture and there's
1503 * a known trap there, display the trap, else if there was an object
1504 * shown there, redisplay the object. So during mapping, furniture
1505 * takes precedence over traps, which take precedence over objects,
1506 * opposite to how normal vision behaves.
1508 oldglyph = glyph_at(x, y);
1509 if (level.flags.hero_memory) {
1510 magic_map_background(x, y, 0);
1511 newsym(x, y); /* show it, if not blocked */
1513 magic_map_background(x, y, 1); /* display it */
1515 if (!IS_FURNITURE(lev->typ)) {
1516 if ((t = t_at(x, y)) != 0 && t->tseen) {
1518 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1519 show_glyph(x, y, oldglyph);
1520 if (level.flags.hero_memory)
1521 lev->glyph = oldglyph;
1529 register int zx, zy;
1530 boolean unconstrained;
1532 unconstrained = unconstrain_map();
1533 for (zx = 1; zx < COLNO; zx++)
1534 for (zy = 0; zy < ROWNO; zy++)
1535 show_map_spot(zx, zy);
1537 if (!level.flags.hero_memory || unconstrained) {
1538 flush_screen(1); /* flush temp screen */
1539 /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
1541 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1542 "anything of interest");
1544 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1545 "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
1550 exercise(A_WIS, TRUE);
1555 do_vicinity_map(sobj)
1556 struct obj *sobj; /* scroll--actually fake spellbook--object */
1558 register int zx, zy;
1561 long save_EDetect_mons;
1563 boolean unconstrained, refresh = FALSE,
1564 mdetected = FALSE, odetected = FALSE,
1565 /* fake spellbook 'sobj' implies hero has cast the spell;
1566 when book is blessed, casting is skilled or expert level;
1567 if already clairvoyant, non-skilled spell acts like skilled */
1568 extended = (sobj && (sobj->blessed || Clairvoyant));
1569 int newglyph, oldglyph,
1570 lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
1571 hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
1572 lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
1573 hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
1574 ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
1577 * 3.6.0 attempted to emphasize terrain over transient map
1578 * properties (monsters and objects) but that led to problems.
1579 * Notably, known trap would be displayed instead of a monster
1580 * on or in it and then the display remained that way after the
1581 * clairvoyant snapshot finished. That could have been fixed by
1582 * issuing --More-- and then regular vision update, but we want
1583 * to avoid that when having a clairvoyant episode every N turns
1584 * (from donating to a temple priest or by carrying the Amulet).
1585 * Unlike when casting the spell, it is much too intrustive when
1586 * in the midst of walking around or combatting monsters.
1588 * As of 3.6.2, show terrain, then object, then monster like regular
1589 * map updating, except in this case the map locations get marked
1590 * as seen from every direction rather than just from direction of
1591 * hero. Skilled spell marks revealed objects as 'seen up close'
1592 * (but for piles, only the top item) and shows monsters as if
1593 * detected. Non-skilled and timed clairvoyance reveals non-visible
1594 * monsters as 'remembered, unseen'.
1597 /* if hero is engulfed, show engulfer at <u.ux,u.uy> */
1598 save_viz_uyux = viz_array[u.uy][u.ux];
1600 viz_array[u.uy][u.ux] |= IN_SIGHT; /* <x,y> are reversed to [y][x] */
1601 save_EDetect_mons = EDetect_monsters;
1602 /* for skilled spell, getpos() scanning of the map will display all
1603 monsters within range; otherwise, "unseen creature" will be shown */
1604 EDetect_monsters |= I_SPECIAL;
1605 unconstrained = unconstrain_map();
1606 for (zx = lo_x; zx <= hi_x; zx++)
1607 for (zy = lo_y; zy <= hi_y; zy++) {
1608 oldglyph = glyph_at(zx, zy);
1609 /* this will remove 'remembered, unseen mon' (and objects) */
1610 show_map_spot(zx, zy);
1611 /* if there are any objects here, see the top one */
1612 if (OBJ_AT(zx, zy)) {
1613 /* not vobj_at(); this is not vision-based access;
1614 unlike object detection, we don't notice buried items */
1615 otmp = level.objects[zx][zy];
1618 map_object(otmp, TRUE);
1619 newglyph = glyph_at(zx, zy);
1620 /* if otmp is underwater, we'll need to redisplay the water */
1621 if (newglyph != oldglyph && covers_objects(zx, zy))
1624 /* if there is a monster here, see or detect it,
1625 possibly as "remembered, unseen monster" */
1626 if ((mtmp = m_at(zx, zy)) != 0
1627 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
1628 /* if we're going to offer browse_map()/getpos() scanning of
1629 the map and we're not doing extended/blessed clairvoyance
1630 (hence must be swallowed or underwater), show "unseen
1631 creature" unless map already displayed a monster here */
1632 if ((unconstrained || !level.flags.hero_memory)
1633 && !extended && (zx != u.ux || zy != u.uy)
1634 && !glyph_is_monster(oldglyph))
1635 map_invisible(zx, zy);
1637 map_monst(mtmp, FALSE);
1638 newglyph = glyph_at(zx, zy);
1639 if (extended && newglyph != oldglyph
1640 && !glyph_is_invisible(newglyph))
1645 if (!level.flags.hero_memory || unconstrained || mdetected || odetected) {
1646 flush_screen(1); /* flush temp screen */
1647 /* the getpos() prompt from browse_map() is only shown when
1648 flags.verbose is set, but make this unconditional so that
1649 not-verbose users become aware of the prompting situation */
1651 You("sense your surroundings.");
1653 You("
\82Ü
\82í
\82è
\82Ì
\82à
\82Ì
\82ð
\8a´
\92m
\82µ
\82½
\81D");
1654 if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
1657 browse_map(ter_typ, "anything of interest");
1659 browse_map(ter_typ, "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
1663 EDetect_monsters = save_EDetect_mons;
1664 viz_array[u.uy][u.ux] = save_viz_uyux;
1666 /* replace monsters with remembered,unseen monster, then run
1667 see_monsters() to update visible ones and warned-of ones */
1668 for (zx = lo_x; zx <= hi_x; zx++)
1669 for (zy = lo_y; zy <= hi_y; zy++) {
1670 if (zx == u.ux && zy == u.uy)
1672 newglyph = glyph_at(zx, zy);
1673 if (glyph_is_monster(newglyph)
1674 && glyph_to_mon(newglyph) != PM_LONG_WORM_TAIL) {
1675 /* map_invisible() was unconditional here but that made
1676 remembered objects be forgotten for the case where a
1677 monster is immediately redrawn by see_monsters() */
1678 if ((mtmp = m_at(zx, zy)) == 0 || !canspotmon(mtmp))
1679 map_invisible(zx, zy);
1688 /* convert a secret door into a normal door */
1690 cvt_sdoor_to_door(lev)
1693 int newmask = lev->doormask & ~WM_MASK;
1695 if (Is_rogue_level(&u.uz))
1696 /* rogue didn't have doors, only doorways */
1699 /* newly exposed door is closed */
1700 if (!(newmask & D_LOCKED))
1701 newmask |= D_CLOSED;
1704 lev->doormask = newmask;
1707 /* find something at one location; it should find all somethings there
1708 since it is used for magical detection rather than physical searching */
1710 findone(zx, zy, num)
1714 register struct trap *ttmp;
1715 register struct monst *mtmp;
1718 * This used to use if/else-if/else-if/else/end-if but that only
1719 * found the first hidden thing at the location. Two hidden things
1720 * at the same spot is uncommon, but it's possible for an undetected
1721 * monster to be hiding at the location of an unseen trap.
1724 if (levl[zx][zy].typ == SDOOR) {
1725 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1726 magic_map_background(zx, zy, 0);
1729 } else if (levl[zx][zy].typ == SCORR) {
1730 levl[zx][zy].typ = CORR;
1731 unblock_point(zx, zy);
1732 magic_map_background(zx, zy, 0);
1737 if ((ttmp = t_at(zx, zy)) != 0 && !ttmp->tseen
1738 /* [shouldn't successful 'find' reveal and activate statue traps?] */
1739 && ttmp->ttyp != STATUE_TRAP) {
1745 if ((mtmp = m_at(zx, zy)) != 0
1746 /* brings hidden monster out of hiding even if already sensed */
1747 && (!canspotmon(mtmp) || mtmp->mundetected || M_AP_TYPE(mtmp))) {
1748 if (M_AP_TYPE(mtmp)) {
1751 } else if (mtmp->mundetected && (is_hider(mtmp->data)
1752 || hides_under(mtmp->data)
1753 || mtmp->data->mlet == S_EEL)) {
1754 mtmp->mundetected = 0;
1758 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1759 map_invisible(zx, zy);
1760 } else if (unmap_invisible(zx, zy)) {
1766 openone(zx, zy, num)
1770 register struct trap *ttmp;
1771 register struct obj *otmp;
1772 int *num_p = (int *) num;
1774 if (OBJ_AT(zx, zy)) {
1775 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1776 if (Is_box(otmp) && otmp->olocked) {
1781 /* let it fall to the next cases. could be on trap. */
1783 if (levl[zx][zy].typ == SDOOR
1784 || (levl[zx][zy].typ == DOOR
1785 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1786 if (levl[zx][zy].typ == SDOOR)
1787 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1788 if (levl[zx][zy].doormask & D_TRAPPED) {
1789 if (distu(zx, zy) < 3)
1791 b_trapped("door", 0);
1793 b_trapped("
\94à", 0);
1796 Norep("You %s an explosion!",
1797 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1798 : "feel the shock of"));
1800 Norep("
\82 \82È
\82½
\82Í
\94\9a\94%s
\81I",
1801 cansee(zx, zy) ? "
\82ð
\8c©
\82½" : (!Deaf ? "
\89¹
\82ð
\95·
\82¢
\82½"
1802 : "
\82Ì
\8fÕ
\8c\82\82ð
\8a´
\82¶
\82½"));
1804 wake_nearto(zx, zy, 11 * 11);
1805 levl[zx][zy].doormask = D_NODOOR;
1807 levl[zx][zy].doormask = D_ISOPEN;
1808 unblock_point(zx, zy);
1811 } else if (levl[zx][zy].typ == SCORR) {
1812 levl[zx][zy].typ = CORR;
1813 unblock_point(zx, zy);
1816 } else if ((ttmp = t_at(zx, zy)) != 0) {
1818 boolean dummy; /* unneeded "you notice it arg" */
1820 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1825 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1826 if (openholdingtrap(mon, &dummy)
1827 || openfallingtrap(mon, TRUE, &dummy))
1829 } else if (find_drawbridge(&zx, &zy)) {
1830 /* make sure it isn't an open drawbridge */
1831 open_drawbridge(zx, zy);
1836 /* returns number of things found */
1844 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1848 /* returns number of things found and opened */
1855 if (is_animal(u.ustuck->data)) {
1858 pline("Its mouth opens!");
1860 pline("
\89½
\8eÒ
\82©
\82Ì
\8cû
\82ª
\8aJ
\82¢
\82½
\81I");
1863 pline("%s opens its mouth!", Monnam(u.ustuck));
1865 pline("%s
\82Í
\8cû
\82ð
\8aJ
\82¢
\82½
\81I", Monnam(u.ustuck));
1867 expels(u.ustuck, u.ustuck->data, TRUE);
1871 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1875 /* callback hack for overriding vision in do_clear_area() */
1878 void FDECL((*func), (int, int, genericptr_t));
1880 return (func == findone || func == openone);
1887 int tt = what_trap(trap->ttyp, rn2);
1888 boolean cleared = FALSE;
1891 exercise(A_WIS, TRUE);
1892 feel_newsym(trap->tx, trap->ty);
1894 /* The "Hallucination ||" is to preserve 3.6.1 behaviour, but this
1895 behaviour might need a rework in the hallucination case
1896 (e.g. to not prompt if any trap glyph appears on the square). */
1897 if (Hallucination ||
1898 levl[trap->tx][trap->ty].glyph !=
1899 trap_to_glyph(trap, rn2_on_display_rng)) {
1900 /* There's too much clutter to see your find otherwise */
1908 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1910 You("%s
\82ð
\8c©
\82Â
\82¯
\82½
\81D", defsyms[trap_to_defsym(tt)].explanation);
1913 display_nhwindow(WIN_MAP, TRUE); /* wait */
1919 mfind0(mtmp, via_warning)
1921 boolean via_warning;
1923 int x = mtmp->mx, y = mtmp->my;
1924 boolean found_something = FALSE;
1926 if (via_warning && !warning_of(mtmp))
1929 if (M_AP_TYPE(mtmp)) {
1931 found_something = TRUE;
1933 /* this used to only be executed if a !canspotmon() test passed
1934 but that failed to bring sensed monsters out of hiding */
1935 found_something = !canspotmon(mtmp);
1936 if (mtmp->mundetected && (is_hider(mtmp->data)
1937 || hides_under(mtmp->data)
1938 || mtmp->data->mlet == S_EEL)) {
1941 Your("warning senses cause you to take a second %s.",
1942 Blind ? "to check nearby" : "look close by");
1944 Your("
\8cx
\89ú
\90S
\82ª%s
\82µ
\82Î
\82ç
\82
\82©
\82©
\82Á
\82½
\81D",
1945 Blind ? "
\82Ü
\82í
\82è
\82ð
\92²
\82×
\82é
\82Ì
\82É" : "
\8bß
\82
\82ð
\8c©
\82é
\82Ì
\82É");
1947 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1949 mtmp->mundetected = 0;
1950 found_something = TRUE;
1955 if (found_something) {
1956 if (!canspotmon(mtmp) && glyph_is_invisible(levl[x][y].glyph))
1957 return -1; /* Found invisible monster in square which already has
1958 * 'I' in it. Logically, this should still take time
1959 * and lead to `return 1', but if we did that the hero
1960 * would keep finding the same monster every turn. */
1961 exercise(A_WIS, TRUE);
1962 if (!canspotmon(mtmp)) {
1963 map_invisible(x, y);
1965 You_feel("an unseen monster!");
1967 You("
\8c©
\82¦
\82È
\82¢
\89ö
\95¨
\82Ì
\8bC
\94z
\82ð
\8a´
\82¶
\82½
\81I");
1968 } else if (!sensemon(mtmp)) {
1970 You("find %s.", mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1972 You("%s
\82ð
\8c©
\82Â
\82¯
\82½
\81D", mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1982 register int aflag; /* intrinsic autosearch vs explicit searching */
1985 /* Some old versions of gcc seriously muck up nested loops. If you get
1986 * strange crashes while searching in a version compiled with gcc, try
1987 * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
1990 volatile xchar x, y;
1992 register xchar x, y;
1994 register struct trap *trap;
1995 register struct monst *mtmp;
2000 pline("What are you looking for? The exit?");
2002 pline("
\89½
\82ð
\92T
\82·
\82ñ
\82¾
\82¢
\81H
\94ñ
\8fí
\8cû
\81H");
2004 int fund = (uwep && uwep->oartifact
2005 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
2007 if (ublindf && ublindf->otyp == LENSES && !Blind)
2008 fund += 2; /* JDS: lenses help searching */
2011 for (x = u.ux - 1; x < u.ux + 2; x++)
2012 for (y = u.uy - 1; y < u.uy + 2; y++) {
2015 if (x == u.ux && y == u.uy)
2018 if (Blind && !aflag)
2019 feel_location(x, y);
2020 if (levl[x][y].typ == SDOOR) {
2023 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
2024 exercise(A_WIS, TRUE);
2026 feel_location(x, y); /* make sure it shows up */
2028 You("find a hidden door.");
2030 You("
\89B
\82³
\82ê
\82½
\94à
\82ð
\8c©
\82Â
\82¯
\82½
\81D");
2031 } else if (levl[x][y].typ == SCORR) {
2034 levl[x][y].typ = CORR;
2035 unblock_point(x, y); /* vision */
2036 exercise(A_WIS, TRUE);
2038 feel_newsym(x, y); /* make sure it shows up */
2040 You("find a hidden passage.");
2042 You("
\89B
\82³
\82ê
\82½
\92Ê
\98H
\82ð
\8c©
\82Â
\82¯
\82½
\81D");
2044 /* Be careful not to find anything in an SCORR or SDOOR */
2045 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
2046 int mfres = mfind0(mtmp, 0);
2054 /* see if an invisible monster has moved--if Blind,
2055 * feel_location() already did it
2057 if (!aflag && !mtmp && !Blind)
2058 (void) unmap_invisible(x, y);
2060 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
2062 if (trap->ttyp == STATUE_TRAP) {
2063 if (activate_statue_trap(trap, x, y, FALSE))
2064 exercise(A_WIS, TRUE);
2076 /* the 's' command -- explicit searching */
2080 return dosearch0(0);
2089 for (x = u.ux - 1; x <= u.ux + 1; x++)
2090 for (y = u.uy - 1; y <= u.uy + 1; y++) {
2091 if (!isok(x, y) || (x == u.ux && y == u.uy))
2093 if ((mtmp = m_at(x, y)) != 0
2094 && warning_of(mtmp) && mtmp->mundetected)
2095 (void) mfind0(mtmp, 1); /* via_warning */
2099 /* Pre-map the sokoban levels */
2104 register struct trap *ttmp;
2105 register struct obj *obj;
2107 /* Map the background and boulders */
2108 for (x = 1; x < COLNO; x++)
2109 for (y = 0; y < ROWNO; y++) {
2110 levl[x][y].seenv = SVALL;
2111 levl[x][y].waslit = TRUE;
2112 map_background(x, y, 1);
2113 if ((obj = sobj_at(BOULDER, x, y)) != 0)
2118 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
2121 /* set sokoban_rules when there is at least one pit or hole */
2122 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
2128 reveal_terrain_getglyph(x, y, full, swallowed, default_glyph, which_subset)
2131 int default_glyph, which_subset;
2133 int glyph, levl_glyph;
2135 boolean keep_traps = (which_subset & TER_TRP) !=0,
2136 keep_objs = (which_subset & TER_OBJ) != 0,
2137 keep_mons = (which_subset & TER_MON) != 0;
2141 /* for 'full', show the actual terrain for the entire level,
2142 otherwise what the hero remembers for seen locations with
2143 monsters, objects, and/or traps removed as caller dictates */
2144 seenv = (full || level.flags.hero_memory)
2145 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
2147 levl[x][y].seenv = SVALL;
2148 glyph = back_to_glyph(x, y);
2149 levl[x][y].seenv = seenv;
2151 levl_glyph = level.flags.hero_memory
2153 : seenv ? back_to_glyph(x, y): default_glyph;
2154 /* glyph_at() returns the displayed glyph, which might
2155 be a monster. levl[][].glyph contains the remembered
2156 glyph, which will never be a monster (unless it is
2157 the invisible monster glyph, which is handled like
2158 an object, replacing any object or trap at its spot) */
2159 glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
2160 if (keep_mons && x == u.ux && y == u.uy && swallowed)
2161 glyph = mon_to_glyph(u.ustuck, rn2_on_display_rng);
2162 else if (((glyph_is_monster(glyph)
2163 || glyph_is_warning(glyph)) && !keep_mons)
2164 || glyph_is_swallow(glyph))
2166 if (((glyph_is_object(glyph) && !keep_objs)
2167 || glyph_is_invisible(glyph))
2168 && keep_traps && !covers_traps(x, y)) {
2169 if ((t = t_at(x, y)) != 0 && t->tseen)
2170 glyph = trap_to_glyph(t, rn2_on_display_rng);
2172 if ((glyph_is_object(glyph) && !keep_objs)
2173 || (glyph_is_trap(glyph) && !keep_traps)
2174 || glyph_is_invisible(glyph)) {
2176 glyph = default_glyph;
2177 } else if (lastseentyp[x][y] == levl[x][y].typ) {
2178 glyph = back_to_glyph(x, y);
2180 /* look for a mimic here posing as furniture;
2181 if we don't find one, we'll have to fake it */
2182 if ((mtmp = m_at(x, y)) != 0
2183 && M_AP_TYPE(mtmp) == M_AP_FURNITURE) {
2184 glyph = cmap_to_glyph(mtmp->mappearance);
2186 /* we have a topology type but we want a screen
2187 symbol in order to derive a glyph; some screen
2188 symbols need the flags field of levl[][] in
2189 addition to the type (to disambiguate STAIRS to
2190 S_upstair or S_dnstair, for example; current
2191 flags might not be intended for remembered type,
2192 but we've got no other choice) */
2193 schar save_typ = levl[x][y].typ;
2195 levl[x][y].typ = lastseentyp[x][y];
2196 glyph = back_to_glyph(x, y);
2197 levl[x][y].typ = save_typ;
2202 if (glyph == cmap_to_glyph(S_darkroom))
2203 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
2211 int x, y, glyph, skippedrows, lastnonblank;
2212 int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON;
2213 int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
2215 boolean blankrow, toprow;
2218 * Squeeze out excess vertial space when dumping the map.
2219 * If there are any blank map rows at the top, suppress them
2220 * (our caller has already printed a separator). If there is
2221 * more than one blank map row at the bottom, keep just one.
2222 * Any blank rows within the middle of the map are kept.
2223 * Note: putstr() with winid==0 is for dumplog.
2227 for (y = 0; y < ROWNO; y++) {
2228 blankrow = TRUE; /* assume blank until we discover otherwise */
2229 lastnonblank = -1; /* buf[] index rather than map's x */
2230 for (x = 1; x < COLNO; x++) {
2234 glyph = reveal_terrain_getglyph(x, y, FALSE, u.uswallow,
2235 default_glyph, subset);
2236 (void) mapglyph(glyph, &ch, &color, &special, x, y, 0);
2240 lastnonblank = x - 1;
2244 buf[lastnonblank + 1] = '\0';
2249 for (x = 0; x < skippedrows; x++)
2251 putstr(0, 0, buf); /* map row #y */
2260 #endif /* DUMPLOG */
2262 /* idea from crawl; show known portion of map without any monsters,
2263 objects, or traps occluding the view of the underlying terrain */
2265 reveal_terrain(full, which_subset)
2266 int full; /* wizard|explore modes allow player to request full map */
2267 int which_subset; /* when not full, whether to suppress objs and/or traps */
2269 if ((Hallucination || Stunned || Confusion) && !full) {
2271 You("are too disoriented for this.");
2273 You("
\8d¬
\97\90\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\82»
\82ê
\82Í
\82Å
\82«
\82È
\82¢
\81D");
2275 int x, y, glyph, default_glyph;
2277 /* there is a TER_MAP bit too; we always show map regardless of it */
2278 boolean keep_traps = (which_subset & TER_TRP) !=0,
2279 keep_objs = (which_subset & TER_OBJ) != 0,
2280 keep_mons = (which_subset & TER_MON) != 0; /* not used */
2281 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
2283 if (unconstrain_map())
2285 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
2287 for (x = 1; x < COLNO; x++)
2288 for (y = 0; y < ROWNO; y++) {
2289 glyph = reveal_terrain_getglyph(x,y, full, swallowed,
2290 default_glyph, which_subset);
2291 show_glyph(x, y, glyph);
2294 /* hero's location is not highlighted, but getpos() starts with
2295 cursor there, and after moving it anywhere '@' moves it back */
2299 Strcpy(buf, "underlying terrain");
2301 Strcpy(buf, "
\89º
\82É
\82 \82é
\92n
\8c`");
2304 Strcpy(buf, "known terrain");
2306 Strcpy(buf, "
\92m
\82Á
\82Ä
\82¢
\82é
\92n
\8c`");
2309 Sprintf(eos(buf), "%s traps",
2310 (keep_objs || keep_mons) ? "," : " and");
2312 Strcat(buf, "
\82Æã©");
2316 Sprintf(eos(buf), "%s%s objects",
2317 (keep_traps || keep_mons) ? "," : "",
2318 keep_mons ? "" : " and");
2320 Strcat(buf, "
\82Æ
\95¨
\91Ì");
2324 Sprintf(eos(buf), "%s and monsters",
2325 (keep_traps || keep_objs) ? "," : "");
2327 Strcat(buf, "
\82Æ
\89ö
\95¨");
2331 pline("Showing %s only...", buf);
2333 pline("%s
\82¾
\82¯
\82ð
\8c©
\82é
\81D
\81D
\81D", buf);
2335 /* allow player to move cursor around and get autodescribe feedback
2336 based on what is visible now rather than what is on 'real' map */
2337 which_subset |= TER_MAP; /* guarantee non-zero */
2339 browse_map(which_subset, "anything of interest");
2341 browse_map(which_subset, "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
2344 docrt(); /* redraw the screen, restoring regular map */