1 /* NetHack 3.6 detect.c $NHDT-Date: 1522891623 2018/04/05 01:27:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */
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-2018 */
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, detected_mon_to_glyph(mtmp));
88 show_glyph(mtmp->mx, mtmp->my,
89 mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
91 if (showtail && mtmp->data == &mons[PM_LONG_WORM])
92 detect_wsegs(mtmp, 0);
95 /* this is checking whether a trap symbol represents a trapped chest,
96 not whether a trapped chest is actually present */
98 trapped_chest_at(ttyp, x, y)
105 if (!glyph_is_trap(glyph_at(x, y)))
107 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
111 * TODO? We should check containers recursively like the trap
112 * detecting routine does. Chests and large boxes do not nest in
113 * themselves or each other, but could be contained inside statues.
115 * For farlook, we should also check for buried containers, but
116 * for '^' command to examine adjacent trap glyph, we shouldn't.
119 /* on map, presence of any trappable container will do */
120 if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y))
122 /* in inventory, we need to find one which is actually trapped */
123 if (x == u.ux && y == u.uy) {
124 for (otmp = invent; otmp; otmp = otmp->nobj)
125 if (Is_box(otmp) && otmp->otrapped)
127 if (u.usteed) { /* steed isn't on map so won't be found by m_at() */
128 for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj)
129 if (Is_box(otmp) && otmp->otrapped)
133 if ((mtmp = m_at(x, y)) != 0)
134 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
135 if (Is_box(otmp) && otmp->otrapped)
140 /* this is checking whether a trap symbol represents a trapped door,
141 not whether the door here is actually trapped */
143 trapped_door_at(ttyp, x, y)
149 if (!glyph_is_trap(glyph_at(x, y)))
151 if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
154 if (!IS_DOOR(lev->typ))
156 if ((lev->doormask & (D_NODOOR | D_BROKEN | D_ISOPEN)) != 0
157 && trapped_chest_at(ttyp, x, y))
162 /* recursively search obj for an object in class oclass, return 1st found */
168 register struct obj *otmp;
171 if (obj->oclass == oclass)
174 if (Has_contents(obj)) {
175 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
176 if (otmp->oclass == oclass)
178 else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)) != 0)
181 return (struct obj *) 0;
184 /* Recursively search obj for an object made of specified material.
185 * Return first found.
188 o_material(obj, material)
192 register struct obj *otmp;
195 if (objects[obj->otyp].oc_material == material)
198 if (Has_contents(obj)) {
199 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
200 if (objects[otmp->otyp].oc_material == material)
202 else if (Has_contents(otmp)
203 && (temp = o_material(otmp, material)) != 0)
206 return (struct obj *) 0;
216 if (Has_contents(obj)) {
217 for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
222 /* Check whether the location has an outdated object displayed on it. */
224 check_map_spot(x, y, oclass, material)
230 register struct obj *otmp;
231 register struct monst *mtmp;
233 glyph = glyph_at(x, y);
234 if (glyph_is_object(glyph)) {
235 /* there's some object shown here */
236 if (oclass == ALL_CLASSES) {
237 return (boolean) !(level.objects[x][y] /* stale if nothing here */
238 || ((mtmp = m_at(x, y)) != 0 && mtmp->minvent));
241 && objects[glyph_to_obj(glyph)].oc_material == material) {
242 /* object shown here is of interest because material matches */
243 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
244 if (o_material(otmp, GOLD))
246 /* didn't find it; perhaps a monster is carrying it */
247 if ((mtmp = m_at(x, y)) != 0) {
248 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
249 if (o_material(otmp, GOLD))
252 /* detection indicates removal of this object from the map */
255 if (oclass && objects[glyph_to_obj(glyph)].oc_class == oclass) {
256 /* obj shown here is of interest because its class matches */
257 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
258 if (o_in(otmp, oclass))
260 /* didn't find it; perhaps a monster is carrying it */
261 if ((mtmp = m_at(x, y)) != 0) {
262 for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
263 if (o_in(otmp, oclass))
266 /* detection indicates removal of this object from the map */
275 * When doing detection, remove stale data from the map display (corpses
276 * rotted away, objects carried away by monsters, etc) so that it won't
277 * reappear after the detection has completed. Return true if noticeable
281 clear_stale_map(oclass, material)
286 boolean change_made = FALSE;
288 for (zx = 1; zx < COLNO; zx++)
289 for (zy = 0; zy < ROWNO; zy++)
290 if (check_map_spot(zx, zy, oclass, material)) {
291 unmap_object(zx, zy);
298 /* look for gold, on the floor or in monsters' possession */
301 register struct obj *sobj;
303 register struct obj *obj;
304 register struct monst *mtmp;
305 struct obj gold, *temp = 0;
306 boolean stale, ugold = FALSE, steedgold = FALSE;
307 int ter_typ = TER_DETECT | TER_OBJ;
309 known = stale = clear_stale_map(COIN_CLASS,
310 (unsigned) (sobj->blessed ? GOLD : 0));
312 /* look for gold carried by monsters (might be in a container) */
313 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
314 if (DEADMONSTER(mtmp))
315 continue; /* probably not needed in this case but... */
316 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
317 if (mtmp == u.usteed) {
321 goto outgoldmap; /* skip further searching */
324 for (obj = mtmp->minvent; obj; obj = obj->nobj)
325 if ((sobj->blessed && o_material(obj, GOLD))
326 || o_in(obj, COIN_CLASS)) {
327 if (mtmp == u.usteed) {
331 goto outgoldmap; /* skip further searching */
337 /* look for gold objects */
338 for (obj = fobj; obj; obj = obj->nobj) {
339 if (sobj->blessed && o_material(obj, GOLD)) {
341 if (obj->ox != u.ux || obj->oy != u.uy)
343 } else if (o_in(obj, COIN_CLASS)) {
345 if (obj->ox != u.ux || obj->oy != u.uy)
351 /* no gold found on floor or monster's inventory.
352 adjust message if you have gold in your inventory */
356 if (youmonst.data == &mons[PM_GOLD_GOLEM])
358 Sprintf(buf, "You feel like a million %s!", currency(2L));
360 Strcpy(buf, "
\82 \82È
\82½
\82Í
\8bà
\8e\9d\82¿
\82É
\82È
\82Á
\82½
\82æ
\82¤
\82É
\8a´
\82¶
\82½
\81I");
361 else if (money_cnt(invent) || hidden_gold())
364 "You feel worried about your future financial situation.");
366 "
\82 \82È
\82½
\82Í
\8f«
\97\88\82Ì
\8co
\8dÏ
\8fó
\8bµ
\82ª
\90S
\94z
\82É
\82È
\82Á
\82½
\81D");
369 Sprintf(buf, "You feel interested in %s financial situation.",
370 s_suffix(x_monnam(u.usteed,
371 u.usteed->mtame ? ARTICLE_YOUR
374 SUPPRESS_SADDLE, FALSE)));
376 Sprintf(buf, "
\82 \82È
\82½
\82Í%s
\82Ì
\8co
\8dÏ
\8fó
\8bµ
\82É
\8b»
\96¡
\82ª
\8fo
\82Ä
\82«
\82½
\81D",
378 u.usteed->mtame ? ARTICLE_YOUR
381 SUPPRESS_SADDLE, FALSE));
385 Strcpy(buf, "You feel materially poor.");
387 Strcpy(buf, "
\82 \82È
\82½
\82Í
\82Ð
\82à
\82¶
\82³
\82ð
\8a´
\82¶
\82½
\81D");
389 strange_feeling(sobj, buf);
393 /* only under me - no separate display required */
397 You("notice some gold between your %s.", makeplural(body_part(FOOT)));
399 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));
405 (void) unconstrain_map();
406 /* Discover gold locations. */
407 for (obj = fobj; obj; obj = obj->nobj) {
408 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
414 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
421 if (temp && temp->ox == u.ux && temp->oy == u.uy)
424 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
425 if (DEADMONSTER(mtmp))
426 continue; /* probably overkill here */
428 if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
429 gold = zeroobj; /* ensure oextra is cleared too */
430 gold.otyp = GOLD_PIECE;
431 gold.quan = (long) rnd(10); /* usually more than 1 */
434 map_object(&gold, 1);
437 for (obj = mtmp->minvent; obj; obj = obj->nobj)
438 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
443 } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
450 if (temp && temp->ox == u.ux && temp->oy == u.uy)
455 ter_typ |= TER_MON; /* so autodescribe will recognize hero */
458 You_feel("very greedy, and sense gold!");
460 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");
461 exercise(A_WIS, TRUE);
464 browse_map(ter_typ, "gold");
466 browse_map(ter_typ, "
\8bà");
477 /* returns 1 if nothing was detected */
478 /* returns 0 if something was detected */
481 register struct obj *sobj;
483 register struct obj *obj;
484 register struct monst *mtmp;
485 register int ct = 0, ctu = 0;
486 boolean confused = (Confusion || (sobj && sobj->cursed)), stale;
487 char oclass = confused ? POTION_CLASS : FOOD_CLASS;
489 const char *what = confused ? something : "food";
491 const char *what = confused ? "
\83n
\83\89\83w
\83\8a" : "
\90H
\82×
\95¨";
493 stale = clear_stale_map(oclass, 0);
494 if (u.usteed) /* some situations leave steed with stale coordinates */
495 u.usteed->mx = u.ux, u.usteed->my = u.uy;
497 for (obj = fobj; obj; obj = obj->nobj)
498 if (o_in(obj, oclass)) {
499 if (obj->ox == u.ux && obj->oy == u.uy)
504 for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) {
505 /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */
506 for (obj = mtmp->minvent; obj; obj = obj->nobj)
507 if (o_in(obj, oclass)) {
508 if (mtmp->mx == u.ux && mtmp->my == u.uy)
509 ctu++; /* steed or an engulfer with inventory */
517 known = stale && !confused;
521 You("sense a lack of %s nearby.", what);
523 You("%s
\82ª
\8c¸
\82Á
\82Ä
\82¢
\82é
\82Ì
\82É
\8bC
\82ª
\82Â
\82¢
\82½
\81D",what);
524 if (sobj && sobj->blessed) {
527 Your("%s starts to tingle.", body_part(NOSE));
529 Your("%s
\82ª
\82Ò
\82
\82Á
\82Æ
\93®
\82¢
\82½
\81D", body_part(NOSE));
536 Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
537 (sobj->blessed && !u.uedibility)
538 ? " then starts to tingle"
541 Sprintf(buf, "
\82 \82È
\82½
\82Ì%s
\82ª
\82Ð
\82
\82Ð
\82
\82Æ
\93®
\82¢%s
\81D", body_part(NOSE),
542 (sobj->blessed && !u.uedibility)
543 ? "
\82Ä
\81C
\82¤
\82¸
\82¤
\82¸
\82µ
\82¾
\82µ
\82½"
546 if (sobj->blessed && !u.uedibility) {
547 boolean savebeginner = flags.beginner;
549 flags.beginner = FALSE; /* prevent non-delivery of message */
550 strange_feeling(sobj, buf);
551 flags.beginner = savebeginner;
554 strange_feeling(sobj, buf);
560 You("%s %s nearby.", sobj ? "smell" : "sense", what);
562 You("
\8bß
\82
\82Å%s%s
\81D", what, sobj ? "
\82Ì
\82É
\82¨
\82¢
\82ð
\82©
\82¢
\82¾" : "
\82ð
\8a´
\92m
\82µ
\82½");
563 if (sobj && sobj->blessed) {
566 pline("Your %s starts to tingle.", body_part(NOSE));
568 pline("
\82 \82È
\82½
\82Ì%s
\82ª
\82¤
\82¸
\82¤
\82¸
\82µ
\82¾
\82µ
\82½
\81D", body_part(NOSE));
573 int ter_typ = TER_DETECT | TER_OBJ;
577 (void) unconstrain_map();
578 for (obj = fobj; obj; obj = obj->nobj)
579 if ((temp = o_in(obj, oclass)) != 0) {
586 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
587 /* no DEADMONSTER() check needed -- dmons never have inventory */
588 for (obj = mtmp->minvent; obj; obj = obj->nobj)
589 if ((temp = o_in(obj, oclass)) != 0) {
593 break; /* skip rest of this monster's inventory */
597 ter_typ |= TER_MON; /* for autodescribe of self */
602 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
603 u.uedibility ? "continues" : "starts", what);
605 Your("%s
\82Í
\82¤
\82¸
\82¤
\82¸
\82µ%s
\81C%s
\82Ì
\93õ
\82¢
\82ð
\9ak
\82¬
\82Æ
\82Á
\82½
\81D", body_part(NOSE),
606 u.uedibility ? "
\91±
\82¯" : "
\8en
\82ß", what);
611 Your("%s tingles and you smell %s.", body_part(NOSE), what);
613 Your("%s
\82Í
\82¤
\82¸
\82¤
\82¸
\82µ
\81C%s
\82Ì
\93õ
\82¢
\82ð
\9ak
\82¬
\82Æ
\82Á
\82½
\81D", body_part(NOSE), what);
616 You("sense %s.", what);
618 You("%s
\82ð
\8a´
\92m
\82µ
\82½
\81D", what);
619 exercise(A_WIS, TRUE);
622 browse_map(ter_typ, "food");
624 browse_map(ter_typ, "
\90H
\97¿");
637 * Used for scrolls, potions, spells, and crystal balls. Returns:
639 * 1 - nothing was detected
640 * 0 - something was detected
643 object_detect(detector, class)
644 struct obj *detector; /* object doing the detecting */
645 int class; /* an object class, 0 for all */
649 int is_cursed = (detector && detector->cursed);
650 int do_dknown = (detector && (detector->oclass == POTION_CLASS
651 || detector->oclass == SPBOOK_CLASS)
652 && detector->blessed);
654 register struct obj *obj, *otmp = (struct obj *) 0;
655 register struct monst *mtmp;
656 int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ;
658 if (class < 0 || class >= MAXOCLASSES) {
659 impossible("object_detect: illegal class %d", class);
663 /* Special boulder symbol check - does the class symbol happen
664 * to match iflags.bouldersym which is a user-defined?
665 * If so, that means we aren't sure what they really wanted to
666 * detect. Rather than trump anything, show both possibilities.
667 * We can exclude checking the buried obj chain for boulders below.
669 sym = class ? def_oc_syms[class].sym : 0;
670 if (sym && iflags.bouldersym && sym == iflags.bouldersym)
671 boulder = ROCK_CLASS;
673 if (Hallucination || (Confusion && class == SCROLL_CLASS))
674 Strcpy(stuff, something);
677 Strcpy(stuff, class ? def_oc_syms[class].name : "objects");
679 Strcpy(stuff, class ? def_oc_syms[class].name : "
\95¨
\91Ì");
680 if (boulder && class != ROCK_CLASS)
682 Strcat(stuff, " and/or large stones");
684 Strcat(stuff, "
\82Æ
\8b\90\8aâ");
687 for (obj = invent; obj; obj = obj->nobj)
690 for (obj = fobj; obj; obj = obj->nobj) {
691 if ((!class && !boulder) || o_in(obj, class) || o_in(obj, boulder)) {
692 if (obj->ox == u.ux && obj->oy == u.uy)
701 for (obj = level.buriedobjlist; obj; obj = obj->nobj) {
702 if (!class || o_in(obj, class)) {
703 if (obj->ox == u.ux && obj->oy == u.uy)
713 u.usteed->mx = u.ux, u.usteed->my = u.uy;
715 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
716 if (DEADMONSTER(mtmp))
718 for (obj = mtmp->minvent; obj; obj = obj->nobj) {
719 if ((!class && !boulder) || o_in(obj, class)
720 || o_in(obj, boulder))
725 if ((is_cursed && mtmp->m_ap_type == M_AP_OBJECT
726 && (!class || class == objects[mtmp->mappearance].oc_class))
727 || (findgold(mtmp->minvent) && (!class || class == COIN_CLASS))) {
733 if (!clear_stale_map(!class ? ALL_CLASSES : class, 0) && !ct) {
737 strange_feeling(detector, "You feel a lack of something.");
739 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");
744 You("sense %s nearby.", stuff);
746 You("
\8bß
\82
\82Ì%s
\82ð
\8a´
\92m
\82µ
\82½
\81D", stuff);
752 (void) unconstrain_map();
754 * Map all buried objects first.
756 for (obj = level.buriedobjlist; obj; obj = obj->nobj)
757 if (!class || (otmp = o_in(obj, class)) != 0) {
768 * If we are mapping all objects, map only the top object of a pile or
769 * the first object in a monster's inventory. Otherwise, go looking
770 * for a matching object class and display the first one encountered
773 * Objects on the floor override buried objects.
775 for (x = 1; x < COLNO; x++)
776 for (y = 0; y < ROWNO; y++)
777 for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
778 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
779 || (otmp = o_in(obj, boulder)) != 0) {
780 if (class || boulder) {
791 /* Objects in the monster's inventory override floor objects. */
792 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
793 if (DEADMONSTER(mtmp))
795 for (obj = mtmp->minvent; obj; obj = obj->nobj)
796 if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
797 || (otmp = o_in(obj, boulder)) != 0) {
798 if (!class && !boulder)
800 otmp->ox = mtmp->mx; /* at monster location */
805 /* Allow a mimic to override the detected objects it is carrying. */
806 if (is_cursed && mtmp->m_ap_type == M_AP_OBJECT
807 && (!class || class == objects[mtmp->mappearance].oc_class)) {
811 temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
815 temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
816 map_object(&temp, 1);
817 } else if (findgold(mtmp->minvent)
818 && (!class || class == COIN_CLASS)) {
821 gold = zeroobj; /* ensure oextra is cleared too */
822 gold.otyp = GOLD_PIECE;
823 gold.quan = (long) rnd(10); /* usually more than 1 */
826 map_object(&gold, 1);
829 if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
834 You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
836 You("%s%s
\81D", stuff, ct ? "
\82ð
\94
\8c©
\82µ
\82½" : "
\82Í
\89½
\82à
\82È
\82¢
\82±
\82Æ
\82ª
\82í
\82©
\82Á
\82½" );
839 display_nhwindow(WIN_MAP, TRUE);
842 browse_map(ter_typ, "object");
844 browse_map(ter_typ, "
\95¨
\91Ì");
847 docrt(); /* this will correctly reset vision */
856 * Used by: crystal balls, potions, fountains
858 * Returns 1 if nothing was detected.
859 * Returns 0 if something was detected.
862 monster_detect(otmp, mclass)
863 register struct obj *otmp; /* detecting object (if any) */
864 int mclass; /* monster class, 0 for all */
866 register struct monst *mtmp;
869 /* Note: This used to just check fmon for a non-zero value
870 * but in versions since 3.3.0 fmon can test TRUE due to the
871 * presence of dmons, so we have to find at least one
872 * with positive hit-points to know for sure.
874 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
875 if (!DEADMONSTER(mtmp)) {
882 strange_feeling(otmp, Hallucination
884 ? "You get the heebie jeebies."
886 ? "
\82 \82È
\82½
\82Í
\8bà
\92¹
\82Ì
\89Ä
\82Å
\83L
\83\93\83`
\83\87\81[
\82µ
\82½
\81D"
888 : "You feel threatened.");
890 : "
\82 \82È
\82½
\82Í
\8b°
\95|
\82Å
\82¼
\82
\82Á
\82Æ
\82µ
\82½
\81D");
893 boolean unconstrained, woken = FALSE;
894 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
897 unconstrained = unconstrain_map();
898 for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
899 if (DEADMONSTER(mtmp))
901 if (!mclass || mtmp->data->mlet == mclass
902 || (mtmp->data == &mons[PM_LONG_WORM]
903 && mclass == S_WORM_TAIL))
904 map_monst(mtmp, TRUE);
906 if (otmp && otmp->cursed
907 && (mtmp->msleeping || !mtmp->mcanmove)) {
908 mtmp->msleeping = mtmp->mfrozen = 0;
916 You("sense the presence of monsters.");
918 You("
\89ö
\95¨
\82Ì
\91¶
\8dÝ
\82ð
\9ak
\82¬
\82Â
\82¯
\82½
\81D");
921 pline("Monsters sense the presence of you.");
923 pline("
\89ö
\95¨
\82Í
\82 \82È
\82½
\82Ì
\91¶
\8dÝ
\82ð
\9ak
\82¬
\82Â
\82¯
\82½
\81D");
925 if ((otmp && otmp->blessed) && !unconstrained) {
926 /* persistent detection--just show updated map */
927 display_nhwindow(WIN_MAP, TRUE);
929 /* one-shot detection--allow player to move cursor around and
930 get autodescribe feedback */
931 EDetect_monsters |= I_SPECIAL;
933 browse_map(TER_DETECT | TER_MON, "monster of interest");
935 browse_map(TER_DETECT | TER_MON, "
\8aÖ
\90S
\82Ì
\82 \82é
\89ö
\95¨");
936 EDetect_monsters &= ~I_SPECIAL;
940 docrt(); /* redraw the screen to remove unseen monsters from map */
950 sense_trap(trap, x, y, src_cursed)
955 if (Hallucination || src_cursed) {
956 struct obj obj; /* fake object */
966 obj.otyp = !Hallucination ? GOLD_PIECE : random_object();
967 obj.quan = (long) ((obj.otyp == GOLD_PIECE) ? rnd(10)
968 : objects[obj.otyp].oc_merge ? rnd(2) : 1);
969 obj.corpsenm = random_monster(); /* if otyp == CORPSE */
974 } else { /* trapped door or trapped chest */
975 struct trap temp_trap; /* fake trap */
977 (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap);
980 temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
981 map_trap(&temp_trap, 1);
985 #define OTRAP_NONE 0 /* nothing found */
986 #define OTRAP_HERE 1 /* found at hero's location */
987 #define OTRAP_THERE 2 /* found at any other location */
989 /* check a list of objects for chest traps; return 1 if found at <ux,uy>,
990 2 if found at some other spot, 3 if both, 0 otherwise; optionally
991 update the map to show where such traps were found */
993 detect_obj_traps(objlist, show_them, how)
996 int how; /* 1 for misleading map feedback */
1000 int result = OTRAP_NONE;
1003 * TODO? Display locations of unarmed land mine and beartrap objects.
1004 * If so, should they be displayed as objects or as traps?
1007 for (otmp = objlist; otmp; otmp = otmp->nobj) {
1008 if (Is_box(otmp) && otmp->otrapped
1009 && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
1010 result |= (x == u.ux && y == u.uy) ? OTRAP_HERE : OTRAP_THERE;
1012 sense_trap((struct trap *) 0, x, y, how);
1014 if (Has_contents(otmp))
1015 result |= detect_obj_traps(otmp->cobj, show_them, how);
1020 /* the detections are pulled out so they can
1021 * also be used in the crystal ball routine
1022 * returns 1 if nothing was detected
1023 * returns 0 if something was detected
1027 struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
1029 register struct trap *ttmp;
1031 int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP;
1032 int cursed_src = sobj && sobj->cursed;
1033 boolean found = FALSE;
1037 u.usteed->mx = u.ux, u.usteed->my = u.uy;
1039 /* floor/ceiling traps */
1040 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
1041 if (ttmp->tx != u.ux || ttmp->ty != u.uy)
1046 /* chest traps (might be buried or carried) */
1047 if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) {
1048 if (tr & OTRAP_THERE)
1053 if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0)) != OTRAP_NONE) {
1054 if (tr & OTRAP_THERE)
1059 for (mon = fmon; mon; mon = mon->nmon) {
1060 if (DEADMONSTER(mon))
1062 if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) {
1063 if (tr & OTRAP_THERE)
1069 if (detect_obj_traps(invent, FALSE, 0) != OTRAP_NONE)
1072 for (door = 0; door < doorindex; door++) {
1074 if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
1075 if (cc.x != u.ux || cc.y != u.uy)
1085 Sprintf(buf, "Your %s stop itching.", makeplural(body_part(TOE)));
1087 Sprintf(buf, "
\82 \82È
\82½
\82Ì%s
\82Ì
\82Þ
\82¸
\82Þ
\82¸
\82Í
\82¨
\82³
\82Ü
\82Á
\82½
\81D", makeplural(body_part(TOE)));
1088 strange_feeling(sobj, buf);
1091 /* traps exist, but only under me - no separate display required */
1093 Your("%s itch.", makeplural(body_part(TOE)));
1095 Your("%s
\82Í
\82Þ
\82¸
\82Þ
\82¸
\82µ
\82½
\81D", makeplural(body_part(TOE)));
1101 (void) unconstrain_map();
1102 /* show chest traps first, so that subsequent floor trap display
1103 will override if both types are present at the same location */
1104 (void) detect_obj_traps(fobj, TRUE, cursed_src);
1105 (void) detect_obj_traps(level.buriedobjlist, TRUE, cursed_src);
1106 for (mon = fmon; mon; mon = mon->nmon) {
1107 if (DEADMONSTER(mon))
1109 (void) detect_obj_traps(mon->minvent, TRUE, cursed_src);
1111 (void) detect_obj_traps(invent, TRUE, cursed_src);
1113 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap)
1114 sense_trap(ttmp, 0, 0, cursed_src);
1116 for (door = 0; door < doorindex; door++) {
1118 if (levl[cc.x][cc.y].doormask & D_TRAPPED)
1119 sense_trap((struct trap *) 0, cc.x, cc.y, cursed_src);
1122 /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
1123 glyph = glyph_at(u.ux, u.uy);
1124 if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) {
1126 ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
1129 You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
1131 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È");
1134 browse_map(ter_typ, "trap of interest");
1136 browse_map(ter_typ, "
\8aÖ
\90S
\82Ì
\82 \82éã©");
1139 docrt(); /* redraw the screen to remove unseen traps from the map */
1148 level_distance(where)
1151 register schar ll = depth(&u.uz) - depth(where);
1152 register boolean indun = (u.uz.dnum == where->dnum);
1155 if (ll < (-8 - rn2(3)))
1160 return "
\82Í
\82é
\82©
\94Þ
\95û
\82É";
1165 return "
\82Í
\82é
\82©
\89º
\95û
\82É";
1169 return "away below you";
1171 return "
\82¸
\82Á
\82Æ
\89º
\95û
\82É";
1176 return "
\89º
\95û
\82É";
1179 return "in the distance";
1181 return "
\89\93\82
\82É";
1184 return "just below";
1186 return "
\90^
\89º
\82É";
1187 } else if (ll > 0) {
1188 if (ll > (8 + rn2(3)))
1193 return "
\82Í
\82é
\82©
\94Þ
\95û
\82É";
1198 return "
\82Í
\82é
\82©
\8fã
\95û
\82É";
1202 return "away above you";
1204 return "
\82¸
\82Á
\82Æ
\8fã
\95û
\82É";
1209 return "
\8fã
\95û
\82É";
1212 return "in the distance";
1214 return "
\89\93\82
\82É";
1217 return "just above";
1219 return "
\90^
\8fã
\82É";
1222 return "in the distance";
1224 return "
\89\93\82
\82É";
1229 return "
\8bß
\82
\82É";
1232 static const struct {
1235 } level_detects[] = {
1237 { "Delphi", &oracle_level },
1239 { "
\83f
\83\8b\83t
\83@
\83C", &oracle_level },
1241 { "Medusa's lair", &medusa_level },
1243 { "
\83\81\83f
\83\85\81[
\83T
\82Ì
\8fZ
\82Ý
\82©", &medusa_level },
1245 { "a castle", &stronghold_level },
1247 { "
\8fé", &stronghold_level },
1249 { "the Wizard of Yendor's tower", &wiz1_level },
1251 { "
\83C
\83F
\83\93\83_
\81[
\82Ì
\96\82\96@
\8eg
\82¢
\82Ì
\93\83", &wiz1_level },
1255 use_crystal_ball(optr)
1260 struct obj *obj = *optr;
1264 pline("Too bad you can't see %s.", the(xname(obj)));
1266 pline("
\82È
\82ñ
\82Ä
\82±
\82Æ
\82¾
\81D%s
\82ð
\8c©
\82é
\82±
\82Æ
\82ª
\82Å
\82«
\82È
\82¢
\81D", the(xname(obj)));
1269 oops = (rnd(20) > ACURR(A_INT) || obj->cursed);
1270 if (oops && (obj->spe > 0)) {
1271 switch (rnd(obj->oartifact ? 4 : 5)) {
1274 pline("%s too much to comprehend!", Tobjnam(obj, "are"));
1276 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));
1280 pline("%s you!", Tobjnam(obj, "confuse"));
1282 pline("%s
\82ð
\94`
\82¢
\82Ä
\82é
\82Æ
\82Ó
\82ç
\82Â
\82¢
\82Ä
\82«
\82½
\81I", xname(obj));
1283 make_confused((HConfusion & TIMEOUT) + (long) rnd(100), FALSE);
1286 if (!resists_blnd(&youmonst)) {
1288 pline("%s your vision!", Tobjnam(obj, "damage"));
1290 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));
1291 make_blinded((Blinded & TIMEOUT) + (long) rnd(100), FALSE);
1293 Your1(vision_clears);
1296 pline("%s your vision.", Tobjnam(obj, "assault"));
1298 pline("%s
\82ª
\82 \82È
\82½
\82Ì
\8e\8b\8aE
\82É
\94\97\82Á
\82Ä
\82«
\82½
\81D", xname(obj));
1300 You("are unaffected!");
1302 pline("
\82µ
\82©
\82µ
\81C
\82 \82È
\82½
\82Í
\89e
\8b¿
\82ð
\8eó
\82¯
\82È
\82©
\82Á
\82½
\81I");
1307 pline("%s your mind!", Tobjnam(obj, "zap"));
1309 pline("%s
\82ð
\94`
\82¢
\82Ä
\82¢
\82é
\82Æ
\8cÜ
\8a´
\82ª
\82¨
\82©
\82µ
\82
\82È
\82Á
\82Ä
\82«
\82½
\81I", xname(obj));
1310 (void) make_hallucinated(
1311 (HHallucination & TIMEOUT) + (long) rnd(100), FALSE, 0L);
1315 pline("%s!", Tobjnam(obj, "explode"));
1317 pline("%s
\82Í
\94\9a\94
\82µ
\82½
\81I", xname(obj));
1319 *optr = obj = 0; /* it's gone */
1320 /* physical damage cause by the shards and force */
1322 losehp(Maybe_Half_Phys(rnd(30)), "exploding crystal ball",
1324 losehp(Maybe_Half_Phys(rnd(30)), "
\90\85\8f»
\8bÊ
\82Ì
\94\9a\94
\82Å",
1329 consume_obj_charge(obj, TRUE);
1333 if (Hallucination) {
1336 pline("All you see is funky %s haze.", hcolor((char *) 0));
1338 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));
1343 You("grok some groovy globs of incandescent lava.");
1345 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");
1349 pline("Whoa! Psychedelic colors, %s!",
1350 poly_gender() == 1 ? "babe" : "dude");
1352 pline("
\83\8f\81[
\83I
\81I
\83\89\83\8a\82Á
\82Ä
\82é
\82©
\82¢
\81H%s
\81I",
1353 poly_gender() == 1 ? "
\83x
\83C
\83r
\81[" : "
\83\86\81[");
1358 pline_The("crystal pulses with sinister %s light!",
1360 pline("
\90\85\8f»
\82Í
\95s
\8bg
\82È%s
\83p
\83\8b\83X
\82ð
\94
\82µ
\82½
\81I",
1361 hcolor((char *) 0));
1365 You_see("goldfish swimming above fluorescent rocks.");
1367 You("
\8cu
\8cõ
\8aâ
\82Ì
\8fã
\82ð
\8bà
\8b\9b\82ª
\89j
\82¢
\82Å
\82¢
\82é
\82Ì
\82ð
\8c©
\82½
\81D");
1372 "tiny snowflakes spinning around a miniature farmhouse.");
1374 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");
1379 pline("Oh wow... like a kaleidoscope!");
1381 pline("
\83\8f\81[
\83I
\81D
\96\9c\89Ø
\8b¾
\82Ì
\82æ
\82¤
\82¾
\81I");
1384 consume_obj_charge(obj, TRUE);
1389 /* read a single character */
1392 You("may look for an object or monster symbol.");
1394 You("
\95¨
\91Ì
\82â
\89ö
\95¨
\82Ì
\8bL
\8d\86\82ð
\92T
\82¹
\82é
\81D");
1396 ch = yn_function("What do you look for?", (char *) 0, '\0');
1398 ch = yn_function("
\89½
\82ð
\92T
\82µ
\82Ü
\82·
\82©
\81H", (char *)0, '\0');
1399 /* Don't filter out ' ' here; it has a use */
1400 if ((ch != def_monsyms[S_GHOST].sym) && index(quitchars, ch)) {
1406 You("peer into %s...", the(xname(obj)));
1408 You("%s
\82ð
\94`
\82«
\82±
\82ñ
\82¾
\81D
\81D
\81D", the(xname(obj)));
1411 multi_reason = "gazing into a crystal ball";
1413 multi_reason = "
\90\85\8f»
\8b\85\82ð
\94`
\82«
\8d\9e\82ñ
\82Å
\82¢
\82é
\8e\9e\82É";
1415 if (obj->spe <= 0) {
1417 pline_The("vision is unclear.");
1419 pline("
\89f
\91\9c\82Í
\95s
\91N
\96¾
\82¾
\82Á
\82½
\81D");
1424 makeknown(CRYSTAL_BALL);
1425 consume_obj_charge(obj, TRUE);
1427 /* special case: accept ']' as synonym for mimic
1428 * we have to do this before the def_char_to_objclass check
1430 if (ch == DEF_MIMIC_DEF)
1433 if ((class = def_char_to_objclass(ch)) != MAXOCLASSES)
1434 ret = object_detect((struct obj *) 0, class);
1435 else if ((class = def_char_to_monclass(ch)) != MAXMCLASSES)
1436 ret = monster_detect((struct obj *) 0, class);
1437 else if (iflags.bouldersym && (ch == iflags.bouldersym))
1438 ret = object_detect((struct obj *) 0, ROCK_CLASS);
1442 ret = trap_detect((struct obj *) 0);
1445 i = rn2(SIZE(level_detects));
1447 You_see("%s, %s.", level_detects[i].what,
1448 level_distance(level_detects[i].where));
1450 You_see("%s
\82ð%s
\8c©
\82½
\81D", level_detects[i].what,
1451 level_distance(level_detects[i].where));
1458 if (!rn2(100)) /* make them nervous */
1460 You_see("the Wizard of Yendor gazing out at you.");
1462 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");
1465 pline_The("vision is unclear.");
1467 pline("
\89f
\91\9c\82Í
\95s
\91N
\96¾
\82É
\82È
\82Á
\82½
\81D");
1481 if (Confusion && rn2(7))
1487 /* Secret corridors are found, but not secret doors. */
1488 if (lev->typ == SCORR) {
1490 unblock_point(x, y);
1494 * Force the real background, then if it's not furniture and there's
1495 * a known trap there, display the trap, else if there was an object
1496 * shown there, redisplay the object. So during mapping, furniture
1497 * takes precedence over traps, which take precedence over objects,
1498 * opposite to how normal vision behaves.
1500 oldglyph = glyph_at(x, y);
1501 if (level.flags.hero_memory) {
1502 magic_map_background(x, y, 0);
1503 newsym(x, y); /* show it, if not blocked */
1505 magic_map_background(x, y, 1); /* display it */
1507 if (!IS_FURNITURE(lev->typ)) {
1508 if ((t = t_at(x, y)) != 0 && t->tseen) {
1510 } else if (glyph_is_trap(oldglyph) || glyph_is_object(oldglyph)) {
1511 show_glyph(x, y, oldglyph);
1512 if (level.flags.hero_memory)
1513 lev->glyph = oldglyph;
1521 register int zx, zy;
1522 boolean unconstrained;
1524 unconstrained = unconstrain_map();
1525 for (zx = 1; zx < COLNO; zx++)
1526 for (zy = 0; zy < ROWNO; zy++)
1527 show_map_spot(zx, zy);
1529 if (!level.flags.hero_memory || unconstrained) {
1530 flush_screen(1); /* flush temp screen */
1531 /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
1533 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1534 "anything of interest");
1536 browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
1537 "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
1542 exercise(A_WIS, TRUE);
1547 do_vicinity_map(sobj)
1548 struct obj *sobj; /* scroll--actually fake spellbook--object */
1550 register int zx, zy;
1552 boolean unconstrained, refresh = FALSE, mdetected = FALSE,
1553 extended = (sobj && sobj->blessed);
1554 int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
1555 hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
1556 lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
1557 hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
1558 ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
1560 unconstrained = unconstrain_map();
1561 for (zx = lo_x; zx <= hi_x; zx++)
1562 for (zy = lo_y; zy <= hi_y; zy++) {
1563 show_map_spot(zx, zy);
1565 if (extended && (mtmp = m_at(zx, zy)) != 0
1566 && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
1567 int oldglyph = glyph_at(zx, zy);
1569 map_monst(mtmp, FALSE);
1570 if (glyph_at(zx, zy) != oldglyph)
1575 if (!level.flags.hero_memory || unconstrained || mdetected) {
1576 flush_screen(1); /* flush temp screen */
1577 /* the getpos() prompt from browse_map() is only shown when
1578 flags.verbose is set, but make this unconditional so that
1579 not-verbose users become aware of the prompting situation */
1581 You("sense your surroundings.");
1583 You("
\82Ü
\82í
\82è
\82Ì
\82à
\82Ì
\82ð
\8a´
\92m
\82µ
\82½
\81D");
1584 if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
1587 EDetect_monsters |= I_SPECIAL;
1589 browse_map(ter_typ, "anything of interest");
1591 browse_map(ter_typ, "
\8aÖ
\90S
\82Ì
\82 \82é
\82à
\82Ì");
1592 EDetect_monsters &= ~I_SPECIAL;
1600 /* convert a secret door into a normal door */
1602 cvt_sdoor_to_door(lev)
1605 int newmask = lev->doormask & ~WM_MASK;
1607 if (Is_rogue_level(&u.uz))
1608 /* rogue didn't have doors, only doorways */
1611 /* newly exposed door is closed */
1612 if (!(newmask & D_LOCKED))
1613 newmask |= D_CLOSED;
1616 lev->doormask = newmask;
1620 findone(zx, zy, num)
1624 register struct trap *ttmp;
1625 register struct monst *mtmp;
1627 if (levl[zx][zy].typ == SDOOR) {
1628 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1629 magic_map_background(zx, zy, 0);
1632 } else if (levl[zx][zy].typ == SCORR) {
1633 levl[zx][zy].typ = CORR;
1634 unblock_point(zx, zy);
1635 magic_map_background(zx, zy, 0);
1638 } else if ((ttmp = t_at(zx, zy)) != 0) {
1639 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1644 } else if ((mtmp = m_at(zx, zy)) != 0) {
1645 if (mtmp->m_ap_type) {
1649 if (mtmp->mundetected
1650 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) {
1651 mtmp->mundetected = 0;
1655 if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
1656 map_invisible(zx, zy);
1657 } else if (unmap_invisible(zx, zy)) {
1663 openone(zx, zy, num)
1667 register struct trap *ttmp;
1668 register struct obj *otmp;
1669 int *num_p = (int *) num;
1671 if (OBJ_AT(zx, zy)) {
1672 for (otmp = level.objects[zx][zy]; otmp; otmp = otmp->nexthere) {
1673 if (Is_box(otmp) && otmp->olocked) {
1678 /* let it fall to the next cases. could be on trap. */
1680 if (levl[zx][zy].typ == SDOOR
1681 || (levl[zx][zy].typ == DOOR
1682 && (levl[zx][zy].doormask & (D_CLOSED | D_LOCKED)))) {
1683 if (levl[zx][zy].typ == SDOOR)
1684 cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */
1685 if (levl[zx][zy].doormask & D_TRAPPED) {
1686 if (distu(zx, zy) < 3)
1688 b_trapped("door", 0);
1690 b_trapped("
\94à", 0);
1693 Norep("You %s an explosion!",
1694 cansee(zx, zy) ? "see" : (!Deaf ? "hear"
1695 : "feel the shock of"));
1697 Norep("
\82 \82È
\82½
\82Í
\94\9a\94%s
\81I",
1698 cansee(zx, zy) ? "
\82ð
\8c©
\82½" : (!Deaf ? "
\89¹
\82ð
\95·
\82¢
\82½"
1699 : "
\82Ì
\8fÕ
\8c\82\82ð
\8a´
\82¶
\82½"));
1701 wake_nearto(zx, zy, 11 * 11);
1702 levl[zx][zy].doormask = D_NODOOR;
1704 levl[zx][zy].doormask = D_ISOPEN;
1705 unblock_point(zx, zy);
1708 } else if (levl[zx][zy].typ == SCORR) {
1709 levl[zx][zy].typ = CORR;
1710 unblock_point(zx, zy);
1713 } else if ((ttmp = t_at(zx, zy)) != 0) {
1715 boolean dummy; /* unneeded "you notice it arg" */
1717 if (!ttmp->tseen && ttmp->ttyp != STATUE_TRAP) {
1722 mon = (zx == u.ux && zy == u.uy) ? &youmonst : m_at(zx, zy);
1723 if (openholdingtrap(mon, &dummy)
1724 || openfallingtrap(mon, TRUE, &dummy))
1726 } else if (find_drawbridge(&zx, &zy)) {
1727 /* make sure it isn't an open drawbridge */
1728 open_drawbridge(zx, zy);
1733 /* returns number of things found */
1741 do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &num);
1745 /* returns number of things found and opened */
1752 if (is_animal(u.ustuck->data)) {
1755 pline("Its mouth opens!");
1757 pline("
\89½
\8eÒ
\82©
\82Ì
\8cû
\82ª
\8aJ
\82¢
\82½
\81I");
1760 pline("%s opens its mouth!", Monnam(u.ustuck));
1762 pline("%s
\82Í
\8cû
\82ð
\8aJ
\82¢
\82½
\81I", Monnam(u.ustuck));
1764 expels(u.ustuck, u.ustuck->data, TRUE);
1768 do_clear_area(u.ux, u.uy, BOLT_LIM, openone, (genericptr_t) &num);
1772 /* callback hack for overriding vision in do_clear_area() */
1775 void FDECL((*func), (int, int, genericptr_t));
1777 return (func == findone || func == openone);
1784 int tt = what_trap(trap->ttyp);
1785 boolean cleared = FALSE;
1788 exercise(A_WIS, TRUE);
1789 feel_newsym(trap->tx, trap->ty);
1791 if (levl[trap->tx][trap->ty].glyph != trap_to_glyph(trap)) {
1792 /* There's too much clutter to see your find otherwise */
1800 You("find %s.", an(defsyms[trap_to_defsym(tt)].explanation));
1802 You("%s
\82ð
\8c©
\82Â
\82¯
\82½
\81D", defsyms[trap_to_defsym(tt)].explanation);
1805 display_nhwindow(WIN_MAP, TRUE); /* wait */
1811 mfind0(mtmp, via_warning)
1813 boolean via_warning;
1818 if (via_warning && !warning_of(mtmp))
1821 if (mtmp->m_ap_type) {
1824 exercise(A_WIS, TRUE);
1825 if (!canspotmon(mtmp)) {
1826 if (glyph_is_invisible(levl[x][y].glyph)) {
1827 /* Found invisible monster in a square which already has
1828 * an 'I' in it. Logically, this should still take time
1829 * and lead to a return 1, but if we did that the player
1830 * would keep finding the same monster every turn.
1835 You_feel("an unseen monster!");
1837 You("
\8c©
\82¦
\82È
\82¢
\89ö
\95¨
\82Ì
\8bC
\94z
\82ð
\8a´
\82¶
\82½
\81I");
1838 map_invisible(x, y);
1840 } else if (!sensemon(mtmp))
1843 mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1845 You("%s
\82ð
\8c©
\82Â
\82¯
\82½
\81D",
1846 mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
1850 if (!canspotmon(mtmp)) {
1851 if (mtmp->mundetected
1852 && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
1855 Your("warning senses cause you to take a second %s.",
1856 Blind ? "to check nearby" : "look close by");
1858 Your("
\8cx
\89ú
\90S
\82ª%s
\82µ
\82Î
\82ç
\82
\82©
\82©
\82Á
\82½
\81D",
1859 Blind ? "
\82Ü
\82í
\82è
\82ð
\92²
\82×
\82é
\82Ì
\82É" : "
\8bß
\82
\82ð
\8c©
\82é
\82Ì
\82É");
1861 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
1863 mtmp->mundetected = 0;
1872 register int aflag; /* intrinsic autosearch vs explicit searching */
1875 /* Some old versions of gcc seriously muck up nested loops. If you get
1876 * strange crashes while searching in a version compiled with gcc, try
1877 * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
1880 volatile xchar x, y;
1882 register xchar x, y;
1884 register struct trap *trap;
1885 register struct monst *mtmp;
1890 pline("What are you looking for? The exit?");
1892 pline("
\89½
\82ð
\92T
\82·
\82ñ
\82¾
\82¢
\81H
\94ñ
\8fí
\8cû
\81H");
1894 int fund = (uwep && uwep->oartifact
1895 && spec_ability(uwep, SPFX_SEARCH)) ? uwep->spe : 0;
1897 if (ublindf && ublindf->otyp == LENSES && !Blind)
1898 fund += 2; /* JDS: lenses help searching */
1901 for (x = u.ux - 1; x < u.ux + 2; x++)
1902 for (y = u.uy - 1; y < u.uy + 2; y++) {
1905 if (x == u.ux && y == u.uy)
1908 if (Blind && !aflag)
1909 feel_location(x, y);
1910 if (levl[x][y].typ == SDOOR) {
1913 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
1914 exercise(A_WIS, TRUE);
1916 feel_location(x, y); /* make sure it shows up */
1918 You("find a hidden door.");
1920 You("
\89B
\82³
\82ê
\82½
\94à
\82ð
\8c©
\82Â
\82¯
\82½
\81D");
1921 } else if (levl[x][y].typ == SCORR) {
1924 levl[x][y].typ = CORR;
1925 unblock_point(x, y); /* vision */
1926 exercise(A_WIS, TRUE);
1928 feel_newsym(x, y); /* make sure it shows up */
1930 You("find a hidden passage.");
1932 You("
\89B
\82³
\82ê
\82½
\92Ê
\98H
\82ð
\8c©
\82Â
\82¯
\82½
\81D");
1934 /* Be careful not to find anything in an SCORR or SDOOR */
1935 if ((mtmp = m_at(x, y)) != 0 && !aflag) {
1936 int mfres = mfind0(mtmp, 0);
1944 /* see if an invisible monster has moved--if Blind,
1945 * feel_location() already did it
1947 if (!aflag && !mtmp && !Blind)
1948 (void) unmap_invisible(x, y);
1950 if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
1952 if (trap->ttyp == STATUE_TRAP) {
1953 if (activate_statue_trap(trap, x, y, FALSE))
1954 exercise(A_WIS, TRUE);
1966 /* the 's' command -- explicit searching */
1970 return dosearch0(0);
1979 for (x = u.ux - 1; x <= u.ux + 1; x++)
1980 for (y = u.uy - 1; y <= u.uy + 1; y++) {
1981 if (!isok(x, y) || (x == u.ux && y == u.uy))
1983 if ((mtmp = m_at(x, y)) != 0
1984 && warning_of(mtmp) && mtmp->mundetected)
1985 (void) mfind0(mtmp, 1); /* via_warning */
1989 /* Pre-map the sokoban levels */
1994 register struct trap *ttmp;
1995 register struct obj *obj;
1997 /* Map the background and boulders */
1998 for (x = 1; x < COLNO; x++)
1999 for (y = 0; y < ROWNO; y++) {
2000 levl[x][y].seenv = SVALL;
2001 levl[x][y].waslit = TRUE;
2002 map_background(x, y, 1);
2003 if ((obj = sobj_at(BOULDER, x, y)) != 0)
2008 for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
2011 /* set sokoban_rules when there is at least one pit or hole */
2012 if (ttmp->ttyp == PIT || ttmp->ttyp == HOLE)
2018 reveal_terrain_getglyph(x, y, full, swallowed, default_glyph, which_subset)
2021 int default_glyph, which_subset;
2023 int glyph, levl_glyph;
2025 boolean keep_traps = (which_subset & TER_TRP) !=0,
2026 keep_objs = (which_subset & TER_OBJ) != 0,
2027 keep_mons = (which_subset & TER_MON) != 0;
2031 /* for 'full', show the actual terrain for the entire level,
2032 otherwise what the hero remembers for seen locations with
2033 monsters, objects, and/or traps removed as caller dictates */
2034 seenv = (full || level.flags.hero_memory)
2035 ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
2037 levl[x][y].seenv = SVALL;
2038 glyph = back_to_glyph(x, y);
2039 levl[x][y].seenv = seenv;
2041 levl_glyph = level.flags.hero_memory
2043 : seenv ? back_to_glyph(x, y): default_glyph;
2044 /* glyph_at() returns the displayed glyph, which might
2045 be a monster. levl[][].glyph contains the remembered
2046 glyph, which will never be a monster (unless it is
2047 the invisible monster glyph, which is handled like
2048 an object, replacing any object or trap at its spot) */
2049 glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
2050 if (keep_mons && x == u.ux && y == u.uy && swallowed)
2051 glyph = mon_to_glyph(u.ustuck);
2052 else if (((glyph_is_monster(glyph)
2053 || glyph_is_warning(glyph)) && !keep_mons)
2054 || glyph_is_swallow(glyph))
2056 if (((glyph_is_object(glyph) && !keep_objs)
2057 || glyph_is_invisible(glyph))
2058 && keep_traps && !covers_traps(x, y)) {
2059 if ((t = t_at(x, y)) != 0 && t->tseen)
2060 glyph = trap_to_glyph(t);
2062 if ((glyph_is_object(glyph) && !keep_objs)
2063 || (glyph_is_trap(glyph) && !keep_traps)
2064 || glyph_is_invisible(glyph)) {
2066 glyph = default_glyph;
2067 } else if (lastseentyp[x][y] == levl[x][y].typ) {
2068 glyph = back_to_glyph(x, y);
2070 /* look for a mimic here posing as furniture;
2071 if we don't find one, we'll have to fake it */
2072 if ((mtmp = m_at(x, y)) != 0
2073 && mtmp->m_ap_type == M_AP_FURNITURE) {
2074 glyph = cmap_to_glyph(mtmp->mappearance);
2076 /* we have a topology type but we want a screen
2077 symbol in order to derive a glyph; some screen
2078 symbols need the flags field of levl[][] in
2079 addition to the type (to disambiguate STAIRS to
2080 S_upstair or S_dnstair, for example; current
2081 flags might not be intended for remembered type,
2082 but we've got no other choice) */
2083 schar save_typ = levl[x][y].typ;
2085 levl[x][y].typ = lastseentyp[x][y];
2086 glyph = back_to_glyph(x, y);
2087 levl[x][y].typ = save_typ;
2092 if (glyph == cmap_to_glyph(S_darkroom))
2093 glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
2101 int x, y, glyph, skippedrows, lastnonblank;
2102 int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON;
2103 int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
2105 boolean blankrow, toprow;
2108 * Squeeze out excess vertial space when dumping the map.
2109 * If there are any blank map rows at the top, suppress them
2110 * (our caller has already printed a separator). If there is
2111 * more than one blank map row at the bottom, keep just one.
2112 * Any blank rows within the middle of the map are kept.
2113 * Note: putstr() with winid==0 is for dumplog.
2117 for (y = 0; y < ROWNO; y++) {
2118 blankrow = TRUE; /* assume blank until we discover otherwise */
2119 lastnonblank = -1; /* buf[] index rather than map's x */
2120 for (x = 1; x < COLNO; x++) {
2124 glyph = reveal_terrain_getglyph(x, y, FALSE, u.uswallow,
2125 default_glyph, subset);
2126 (void) mapglyph(glyph, &ch, &color, &special, x, y);
2130 lastnonblank = x - 1;
2134 buf[lastnonblank + 1] = '\0';
2139 for (x = 0; x < skippedrows; x++)
2141 putstr(0, 0, buf); /* map row #y */
2150 #endif /* DUMPLOG */
2152 /* idea from crawl; show known portion of map without any monsters,
2153 objects, or traps occluding the view of the underlying terrain */
2155 reveal_terrain(full, which_subset)
2156 int full; /* wizard|explore modes allow player to request full map */
2157 int which_subset; /* when not full, whether to suppress objs and/or traps */
2159 if ((Hallucination || Stunned || Confusion) && !full) {
2161 You("are too disoriented for this.");
2163 You("
\8d¬
\97\90\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\82»
\82ê
\82Í
\82Å
\82«
\82È
\82¢
\81D");
2165 int x, y, glyph, default_glyph;
2167 /* there is a TER_MAP bit too; we always show map regardless of it */
2168 boolean keep_traps = (which_subset & TER_TRP) !=0,
2169 keep_objs = (which_subset & TER_OBJ) != 0,
2170 keep_mons = (which_subset & TER_MON) != 0; /* not used */
2171 unsigned swallowed = u.uswallow; /* before unconstrain_map() */
2173 if (unconstrain_map())
2175 default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
2177 for (x = 1; x < COLNO; x++)
2178 for (y = 0; y < ROWNO; y++) {
2179 glyph = reveal_terrain_getglyph(x,y, full, swallowed,
2180 default_glyph, which_subset);
2181 show_glyph(x, y, glyph);
2184 /* hero's location is not highlighted, but getpos() starts with
2185 cursor there, and after moving it anywhere '@' moves it back */
2189 Strcpy(buf, "underlying terrain");
2191 Strcpy(buf, "
\89º
\82É
\82 \82é
\92n
\8c`");
2194 Strcpy(buf, "known terrain");
2196 Strcpy(buf, "
\92m
\82Á
\82Ä
\82¢
\82é
\92n
\8c`");
2199 Sprintf(eos(buf), "%s traps",
2200 (keep_objs || keep_mons) ? "," : " and");
2202 Strcat(buf, "
\82Æã©");
2206 Sprintf(eos(buf), "%s%s objects",
2207 (keep_traps || keep_mons) ? "," : "",
2208 keep_mons ? "" : " and");
2210 Strcat(buf, "
\82Æ
\95¨
\91Ì");
2214 Sprintf(eos(buf), "%s and monsters",
2215 (keep_traps || keep_objs) ? "," : "");
2217 Strcat(buf, "
\82Æ
\89ö
\95¨");
2221 pline("Showing %s only...", buf);
2223 pline("%s
\82¾
\82¯
\82ð
\8c©
\82é
\81D
\81D
\81D", buf);
2225 /* allow player to move cursor around and get autodescribe feedback
2226 based on what is visible now rather than what is on 'real' map */
2227 which_subset |= TER_MAP; /* guarantee non-zero */
2228 browse_map(which_subset, "anything of interest");
2231 docrt(); /* redraw the screen, restoring regular map */