From: PatR Date: Thu, 2 Jun 2016 08:08:34 +0000 (-0700) Subject: detection revamp X-Git-Tag: NetHack-3.6.1_RC01~712 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=894d6c68593d65ba6bb9b8b8929113312e51405e;p=nethack detection revamp Change most instances of detection to offer the player a chance to move the cursor around on the map so that the getpos() autodescribe feature can explain things that might go away as soon as the current detection completes. The few instances that don't offer such a chance are the ones where everything which has been revealed will still be there once the action finishes (such as regular magic mapping and blessed/persistent monster detection). There were quite a lot of inconsistencies in things like handling for detection while swallowed or underwater. I didn't keep track of them to distinguish between 3.6.0, current dev code, or my patch in progess. They should be much more consistent now but without a comprehensive fixes36.1 entry. Blessed clairvoyance (divination spells at skilled or expert) now shows monsters as well as terrain. I first had it like that for any clairvoyance, but having getpos/autodescribe kick in every 15 or 30 turns once you have the amulet--or pay the appropriate amount to a temple preist--was nearly unplayable. When it only follows an explicit spell cast it is not intrusive. --- diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 8cfd5880a..2e58f8f23 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -280,6 +280,8 @@ thrown potion that killed peaceful monster would cause "the gets argry" after the message about it being killed when poly'd into a hider and engulfed, attempt to hide via #monster was blocked but feedback said "can't hide while held" rather than "while engulfed" +various monster/object/food/gold/trap detections were inconsistent in how they + behaved if performed while engulfed or underwater Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository @@ -395,9 +397,12 @@ during end of game disclosure, the vanquished monsters list can be sorted in one of several ways by answering 'a' to "disclose vanquished monsters?" when #terrain is displaying a censored version of the map (no monsters, &c), moving the cursor will display farlook's brief autodescribe feedback +when monster/object/trap detection temporarily clears the map to highlight the + results of such detection, farlook autodescibe can be used to view it interrupt a multi turn action if hp or pw is restored to maximum pressing d or D when cursor positioning targets doors and doorways pressing x or X when cursor positioning targets possibly unexplored location + (potentially useful when using '_' [not mouse] to invoke travel) Platform- and/or Interface-Specific New Features @@ -420,10 +425,10 @@ X11: status display split into three columns to accomodate Stone/Deaf/Lev/&c; NetHack Community Patches (or Variation) Included ------------------------------------------------- Malcolm Ryan's improved tin opener -Ray Chason's keyboard may stop responding after locking or unlocking a door when - using altkeyhandler=nhraykey.dll -Ray Chason's fix: window interfaces that support transparency may give away unseen - parts of the map +Ray Chason's keyboard may stop responding after locking or unlocking a door + when using altkeyhandler=nhraykey.dll +Ray Chason's fix: window interfaces that support transparency may give away + unseen parts of the map Ray Chason's xprname should honor iflags.menu_tab_sep Ray Chason's punctuation for "That foo is really a mimic." Ray Chason's proper background tiles for lava and water diff --git a/include/extern.h b/include/extern.h index 4c5cb8e64..4b58776f5 100644 --- a/include/extern.h +++ b/include/extern.h @@ -248,7 +248,7 @@ E int FDECL(trap_detect, (struct obj *)); E const char *FDECL(level_distance, (d_level *)); E void FDECL(use_crystal_ball, (struct obj **)); E void NDECL(do_mapping); -E void NDECL(do_vicinity_map); +E void FDECL(do_vicinity_map, (struct obj *)); E void FDECL(cvt_sdoor_to_door, (struct rm *)); #ifdef USE_TRAMPOLI E void FDECL(findone, (int, int, genericptr_t)); diff --git a/include/flag.h b/include/flag.h index 79bedc9b0..a8a6d8593 100644 --- a/include/flag.h +++ b/include/flag.h @@ -187,10 +187,11 @@ struct instance_flags { int override_ID; /* true to force full identification of objects */ int suppress_price; /* controls doname() for unpaid objects */ int terrainmode; /* for getpos()'s autodescribe when #terrain is active */ -#define TER_MAP 0x01 -#define TER_TRP 0x02 -#define TER_OBJ 0x04 -#define TER_MON 0x08 +#define TER_MAP 0x01 +#define TER_TRP 0x02 +#define TER_OBJ 0x04 +#define TER_MON 0x08 +#define TER_DETECT 0x10 /* detect_foo magic rather than #terrain */ coord travelcc; /* coordinates for travel_cache */ boolean window_inited; /* true if init_nhwindows() completed */ boolean vision_inited; /* true if vision is ready */ diff --git a/src/allmain.c b/src/allmain.c index 33afd9927..3aecf399a 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -370,7 +370,7 @@ boolean resuming; clear_bypasses(); if ((u.uhave.amulet || Clairvoyant) && !In_endgame(&u.uz) && !BClairvoyant && !(moves % 15) && !rn2(2)) - do_vicinity_map(); + do_vicinity_map((struct obj *) 0); if (u.utrap && u.utraptype == TT_LAVA) sink_into_lava(); /* when/if hero escapes from lava, he can't just stay there */ diff --git a/src/detect.c b/src/detect.c index 07b93227f..7668d77bb 100644 --- a/src/detect.c +++ b/src/detect.c @@ -12,6 +12,10 @@ extern boolean known; /* from read.c */ +STATIC_DCL boolean NDECL(unconstrain_map); +STATIC_DCL void NDECL(reconstrain_map); +STATIC_DCL void FDECL(browse_map, (int, const char *)); +STATIC_DCL void FDECL(map_monst, (struct monst *, BOOLEAN_P)); STATIC_DCL void FDECL(do_dknown_of, (struct obj *)); STATIC_DCL boolean FDECL(check_map_spot, (int, int, CHAR_P, unsigned)); STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P, unsigned)); @@ -22,6 +26,62 @@ STATIC_PTR void FDECL(findone, (int, int, genericptr_t)); STATIC_PTR void FDECL(openone, (int, int, genericptr_t)); STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P)); +/* bring hero out from underwater or underground or being engulfed; + return True iff any change occurred */ +STATIC_OVL boolean +unconstrain_map() +{ + boolean res = u.uinwater || u.uburied || u.uswallow; + + /* bring Underwater, buried, or swallowed hero to normal map */ + iflags.save_uinwater = u.uinwater, u.uinwater = 0; + iflags.save_uburied = u.uburied, u.uburied = 0; + iflags.save_uswallow = u.uswallow, u.uswallow = 0; + + return res; +} + +/* put hero back underwater or underground or engulfed */ +STATIC_OVL void +reconstrain_map() +{ + u.uinwater = iflags.save_uinwater, iflags.save_uinwater = 0; + u.uburied = iflags.save_uburied, iflags.save_uburied = 0; + u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0; +} + +/* use getpos()'s 'autodescribe' to view whatever is currently shown on map */ +STATIC_DCL void +browse_map(ter_typ, ter_explain) +int ter_typ; +const char *ter_explain; +{ + coord dummy_pos; /* don't care whether player actually picks a spot */ + + dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */ + iflags.autodescribe = TRUE; + iflags.terrainmode = ter_typ; + getpos(&dummy_pos, FALSE, ter_explain); + iflags.terrainmode = 0; + /* leave iflags.autodescribe 'on' even if previously 'off' */ +} + +/* extracted from monster_detection() so can be shared by do_vicinity_map() */ +STATIC_DCL void +map_monst(mtmp, showtail) +struct monst *mtmp; +boolean showtail; +{ + if (def_monsyms[(int) mtmp->data->mlet].sym == ' ') + show_glyph(mtmp->mx, mtmp->my, detected_mon_to_glyph(mtmp)); + else + show_glyph(mtmp->mx, mtmp->my, + mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp)); + + if (showtail && mtmp->data == &mons[PM_LONG_WORM]) + detect_wsegs(mtmp, 0); +} + /* this is checking whether a trap symbol represents a trapped chest, not whether a trapped chest is actually present */ boolean @@ -29,12 +89,42 @@ trapped_chest_at(ttyp, x, y) int ttyp; int x, y; { + struct monst *mtmp; + struct obj *otmp; + if (!glyph_is_trap(glyph_at(x, y))) return FALSE; if (ttyp != BEAR_TRAP || (Hallucination && rn2(20))) return FALSE; - /* presence of any trappable container will do */ - return (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y)) ? TRUE : FALSE; + + /* + * TODO? We should check containers recursively like the trap + * detecting routine does. Chests and large boxes do not nest in + * themselves or each other, but could be contained inside statues. + * + * For farlook, we should also check for buried containers, but + * for '^' command to examine adjacent trap glyph, we shouldn't. + */ + + /* on map, presence of any trappable container will do */ + if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y)) + return TRUE; + /* in inventory, we need to find one which is actually trapped */ + if (x == u.ux && y == u.uy) { + for (otmp = invent; otmp; otmp = otmp->nobj) + if (Is_box(otmp) && otmp->otrapped) + return TRUE; + if (u.usteed) { /* steed isn't on map so won't be found by m_at() */ + for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj) + if (Is_box(otmp) && otmp->otrapped) + return TRUE; + } + } + if ((mtmp = m_at(x, y)) != 0) + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + if (Is_box(otmp) && otmp->otrapped) + return TRUE; + return FALSE; } /* this is checking whether a trap symbol represents a trapped door, @@ -202,27 +292,34 @@ register struct obj *sobj; { register struct obj *obj; register struct monst *mtmp; - struct obj *temp; - boolean stale; + struct obj gold, *temp = 0; + boolean stale, ugold = FALSE, steedgold = FALSE; + int ter_typ = TER_DETECT | TER_OBJ; - known = stale = - clear_stale_map(COIN_CLASS, (unsigned) (sobj->blessed ? GOLD : 0)); + known = stale = clear_stale_map(COIN_CLASS, + (unsigned) (sobj->blessed ? GOLD : 0)); /* look for gold carried by monsters (might be in a container) */ for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; /* probably not needed in this case but... */ if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { - known = TRUE; - goto outgoldmap; /* skip further searching */ + if (mtmp == u.usteed) { + steedgold = TRUE; + } else { + known = TRUE; + goto outgoldmap; /* skip further searching */ + } } else { for (obj = mtmp->minvent; obj; obj = obj->nobj) - if (sobj->blessed && o_material(obj, GOLD)) { - known = TRUE; - goto outgoldmap; - } else if (o_in(obj, COIN_CLASS)) { - known = TRUE; - goto outgoldmap; /* skip further searching */ + if ((sobj->blessed && o_material(obj, GOLD)) + || o_in(obj, COIN_CLASS)) { + if (mtmp == u.usteed) { + steedgold = TRUE; + } else { + known = TRUE; + goto outgoldmap; /* skip further searching */ + } } } } @@ -246,13 +343,21 @@ register struct obj *sobj; if (sobj) { char buf[BUFSZ]; - if (youmonst.data == &mons[PM_GOLD_GOLEM]) { + if (youmonst.data == &mons[PM_GOLD_GOLEM]) Sprintf(buf, "You feel like a million %s!", currency(2L)); - } else if (hidden_gold() || money_cnt(invent)) + else if (money_cnt(invent) || hidden_gold()) Strcpy(buf, "You feel worried about your future financial situation."); + else if (steedgold) + Sprintf(buf, "You feel interested in %s financial situation.", + s_suffix(x_monnam(u.usteed, + u.usteed->mtame ? ARTICLE_YOUR + : ARTICLE_THE, + (char *) 0, + SUPPRESS_SADDLE, FALSE))); else Strcpy(buf, "You feel materially poor."); + strange_feeling(sobj, buf); } return 1; @@ -266,8 +371,7 @@ register struct obj *sobj; outgoldmap: cls(); - iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied; - u.uinwater = u.uburied = 0; + (void) unconstrain_map(); /* Discover gold locations. */ for (obj = fobj; obj; obj = obj->nobj) { if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) { @@ -283,19 +387,21 @@ outgoldmap: } map_object(temp, 1); } + if (temp && temp->ox == u.ux && temp->oy == u.uy) + ugold = TRUE; } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; /* probably overkill here */ + temp = 0; if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) { - struct obj gold; - gold = zeroobj; /* ensure oextra is cleared too */ gold.otyp = GOLD_PIECE; gold.quan = (long) rnd(10); /* usually more than 1 */ gold.ox = mtmp->mx; gold.oy = mtmp->my; map_object(&gold, 1); + temp = &gold; } else { for (obj = mtmp->minvent; obj; obj = obj->nobj) if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) { @@ -310,13 +416,19 @@ outgoldmap: break; } } + if (temp && temp->ox == u.ux && temp->oy == u.uy) + ugold = TRUE; + } + if (!ugold) { + newsym(u.ux, u.uy); + ter_typ |= TER_MON; /* so autodescribe will recognize hero */ } - newsym(u.ux, u.uy); - u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied; - iflags.save_uinwater = iflags.save_uburied = 0; You_feel("very greedy, and sense gold!"); exercise(A_WIS, TRUE); - display_nhwindow(WIN_MAP, TRUE); + + browse_map(ter_typ, "gold"); + + reconstrain_map(); docrt(); if (Underwater) under_water(2); @@ -339,6 +451,8 @@ register struct obj *sobj; const char *what = confused ? something : "food"; stale = clear_stale_map(oclass, 0); + if (u.usteed) /* some situations leave steed with stale coordinates */ + u.usteed->mx = u.ux, u.usteed->my = u.uy; for (obj = fobj; obj; obj = obj->nobj) if (o_in(obj, oclass)) { @@ -347,12 +461,14 @@ register struct obj *sobj; else ct++; } - for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) { - /* no DEADMONSTER(mtmp) check needed since dmons never have inventory - */ + for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) { + /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */ for (obj = mtmp->minvent; obj; obj = obj->nobj) if (o_in(obj, oclass)) { - ct++; + if (mtmp->mx == u.ux && mtmp->my == u.uy) + ctu++; /* steed or an engulfer with inventory */ + else + ct++; break; } } @@ -369,6 +485,7 @@ register struct obj *sobj; } } else if (sobj) { char buf[BUFSZ]; + Sprintf(buf, "Your %s twitches%s.", body_part(NOSE), (sobj->blessed && !u.uedibility) ? " then starts to tingle" @@ -394,10 +511,11 @@ register struct obj *sobj; } } else { struct obj *temp; + int ter_typ = TER_DETECT | TER_OBJ; + known = TRUE; cls(); - iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied; - u.uinwater = u.uburied = 0; + (void) unconstrain_map(); for (obj = fobj; obj; obj = obj->nobj) if ((temp = o_in(obj, oclass)) != 0) { if (temp != obj) { @@ -407,8 +525,7 @@ register struct obj *sobj; map_object(temp, 1); } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - /* no DEADMONSTER(mtmp) check needed since dmons never have - * inventory */ + /* no DEADMONSTER() check needed -- dmons never have inventory */ for (obj = mtmp->minvent; obj; obj = obj->nobj) if ((temp = o_in(obj, oclass)) != 0) { temp->ox = mtmp->mx; @@ -416,9 +533,10 @@ register struct obj *sobj; map_object(temp, 1); break; /* skip rest of this monster's inventory */ } - newsym(u.ux, u.uy); - u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied; - iflags.save_uinwater = iflags.save_uburied = 0; + if (!ctu) { + newsym(u.ux, u.uy); + ter_typ |= TER_MON; /* for autodescribe of self */ + } if (sobj) { if (sobj->blessed) { Your("%s %s to tingle and you smell %s.", body_part(NOSE), @@ -428,8 +546,11 @@ register struct obj *sobj; Your("%s tingles and you smell %s.", body_part(NOSE), what); } else You("sense %s.", what); - display_nhwindow(WIN_MAP, TRUE); exercise(A_WIS, TRUE); + + browse_map(ter_typ, "food"); + + reconstrain_map(); docrt(); if (Underwater) under_water(2); @@ -459,7 +580,7 @@ int class; /* an object class, 0 for all */ int ct = 0, ctu = 0; register struct obj *obj, *otmp = (struct obj *) 0; register struct monst *mtmp; - int sym, boulder = 0; + int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ; if (class < 0 || class >= MAXOCLASSES) { impossible("object_detect: illegal class %d", class); @@ -509,6 +630,9 @@ int class; /* an object class, 0 for all */ do_dknown_of(obj); } + if (u.usteed) + u.usteed->mx = u.ux, u.usteed->my = u.uy; + for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; @@ -540,8 +664,7 @@ int class; /* an object class, 0 for all */ cls(); - iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied; - u.uinwater = u.uburied = 0; + (void) unconstrain_map(); /* * Map all buried objects first. */ @@ -618,16 +741,18 @@ int class; /* an object class, 0 for all */ map_object(&gold, 1); } } - - newsym(u.ux, u.uy); - u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied; - iflags.save_uinwater = iflags.save_uburied = 0; + if (!glyph_is_object(glyph_at(u.ux, u.uy))) { + newsym(u.ux, u.uy); + ter_typ |= TER_MON; + } You("detect the %s of %s.", ct ? "presence" : "absence", stuff); - display_nhwindow(WIN_MAP, TRUE); - /* - * What are we going to do when the hero does an object detect while blind - * and the detected object covers a known pool? - */ + + if (!ct) + display_nhwindow(WIN_MAP, TRUE); + else + browse_map(ter_typ, "object"); + + reconstrain_map(); docrt(); /* this will correctly reset vision */ if (Underwater) under_water(2); @@ -668,26 +793,19 @@ int mclass; /* monster class, 0 for all */ : "You feel threatened."); return 1; } else { - boolean woken = FALSE; + boolean unconstrained, woken = FALSE; + unsigned swallowed = u.uswallow; /* before unconstrain_map() */ cls(); + unconstrained = unconstrain_map(); for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { if (DEADMONSTER(mtmp)) continue; if (!mclass || mtmp->data->mlet == mclass || (mtmp->data == &mons[PM_LONG_WORM] && mclass == S_WORM_TAIL)) - if (mtmp->mx > 0) { - if (mclass && def_monsyms[mclass].sym == ' ') - show_glyph(mtmp->mx, mtmp->my, - detected_mon_to_glyph(mtmp)); - else - show_glyph(mtmp->mx, mtmp->my, - mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp)); - /* don't be stingy - display entire worm */ - if (mtmp->data == &mons[PM_LONG_WORM]) - detect_wsegs(mtmp, 0); - } + map_monst(mtmp, TRUE); + if (otmp && otmp->cursed && (mtmp->msleeping || !mtmp->mcanmove)) { mtmp->msleeping = mtmp->mfrozen = 0; @@ -695,12 +813,25 @@ int mclass; /* monster class, 0 for all */ woken = TRUE; } } - display_self(); + if (!swallowed) + display_self(); You("sense the presence of monsters."); if (woken) pline("Monsters sense the presence of you."); - display_nhwindow(WIN_MAP, TRUE); - docrt(); + + if ((otmp && otmp->blessed) && !unconstrained) { + /* persistent detection--just show updated map */ + display_nhwindow(WIN_MAP, TRUE); + } else { + /* one-shot detection--allow player to move cursor around and + get autodescribe feedback */ + EDetect_monsters |= I_SPECIAL; + browse_map(TER_DETECT | TER_MON, "monster of interest"); + EDetect_monsters &= ~I_SPECIAL; + } + + reconstrain_map(); + docrt(); /* redraw the screen to remove unseen monsters from map */ if (Underwater) under_water(2); if (u.uburied) @@ -734,7 +865,7 @@ int src_cursed; } else if (trap) { map_trap(trap, 1); trap->tseen = 1; - } else { + } else { /* trapped door or trapped chest */ struct trap temp_trap; /* fake trap */ (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap); @@ -762,6 +893,11 @@ int how; /* 1 for misleading map feedback */ xchar x, y; int result = OTRAP_NONE; + /* + * TODO? Display locations of unarmed land mine and beartrap objects. + * If so, should they be displayed as objects or as traps? + */ + for (otmp = objlist; otmp; otmp = otmp->nobj) { if (Is_box(otmp) && otmp->otrapped && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) { @@ -786,11 +922,14 @@ struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */ { register struct trap *ttmp; struct monst *mon; - int door, glyph, tr; + int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP; int cursed_src = sobj && sobj->cursed; boolean found = FALSE; coord cc; + if (u.usteed) + u.usteed->mx = u.ux, u.usteed->my = u.uy; + /* floor/ceiling traps */ for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) { if (ttmp->tx != u.ux || ttmp->ty != u.uy) @@ -847,9 +986,7 @@ struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */ outtrapmap: cls(); - iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied; - u.uinwater = u.uburied = 0; - + (void) unconstrain_map(); /* show chest traps first, so that subsequent floor trap display will override if both types are present at the same location */ (void) detect_obj_traps(fobj, TRUE, cursed_src); @@ -872,15 +1009,16 @@ outtrapmap: /* redisplay hero unless sense_trap() revealed something at */ glyph = glyph_at(u.ux, u.uy); - if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) + if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) { newsym(u.ux, u.uy); - u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied; - iflags.save_uinwater = iflags.save_uburied = 0; - + ter_typ |= TER_MON; /* for autodescribe at */ + } You_feel("%s.", cursed_src ? "very greedy" : "entrapped"); - /* wait for user to respond, then reset map display to normal */ - display_nhwindow(WIN_MAP, TRUE); - docrt(); + + browse_map(ter_typ, "trap of interest"); + + reconstrain_map(); + docrt(); /* redraw the screen to remove unseen traps from the map */ if (Underwater) under_water(2); if (u.uburied) @@ -1132,40 +1270,67 @@ void do_mapping() { register int zx, zy; + boolean unconstrained; - iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied; - u.uinwater = u.uburied = 0; + unconstrained = unconstrain_map(); for (zx = 1; zx < COLNO; zx++) for (zy = 0; zy < ROWNO; zy++) show_map_spot(zx, zy); - u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied; - iflags.save_uinwater = iflags.save_uburied = 0; - if (!level.flags.hero_memory || Underwater) { + + if (!level.flags.hero_memory || unconstrained) { flush_screen(1); /* flush temp screen */ - display_nhwindow(WIN_MAP, TRUE); /* wait */ + /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */ + browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ, + "anything of interest"); docrt(); } + reconstrain_map(); exercise(A_WIS, TRUE); } +/* clairvoyance */ void -do_vicinity_map() +do_vicinity_map(sobj) +struct obj *sobj; /* scroll--actually fake spellbook--object */ { register int zx, zy; - int lo_y = (u.uy - 5 < 0 ? 0 : u.uy - 5), - hi_y = (u.uy + 6 > ROWNO ? ROWNO : u.uy + 6), - lo_x = (u.ux - 9 < 1 ? 1 : u.ux - 9), /* avoid column 0 */ - hi_x = (u.ux + 10 > COLNO ? COLNO : u.ux + 10); - - for (zx = lo_x; zx < hi_x; zx++) - for (zy = lo_y; zy < hi_y; zy++) + struct monst *mtmp; + boolean unconstrained, refresh = FALSE, mdetected = FALSE, + extended = (sobj && sobj->blessed); + int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5), + hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6), + lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */ + hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10), + ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ; + + unconstrained = unconstrain_map(); + for (zx = lo_x; zx <= hi_x; zx++) + for (zy = lo_y; zy <= hi_y; zy++) { show_map_spot(zx, zy); - if (!level.flags.hero_memory || Underwater) { + if (extended && (mtmp = m_at(zx, zy)) != 0 + && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */ + int oldglyph = glyph_at(zx, zy); + + map_monst(mtmp, FALSE); + if (glyph_at(zx, zy) != oldglyph) + mdetected = TRUE; + } + } + + if (!level.flags.hero_memory || unconstrained || mdetected) { flush_screen(1); /* flush temp screen */ - display_nhwindow(WIN_MAP, TRUE); /* wait */ - docrt(); + if (extended || glyph_is_monster(glyph_at(u.ux, u.uy))) + ter_typ |= TER_MON; + if (extended) + EDetect_monsters |= I_SPECIAL; + browse_map(ter_typ, "anything of interest"); + EDetect_monsters &= ~I_SPECIAL; + refresh = TRUE; } + reconstrain_map(); + if (refresh) + docrt(); } /* convert a secret door into a normal door */ @@ -1379,12 +1544,10 @@ boolean via_warning; exercise(A_WIS, TRUE); if (!canspotmon(mtmp)) { if (glyph_is_invisible(levl[x][y].glyph)) { - /* found invisible monster in a square - * which already has an 'I' in it. - * Logically, this should still take - * time and lead to a return(1), but - * if we did that the player would keep - * finding the same monster every turn. + /* Found invisible monster in a square which already has + * an 'I' in it. Logically, this should still take time + * and lead to a return 1, but if we did that the player + * would keep finding the same monster every turn. */ return -1; } else { @@ -1392,18 +1555,16 @@ boolean via_warning; map_invisible(x, y); } } else if (!sensemon(mtmp)) - You("find %s.", mtmp->mtame - ? y_monnam(mtmp) - : a_monnam(mtmp)); + You("find %s.", + mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp)); return 1; } if (!canspotmon(mtmp)) { if (mtmp->mundetected - && (is_hider(mtmp->data) - || mtmp->data->mlet == S_EEL)) + && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL)) if (via_warning) { Your("warning senses cause you to take a second %s.", - Blind ? "to check nearby" : "look close by"); + Blind ? "to check nearby" : "look close by"); display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */ } mtmp->mundetected = 0; @@ -1418,10 +1579,10 @@ dosearch0(aflag) register int aflag; /* intrinsic autosearch vs explicit searching */ { #ifdef GCC_BUG - /* some versions of gcc seriously muck up nested loops. if you get strange - crashes while searching in a version compiled with gcc, try putting - #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the - makefile). + /* Some old versions of gcc seriously muck up nested loops. If you get + * strange crashes while searching in a version compiled with gcc, try + * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in + * the makefile). */ volatile xchar x, y; #else @@ -1566,21 +1727,16 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */ } else { int x, y, glyph, levl_glyph, default_glyph; uchar seenv; - unsigned save_swallowed; struct monst *mtmp; struct trap *t; - coord pos; char buf[BUFSZ]; /* there is a TER_MAP bit too; we always show map regardless of it */ boolean keep_traps = (which_subset & TER_TRP) !=0, keep_objs = (which_subset & TER_OBJ) != 0, keep_mons = (which_subset & TER_MON) != 0; /* not used */ + unsigned swallowed = u.uswallow; /* before unconstrain_map() */ - save_swallowed = u.uswallow; - iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied; - u.uinwater = u.uburied = 0; - u.uswallow = 0; - if (iflags.save_uinwater || iflags.save_uburied) + if (unconstrain_map()) docrt(); default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone); /* for 'full', show the actual terrain for the entire level, @@ -1605,8 +1761,8 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */ glyph, which will never be a monster (unless it is the invisible monster glyph, which is handled like an object, replacing any object or trap at its spot) */ - glyph = !save_swallowed ? glyph_at(x, y) : levl_glyph; - if (keep_mons && x == u.ux && y == u.uy && save_swallowed) + glyph = !swallowed ? glyph_at(x, y) : levl_glyph; + if (keep_mons && x == u.ux && y == u.uy && swallowed) glyph = mon_to_glyph(u.ustuck); else if (((glyph_is_monster(glyph) || glyph_is_warning(glyph)) && !keep_mons) @@ -1676,17 +1832,10 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */ /* allow player to move cursor around and get autodescribe feedback based on what is visible now rather than what is on 'real' map */ - pos.x = u.ux, pos.y = u.uy; - iflags.autodescribe = TRUE; - iflags.terrainmode = which_subset | TER_MAP; /* guaranteed non-zero */ - getpos(&pos, FALSE, "anything of interest"); - iflags.terrainmode = 0; - /* leave iflags.autodescribe 'on' even if it was previously 'off' */ - - u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied; - iflags.save_uinwater = iflags.save_uburied = 0; - if (save_swallowed) - u.uswallow = 1; + which_subset |= TER_MAP; /* guarantee non-zero */ + browse_map(which_subset, "anything of interest"); + + reconstrain_map(); docrt(); /* redraw the screen, restoring regular map */ if (Underwater) under_water(2); diff --git a/src/pager.c b/src/pager.c index 8ffe31409..dac2bc3b5 100644 --- a/src/pager.c +++ b/src/pager.c @@ -74,11 +74,13 @@ char *outbuf; mons[u.umonnum].mname, plname); if (u.usteed) Sprintf(eos(outbuf), ", mounted on %s", y_monnam(u.usteed)); + if (u.uundetected || (Upolyd && youmonst.m_ap_type)) + mhidden_description(&youmonst, FALSE, eos(outbuf)); return outbuf; } /* describe a hidden monster; used for look_at during extended monster - detection and for probing */ + detection and for probing; also when looking at self */ void mhidden_description(mon, altmon, outbuf) struct monst *mon; @@ -86,8 +88,10 @@ boolean altmon; /* for probing: if mimicking a monster, say so */ char *outbuf; { struct obj *otmp; - boolean fakeobj; - int x = mon->mx, y = mon->my, glyph = levl[x][y].glyph; + boolean fakeobj, isyou = (mon == &youmonst); + int x = isyou ? u.ux : mon->mx, y = isyou ? u.uy : mon->my, + glyph = (level.flags.hero_memory && !isyou) ? levl[x][y].glyph + : glyph_at(x, y); *outbuf = '\0'; if (mon->m_ap_type == M_AP_FURNITURE @@ -113,7 +117,7 @@ char *outbuf; if (altmon) Sprintf(outbuf, ", masquerading as %s", an(mons[mon->mappearance].mname)); - } else if (mon->mundetected) { + } else if (isyou ? u.uundetected : mon->mundetected) { Strcpy(outbuf, ", hiding"); if (hides_under(mon->data)) { Strcat(outbuf, " under "); @@ -141,10 +145,16 @@ struct obj **obj_p; { boolean fakeobj = FALSE; struct monst *mtmp; - struct obj *otmp = vobj_at(x, y); + struct obj *otmp; int glyphotyp = glyph_to_obj(glyph); *obj_p = (struct obj *) 0; + /* TODO: check inside containers in case glyph came from detection */ + if ((otmp = sobj_at(glyphotyp, x, y)) == 0) + for (otmp = level.buriedobjlist; otmp; otmp = otmp->nobj) + if (otmp->ox == x && otmp->oy == y && otmp->otyp == glyphotyp) + break; + /* there might be a mimic here posing as an object */ mtmp = m_at(x, y); if (mtmp && is_obj_mappear(mtmp, (unsigned) glyphotyp)) @@ -194,7 +204,9 @@ int x, y, glyph; } else Strcpy(buf, something); /* sanity precaution */ - if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) + if (otmp && otmp->where == OBJ_BURIED) + Strcat(buf, " (buried)"); + else if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) Strcat(buf, " embedded in stone"); else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) Strcat(buf, " embedded in a wall"); @@ -229,9 +241,14 @@ int x, y; ? "peaceful " : "", name); - if (u.ustuck == mtmp) - Strcat(buf, (Upolyd && sticks(youmonst.data)) - ? ", being held" : ", holding you"); + if (u.ustuck == mtmp) { + if (u.uswallow || iflags.save_uswallow) /* monster detection */ + Strcat(buf, is_animal(mtmp->data) + ? ", swallowing you" : ", engulfing you"); + else + Strcat(buf, (Upolyd && sticks(youmonst.data)) + ? ", being held" : ", holding you"); + } if (mtmp->mleashed) Strcat(buf, ", leashed to you"); @@ -328,6 +345,7 @@ char *buf, *monbuf; buf[0] = monbuf[0] = '\0'; glyph = glyph_at(x, y); if (u.ux == x && u.uy == y && canspotself() + && !(iflags.save_uswallow && glyph == mon_to_glyph(u.ustuck)) && (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0)) { /* fill in buf[] */ (void) self_lookat(buf); @@ -342,7 +360,8 @@ char *buf, *monbuf; (even if you could also see yourself via other means). Sensing self while blind or swallowed is treated as if it were by normal vision (cf canseeself()). */ - if ((Invisible || u.uundetected) && !Blind && !u.uswallow) { + if ((Invisible || u.uundetected) && !Blind + && !(u.uswallow || iflags.save_uswallow)) { unsigned how = 0; if (Infravision) @@ -702,10 +721,15 @@ const char **firstmatch; * submerged will always both be False and skip this code.) */ x_str = 0; - if (looked && (u.uswallow || submerged) && distu(cc.x, cc.y) > 2) { + if (!looked) { + ; /* skip special handling */ + } else if (((u.uswallow || submerged) && distu(cc.x, cc.y) > 2) + /* detection showing some category, so mostly background */ + || ((iflags.terrainmode & (TER_DETECT | TER_MAP)) == TER_DETECT + && glyph == cmap_to_glyph(S_stone))) { x_str = unreconnoitered; need_to_look = FALSE; - } else if (looked && u.uswallow && is_swallow_sym(sym)) { + } else if (is_swallow_sym(sym)) { x_str = mon_interior; need_to_look = TRUE; /* for specific monster type */ } @@ -943,9 +967,7 @@ coord *click_cc; if (quick) { from_screen = TRUE; /* yes, we want to use the cursor */ i = 'y'; - } - - if (i != 'y') { + } else { menu_item *pick_list = (menu_item *) 0; winid win; anything any; diff --git a/src/spell.c b/src/spell.c index a8adba34c..e0abcb541 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1136,6 +1136,7 @@ boolean atme; case SPE_INVISIBILITY: (void) peffects(pseudo); break; + /* end of potion-like spells */ case SPE_CURE_BLINDNESS: healup(0, 0, FALSE, TRUE); @@ -1151,10 +1152,12 @@ boolean atme; (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE); break; case SPE_CLAIRVOYANCE: - if (!BClairvoyant) - do_vicinity_map(); + if (!BClairvoyant) { + if (role_skill >= P_SKILLED) + pseudo->blessed = 1; /* detect monsters as well as map */ + do_vicinity_map(pseudo); /* at present, only one thing blocks clairvoyance */ - else if (uarmh && uarmh->otyp == CORNUTHAUM) + } else if (uarmh && uarmh->otyp == CORNUTHAUM) You("sense a pointy hat on top of your %s.", body_part(HEAD)); break; case SPE_PROTECTION: