1 /* NetHack 3.6 detect.c $NHDT-Date: 1544437284 2018/12/10 10:21:24 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.91 $ */
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-2019 */
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 iflags.bouldersym which is a 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 && iflags.bouldersym && sym == iflags.bouldersym)
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 (iflags.bouldersym && (ch == iflags.bouldersym))
1445 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1449 ret = trap_detect((struct obj *) 0);
1452 i = rn2(SIZE(level_detects));
1454 You_see("%s, %s.", level_detects[i].what,
1455 level_distance(level_detects[i].where));
1457 You_see("%s
\82ð%s
\8c©
\82½
\81D", level_detects[i].what,
1458 level_distance(level_detects[i].where));
1465 if (!rn2(100)) /* make them nervous */
1467 You_see("the Wizard of Yendor gazing out at you.");
1469 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");
1472 pline_The("vision is unclear.");
1474 pline("
\89f
\91\9c\82Í
\95s
\91N
\96¾
\82É
\82È
\82Á
\82½
\81D");
1488 if (Confusion && rn2(7))
1494 /* Secret corridors are found, but not secret doors. */
1495 if (lev->typ == SCORR) {
1497 unblock_point(x, y);
1501 * Force the real background, then if it's not furniture and there's
1502 * a known trap there, display the trap, else if there was an object
1503 * shown there, redisplay the object. So during mapping, furniture
1504 * takes precedence over traps, which take precedence over objects,
1505 * opposite to how normal vision behaves.
1507 oldglyph = glyph_at(x, y);
1508 if (level.flags.hero_memory) {
1509 magic_map_background(x, y, 0);
1510 newsym(x, y); /* show it, if not blocked */
1512 magic_map_background(x, y, 1); /* display it */
1514 if (!IS_FURNITURE(lev->typ)) {
1515 if ((t = t_at(x, y)) != 0 && t->tseen) {
1517 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1518 show_glyph(x, y, oldglyph);
1519 if (level.flags.hero_memory)
1520 lev->glyph = oldglyph;
1528 register int zx, zy;
1529 boolean unconstrained;
1531 unconstrained = unconstrain_map();
1532 for (zx = 1; zx < COLNO; zx++)
1533 for (zy = 0; zy < ROWNO; zy++)
1534 show_map_spot(zx, zy);
1536 if (!level.flags.hero_memory || unconstrained) {
1537 flush_screen(1); /* flush temp screen */
1538 /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
1540 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1541 "anything of interest");
1543 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1544 "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
1549 exercise(A_WIS, TRUE);
1554 do_vicinity_map(sobj)
1555 struct obj *sobj; /* scroll--actually fake spellbook--object */
1557 register int zx, zy;
1560 long save_EDetect_mons;
1562 boolean unconstrained, refresh = FALSE,
1563 mdetected = FALSE, odetected = FALSE,
1564 /* fake spellbook 'sobj' implies hero has cast the spell;
1565 when book is blessed, casting is skilled or expert level;
1566 if already clairvoyant, non-skilled spell acts like skilled */
1567 extended = (sobj && (sobj->blessed || Clairvoyant));
1568 int newglyph, oldglyph,
1569 lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
1570 hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
1571 lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
1572 hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
1573 ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
1576 * 3.6.0 attempted to emphasize terrain over transient map
1577 * properties (monsters and objects) but that led to problems.
1578 * Notably, known trap would be displayed instead of a monster
1579 * on or in it and then the display remained that way after the
1580 * clairvoyant snapshot finished. That could have been fixed by
1581 * issuing --More-- and then regular vision update, but we want
1582 * to avoid that when having a clairvoyant episode every N turns
1583 * (from donating to a temple priest or by carrying the Amulet).
1584 * Unlike when casting the spell, it is much too intrustive when
1585 * in the midst of walking around or combatting monsters.
1587 * For 3.6.2, show terrain, then object, then monster like regular
1588 * map updating, except in this case the map locations get marked
1589 * as seen from every direction rather than just from direction of
1590 * hero. Skilled spell marks revealed objects as 'seen up close'
1591 * (but for piles, only the top item) and shows monsters as if
1592 * detected. Non-skilled and timed clairvoyance reveals non-visible
1593 * monsters as 'remembered, unseen'.
1596 /* if hero is engulfed, show engulfer at <u.ux,u.uy> */
1597 save_viz_uyux = viz_array[u.uy][u.ux];
1599 viz_array[u.uy][u.ux] |= IN_SIGHT; /* <x,y> are reversed to [y][x] */
1600 save_EDetect_mons = EDetect_monsters;
1601 /* for skilled spell, getpos() scanning of the map will display all
1602 monsters within range; otherwise, "unseen creature" will be shown */
1603 EDetect_monsters |= I_SPECIAL;
1604 unconstrained = unconstrain_map();
1605 for (zx = lo_x; zx <= hi_x; zx++)
1606 for (zy = lo_y; zy <= hi_y; zy++) {
1607 oldglyph = glyph_at(zx, zy);
1608 /* this will remove 'remembered, unseen mon' (and objects) */
1609 show_map_spot(zx, zy);
1610 /* if there are any objects here, see the top one */
1611 if (OBJ_AT(zx, zy)) {
1612 /* not vobj_at(); this is not vision-based access;
1613 unlike object detection, we don't notice buried items */
1614 otmp = level.objects[zx][zy];
1617 map_object(otmp, TRUE);
1618 newglyph = glyph_at(zx, zy);
1619 /* if otmp is underwater, we'll need to redisplay the water */
1620 if (newglyph != oldglyph && covers_objects(zx, zy))
1623 /* if there is a monster here, see or detect it,
1624 possibly as "remembered, unseen monster" */
1625 if ((mtmp = m_at(zx, zy)) != 0
1626 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
1627 /* if we're going to offer browse_map()/getpos() scanning of
1628 the map and we're not doing extended/blessed clairvoyance
1629 (hence must be swallowed or underwater), show "unseen
1630 creature" unless map already displayed a monster here */
1631 if ((unconstrained || !level.flags.hero_memory)
1632 && !extended && (zx != u.ux || zy != u.uy)
1633 && !glyph_is_monster(oldglyph))
1634 map_invisible(zx, zy);
1636 map_monst(mtmp, FALSE);
1637 newglyph = glyph_at(zx, zy);
1638 if (extended && newglyph != oldglyph
1639 && !glyph_is_invisible(newglyph))
1644 if (!level.flags.hero_memory || unconstrained || mdetected || odetected) {
1645 flush_screen(1); /* flush temp screen */
1646 /* the getpos() prompt from browse_map() is only shown when
1647 flags.verbose is set, but make this unconditional so that
1648 not-verbose users become aware of the prompting situation */
1650 You("sense your surroundings.");
1652 You("
\82Ü
\82í
\82è
\82Ì
\82à
\82Ì
\82ð
\8a´
\92m
\82µ
\82½
\81D");
1653 if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
1656 browse_map(ter_typ, "anything of interest");
1658 browse_map(ter_typ, "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
1662 EDetect_monsters = save_EDetect_mons;
1663 viz_array[u.uy][u.ux] = save_viz_uyux;
1665 /* replace monsters with remembered,unseen monster, then run
1666 see_monsters() to update visible ones and warned-of ones */
1667 for (zx = lo_x; zx <= hi_x; zx++)
1668 for (zy = lo_y; zy <= hi_y; zy++) {
1669 if (zx == u.ux && zy == u.uy)
1671 newglyph = glyph_at(zx, zy);
1672 if (glyph_is_monster(newglyph)
1673 && glyph_to_mon(newglyph) != PM_LONG_WORM_TAIL)
1674 map_invisible(zx, zy);
1682 /* convert a secret door into a normal door */
1684 cvt_sdoor_to_door(lev)
1687 int newmask = lev->doormask & ~WM_MASK;
1689 if (Is_rogue_level(&u.uz))
1690 /* rogue didn't have doors, only doorways */
1693 /* newly exposed door is closed */
1694 if (!(newmask & D_LOCKED))
1695 newmask |= D_CLOSED;
1698 lev->doormask = newmask;
1702 findone(zx, zy, num)
1706 register struct trap *ttmp;
1707 register struct monst *mtmp;
1709 if (levl[zx][zy].typ == SDOOR) {
1710 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1711 magic_map_background(zx, zy, 0);
1714 } else if (levl[zx][zy].typ == SCORR) {
1715 levl[zx][zy].typ = CORR;
1716 unblock_point(zx, zy);
1717 magic_map_background(zx, zy, 0);
1720 } else if ((ttmp = t_at(zx, zy)) != 0) {
1721 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1726 } else if ((mtmp = m_at(zx, zy)) != 0) {
1727 if (M_AP_TYPE(mtmp)) {
1731 if (mtmp->mundetected
1732 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1733 mtmp->mundetected = 0;
1737 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1738 map_invisible(zx, zy);
1739 } else if (unmap_invisible(zx, zy)) {
1745 openone(zx, zy, num)
1749 register struct trap *ttmp;
1750 register struct obj *otmp;
1751 int *num_p = (int *) num;
1753 if (OBJ_AT(zx, zy)) {
1754 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1755 if (Is_box(otmp) && otmp->olocked) {
1760 /* let it fall to the next cases. could be on trap. */
1762 if (levl[zx][zy].typ == SDOOR
1763 || (levl[zx][zy].typ == DOOR
1764 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1765 if (levl[zx][zy].typ == SDOOR)
1766 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1767 if (levl[zx][zy].doormask & D_TRAPPED) {
1768 if (distu(zx, zy) < 3)
1770 b_trapped("door", 0);
1772 b_trapped("
\94à", 0);
1775 Norep("You %s an explosion!",
1776 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1777 : "feel the shock of"));
1779 Norep("
\82 \82È
\82½
\82Í
\94\9a\94%s
\81I",
1780 cansee(zx, zy) ? "
\82ð
\8c©
\82½" : (!Deaf ? "
\89¹
\82ð
\95·
\82¢
\82½"
1781 : "
\82Ì
\8fÕ
\8c\82\82ð
\8a´
\82¶
\82½"));
1783 wake_nearto(zx, zy, 11 * 11);
1784 levl[zx][zy].doormask = D_NODOOR;
1786 levl[zx][zy].doormask = D_ISOPEN;
1787 unblock_point(zx, zy);
1790 } else if (levl[zx][zy].typ == SCORR) {
1791 levl[zx][zy].typ = CORR;
1792 unblock_point(zx, zy);
1795 } else if ((ttmp = t_at(zx, zy)) != 0) {
1797 boolean dummy; /* unneeded "you notice it arg" */
1799 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1804 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1805 if (openholdingtrap(mon, &dummy)
1806 || openfallingtrap(mon, TRUE, &dummy))
1808 } else if (find_drawbridge(&zx, &zy)) {
1809 /* make sure it isn't an open drawbridge */
1810 open_drawbridge(zx, zy);
1815 /* returns number of things found */
1823 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1827 /* returns number of things found and opened */
1834 if (is_animal(u.ustuck->data)) {
1837 pline("Its mouth opens!");
1839 pline("
\89½
\8eÒ
\82©
\82Ì
\8cû
\82ª
\8aJ
\82¢
\82½
\81I");
1842 pline("%s opens its mouth!", Monnam(u.ustuck));
1844 pline("%s
\82Í
\8cû
\82ð
\8aJ
\82¢
\82½
\81I", Monnam(u.ustuck));
1846 expels(u.ustuck, u.ustuck->data, TRUE);
1850 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1854 /* callback hack for overriding vision in do_clear_area() */
1857 void FDECL((*func), (int, int, genericptr_t));
1859 return (func == findone || func == openone);
1866 int tt = what_trap(trap->ttyp, rn2);
1867 boolean cleared = FALSE;
1870 exercise(A_WIS, TRUE);
1871 feel_newsym(trap->tx, trap->ty);
1873 /* The "Hallucination ||" is to preserve 3.6.1 behaviour, but this
1874 behaviour might need a rework in the hallucination case
1875 (e.g. to not prompt if any trap glyph appears on the
1877 if (Hallucination ||
1878 levl[trap->tx][trap->ty].glyph !=
1879 trap_to_glyph(trap, rn2_on_display_rng)) {
1880 /* There's too much clutter to see your find otherwise */
1888 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1890 You("%s
\82ð
\8c©
\82Â
\82¯
\82½
\81D", defsyms[trap_to_defsym(tt)].explanation);
1893 display_nhwindow(WIN_MAP, TRUE); /* wait */
1899 mfind0(mtmp, via_warning)
1901 boolean via_warning;
1903 int x = mtmp->mx, y = mtmp->my;
1904 boolean found_something = FALSE;
1906 if (via_warning && !warning_of(mtmp))
1909 if (M_AP_TYPE(mtmp)) {
1911 found_something = TRUE;
1912 } else if (!canspotmon(mtmp)) {
1913 if (mtmp->mundetected
1914 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1917 Your("warning senses cause you to take a second %s.",
1918 Blind ? "to check nearby" : "look close by");
1920 Your("
\8cx
\89ú
\90S
\82ª%s
\82µ
\82Î
\82ç
\82
\82©
\82©
\82Á
\82½
\81D",
1921 Blind ? "
\82Ü
\82í
\82è
\82ð
\92²
\82×
\82é
\82Ì
\82É" : "
\8bß
\82
\82ð
\8c©
\82é
\82Ì
\82É");
1923 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1925 mtmp->mundetected = 0;
1928 found_something = TRUE;
1931 if (found_something) {
1932 if (!canspotmon(mtmp) && glyph_is_invisible(levl[x][y].glyph))
1933 return -1; /* Found invisible monster in square which already has
1934 * 'I' in it. Logically, this should still take time
1935 * and lead to `return 1', but if we did that the hero
1936 * would keep finding the same monster every turn. */
1937 exercise(A_WIS, TRUE);
1938 if (!canspotmon(mtmp)) {
1939 map_invisible(x, y);
1941 You_feel("an unseen monster!");
1943 You("
\8c©
\82¦
\82È
\82¢
\89ö
\95¨
\82Ì
\8bC
\94z
\82ð
\8a´
\82¶
\82½
\81I");
1944 } else if (!sensemon(mtmp)) {
1946 You("find %s.", mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1948 You("%s
\82ð
\8c©
\82Â
\82¯
\82½
\81D", mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1958 register int aflag; /* intrinsic autosearch vs explicit searching */
1961 /* Some old versions of gcc seriously muck up nested loops. If you get
1962 * strange crashes while searching in a version compiled with gcc, try
1963 * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
1966 volatile xchar x, y;
1968 register xchar x, y;
1970 register struct trap *trap;
1971 register struct monst *mtmp;
1976 pline("What are you looking for? The exit?");
1978 pline("
\89½
\82ð
\92T
\82·
\82ñ
\82¾
\82¢
\81H
\94ñ
\8fí
\8cû
\81H");
1980 int fund = (uwep && uwep->oartifact
1981 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1983 if (ublindf && ublindf->otyp == LENSES && !Blind)
1984 fund += 2; /* JDS: lenses help searching */
1987 for (x = u.ux - 1; x < u.ux + 2; x++)
1988 for (y = u.uy - 1; y < u.uy + 2; y++) {
1991 if (x == u.ux && y == u.uy)
1994 if (Blind && !aflag)
1995 feel_location(x, y);
1996 if (levl[x][y].typ == SDOOR) {
1999 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
2000 exercise(A_WIS, TRUE);
2002 feel_location(x, y); /* make sure it shows up */
2004 You("find a hidden door.");
2006 You("
\89B
\82³
\82ê
\82½
\94à
\82ð
\8c©
\82Â
\82¯
\82½
\81D");
2007 } else if (levl[x][y].typ == SCORR) {
2010 levl[x][y].typ = CORR;
2011 unblock_point(x, y); /* vision */
2012 exercise(A_WIS, TRUE);
2014 feel_newsym(x, y); /* make sure it shows up */
2016 You("find a hidden passage.");
2018 You("
\89B
\82³
\82ê
\82½
\92Ê
\98H
\82ð
\8c©
\82Â
\82¯
\82½
\81D");
2020 /* Be careful not to find anything in an SCORR or SDOOR */
2021 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
2022 int mfres = mfind0(mtmp, 0);
2030 /* see if an invisible monster has moved--if Blind,
2031 * feel_location() already did it
2033 if (!aflag && !mtmp && !Blind)
2034 (void) unmap_invisible(x, y);
2036 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
2038 if (trap->ttyp == STATUE_TRAP) {
2039 if (activate_statue_trap(trap, x, y, FALSE))
2040 exercise(A_WIS, TRUE);
2052 /* the 's' command -- explicit searching */
2056 return dosearch0(0);
2065 for (x = u.ux - 1; x <= u.ux + 1; x++)
2066 for (y = u.uy - 1; y <= u.uy + 1; y++) {
2067 if (!isok(x, y) || (x == u.ux && y == u.uy))
2069 if ((mtmp = m_at(x, y)) != 0
2070 && warning_of(mtmp) && mtmp->mundetected)
2071 (void) mfind0(mtmp, 1); /* via_warning */
2075 /* Pre-map the sokoban levels */
2080 register struct trap *ttmp;
2081 register struct obj *obj;
2083 /* Map the background and boulders */
2084 for (x = 1; x < COLNO; x++)
2085 for (y = 0; y < ROWNO; y++) {
2086 levl[x][y].seenv = SVALL;
2087 levl[x][y].waslit = TRUE;
2088 map_background(x, y, 1);
2089 if ((obj = sobj_at(BOULDER, x, y)) != 0)
2094 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
2097 /* set sokoban_rules when there is at least one pit or hole */
2098 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
2104 reveal_terrain_getglyph(x, y, full, swallowed, default_glyph, which_subset)
2107 int default_glyph, which_subset;
2109 int glyph, levl_glyph;
2111 boolean keep_traps = (which_subset & TER_TRP) !=0,
2112 keep_objs = (which_subset & TER_OBJ) != 0,
2113 keep_mons = (which_subset & TER_MON) != 0;
2117 /* for 'full', show the actual terrain for the entire level,
2118 otherwise what the hero remembers for seen locations with
2119 monsters, objects, and/or traps removed as caller dictates */
2120 seenv = (full || level.flags.hero_memory)
2121 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
2123 levl[x][y].seenv = SVALL;
2124 glyph = back_to_glyph(x, y);
2125 levl[x][y].seenv = seenv;
2127 levl_glyph = level.flags.hero_memory
2129 : seenv ? back_to_glyph(x, y): default_glyph;
2130 /* glyph_at() returns the displayed glyph, which might
2131 be a monster. levl[][].glyph contains the remembered
2132 glyph, which will never be a monster (unless it is
2133 the invisible monster glyph, which is handled like
2134 an object, replacing any object or trap at its spot) */
2135 glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
2136 if (keep_mons && x == u.ux && y == u.uy && swallowed)
2137 glyph = mon_to_glyph(u.ustuck, rn2_on_display_rng);
2138 else if (((glyph_is_monster(glyph)
2139 || glyph_is_warning(glyph)) && !keep_mons)
2140 || glyph_is_swallow(glyph))
2142 if (((glyph_is_object(glyph) && !keep_objs)
2143 || glyph_is_invisible(glyph))
2144 && keep_traps && !covers_traps(x, y)) {
2145 if ((t = t_at(x, y)) != 0 && t->tseen)
2146 glyph = trap_to_glyph(t, rn2_on_display_rng);
2148 if ((glyph_is_object(glyph) && !keep_objs)
2149 || (glyph_is_trap(glyph) && !keep_traps)
2150 || glyph_is_invisible(glyph)) {
2152 glyph = default_glyph;
2153 } else if (lastseentyp[x][y] == levl[x][y].typ) {
2154 glyph = back_to_glyph(x, y);
2156 /* look for a mimic here posing as furniture;
2157 if we don't find one, we'll have to fake it */
2158 if ((mtmp = m_at(x, y)) != 0
2159 && M_AP_TYPE(mtmp) == M_AP_FURNITURE) {
2160 glyph = cmap_to_glyph(mtmp->mappearance);
2162 /* we have a topology type but we want a screen
2163 symbol in order to derive a glyph; some screen
2164 symbols need the flags field of levl[][] in
2165 addition to the type (to disambiguate STAIRS to
2166 S_upstair or S_dnstair, for example; current
2167 flags might not be intended for remembered type,
2168 but we've got no other choice) */
2169 schar save_typ = levl[x][y].typ;
2171 levl[x][y].typ = lastseentyp[x][y];
2172 glyph = back_to_glyph(x, y);
2173 levl[x][y].typ = save_typ;
2178 if (glyph == cmap_to_glyph(S_darkroom))
2179 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
2187 int x, y, glyph, skippedrows, lastnonblank;
2188 int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON;
2189 int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
2191 boolean blankrow, toprow;
2194 * Squeeze out excess vertial space when dumping the map.
2195 * If there are any blank map rows at the top, suppress them
2196 * (our caller has already printed a separator). If there is
2197 * more than one blank map row at the bottom, keep just one.
2198 * Any blank rows within the middle of the map are kept.
2199 * Note: putstr() with winid==0 is for dumplog.
2203 for (y = 0; y < ROWNO; y++) {
2204 blankrow = TRUE; /* assume blank until we discover otherwise */
2205 lastnonblank = -1; /* buf[] index rather than map's x */
2206 for (x = 1; x < COLNO; x++) {
2210 glyph = reveal_terrain_getglyph(x, y, FALSE, u.uswallow,
2211 default_glyph, subset);
2212 (void) mapglyph(glyph, &ch, &color, &special, x, y);
2216 lastnonblank = x - 1;
2220 buf[lastnonblank + 1] = '\0';
2225 for (x = 0; x < skippedrows; x++)
2227 putstr(0, 0, buf); /* map row #y */
2236 #endif /* DUMPLOG */
2238 /* idea from crawl; show known portion of map without any monsters,
2239 objects, or traps occluding the view of the underlying terrain */
2241 reveal_terrain(full, which_subset)
2242 int full; /* wizard|explore modes allow player to request full map */
2243 int which_subset; /* when not full, whether to suppress objs and/or traps */
2245 if ((Hallucination || Stunned || Confusion) && !full) {
2247 You("are too disoriented for this.");
2249 You("
\8d¬
\97\90\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\82»
\82ê
\82Í
\82Å
\82«
\82È
\82¢
\81D");
2251 int x, y, glyph, default_glyph;
2253 /* there is a TER_MAP bit too; we always show map regardless of it */
2254 boolean keep_traps = (which_subset & TER_TRP) !=0,
2255 keep_objs = (which_subset & TER_OBJ) != 0,
2256 keep_mons = (which_subset & TER_MON) != 0; /* not used */
2257 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
2259 if (unconstrain_map())
2261 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
2263 for (x = 1; x < COLNO; x++)
2264 for (y = 0; y < ROWNO; y++) {
2265 glyph = reveal_terrain_getglyph(x,y, full, swallowed,
2266 default_glyph, which_subset);
2267 show_glyph(x, y, glyph);
2270 /* hero's location is not highlighted, but getpos() starts with
2271 cursor there, and after moving it anywhere '@' moves it back */
2275 Strcpy(buf, "underlying terrain");
2277 Strcpy(buf, "
\89º
\82É
\82 \82é
\92n
\8c`");
2280 Strcpy(buf, "known terrain");
2282 Strcpy(buf, "
\92m
\82Á
\82Ä
\82¢
\82é
\92n
\8c`");
2285 Sprintf(eos(buf), "%s traps",
2286 (keep_objs || keep_mons) ? "," : " and");
2288 Strcat(buf, "
\82Æã©");
2292 Sprintf(eos(buf), "%s%s objects",
2293 (keep_traps || keep_mons) ? "," : "",
2294 keep_mons ? "" : " and");
2296 Strcat(buf, "
\82Æ
\95¨
\91Ì");
2300 Sprintf(eos(buf), "%s and monsters",
2301 (keep_traps || keep_objs) ? "," : "");
2303 Strcat(buf, "
\82Æ
\89ö
\95¨");
2307 pline("Showing %s only...", buf);
2309 pline("%s
\82¾
\82¯
\82ð
\8c©
\82é
\81D
\81D
\81D", buf);
2311 /* allow player to move cursor around and get autodescribe feedback
2312 based on what is visible now rather than what is on 'real' map */
2313 which_subset |= TER_MAP; /* guarantee non-zero */
2314 browse_map(which_subset, "anything of interest");
2317 docrt(); /* redraw the screen, restoring regular map */