trap/secret door detection enhancement

The old secret door detection just redisplayed locations with
discoveries (secret doors and traps, mostly).  Somewhere along the
line it was augmented to find hidden monsters and to deliver one or
two messages reporting how many things had been discovered.  Now it
has been augmented again, to find trapped doors and chests, and to
supply a message when the detection attempt fails to find anything.

More substantially, it highlights the relevant locations as they're
found, before the feedback message(s).

Initially I was using tmp_at() to mark all significant locations,
but that required --More-- and for player to acknowledge it when
detection was done.  That would probably be ok for wand of secret
door detection and spell of detect unseen, but it would be a hassle
for ^E.  It's been revised to use flash_glyph_at() [previously only
used when ^G creates unseen monsters, I think].

The new behavior seems to be working reasonably well.  For curses,
the 'timed_delay' option must be set.  flash_glyph_at() calls
flush_screen() between its output and nap in each cycle of multiple
flashes, but that evidently isn't sufficient for curses.  Maybe
curses init should just force on 'timed_delay'.

I've left the tmp_at() stuff in.  We might want to modify things to
use it instead of flash_glyph_at() when the accessibility flag is
set.  Its current compile-time selection won't be adequate though.
This commit is contained in:
PatR
2024-08-29 14:08:23 -07:00
parent 0dff31a745
commit 1fd943e860
2 changed files with 158 additions and 49 deletions
+2
View File
@@ -2664,6 +2664,8 @@ if hero is punished or tethered to a buried iron ball and has no inventory (or
during streaming video during streaming video
'query_menu' option to use a menu when asked certain yes/no questions 'query_menu' option to use a menu when asked certain yes/no questions
pyrolisk eggs explode when broken pyrolisk eggs explode when broken
wand of secret door detection, spell of detect unseen, and wizard mode ^E now
flash the cursor at each location where detection finds something
Platform- and/or Interface-Specific New Features Platform- and/or Interface-Specific New Features
+156 -49
View File
@@ -11,6 +11,16 @@
#include "hack.h" #include "hack.h"
#include "artifact.h" #include "artifact.h"
#ifndef FOUND_FLASH_COUNT
/* for screen alert shown to player when secret door detection or ^E
finds stuff; to use tmp_at() instead of flash_glyph_at(), define as 0;
extra code for tmp_at() will be included and the flash_glyph_at()
calls will execute but won't do anything */
#define FOUND_FLASH_COUNT 6
#endif
struct found_things;
staticfn boolean unconstrain_map(void); staticfn boolean unconstrain_map(void);
staticfn void reconstrain_map(void); staticfn void reconstrain_map(void);
staticfn void map_redisplay(void); staticfn void map_redisplay(void);
@@ -20,13 +30,16 @@ staticfn void do_dknown_of(struct obj *);
staticfn boolean check_map_spot(coordxy, coordxy, char, unsigned); staticfn boolean check_map_spot(coordxy, coordxy, char, unsigned);
staticfn boolean clear_stale_map(char, unsigned); staticfn boolean clear_stale_map(char, unsigned);
staticfn void sense_trap(struct trap *, coordxy, coordxy, int); staticfn void sense_trap(struct trap *, coordxy, coordxy, int);
staticfn int detect_obj_traps(struct obj *, boolean, int); staticfn int detect_obj_traps(struct obj *, boolean, int,
struct found_things *) NO_NNARGS;
staticfn void display_trap_map(int); staticfn void display_trap_map(int);
staticfn int furniture_detect(void); staticfn int furniture_detect(void);
staticfn void foundone(coordxy, coordxy, int);
staticfn void findone(coordxy, coordxy, genericptr_t); staticfn void findone(coordxy, coordxy, genericptr_t);
staticfn void openone(coordxy, coordxy, genericptr_t); staticfn void openone(coordxy, coordxy, genericptr_t);
staticfn int mfind0(struct monst *, boolean); staticfn int mfind0(struct monst *, boolean);
staticfn int reveal_terrain_getglyph(coordxy, coordxy, unsigned, int, unsigned); staticfn int reveal_terrain_getglyph(coordxy, coordxy, unsigned, int,
unsigned);
/* dummytrap: used when detecting traps finds a door or chest trap; the /* dummytrap: used when detecting traps finds a door or chest trap; the
couple of fields that matter are always re-initialized during use so couple of fields that matter are always re-initialized during use so
@@ -36,6 +49,7 @@ static struct trap dummytrap;
/* data for enhanced feedback from findone() */ /* data for enhanced feedback from findone() */
struct found_things { struct found_things {
coord ft_cc; /* for passing extra info to detect_obj_traps() */
uchar num_sdoors; uchar num_sdoors;
uchar num_scorrs; uchar num_scorrs;
uchar num_traps; uchar num_traps;
@@ -892,11 +906,12 @@ staticfn int
detect_obj_traps( detect_obj_traps(
struct obj *objlist, struct obj *objlist,
boolean show_them, boolean show_them,
int how) /* 1 for misleading map feedback */ int how, /* 1 for misleading map feedback */
struct found_things *ft) /* being called by findone() when non-Null */
{ {
struct obj *otmp; struct obj *otmp;
coordxy x, y; coordxy x, y;
int result = OTRAP_NONE; int trapglyph, result = OTRAP_NONE;
/* /*
* TODO? Display locations of unarmed land mine and beartrap objects. * TODO? Display locations of unarmed land mine and beartrap objects.
@@ -904,17 +919,32 @@ detect_obj_traps(
*/ */
dummytrap.ttyp = TRAPPED_CHEST; dummytrap.ttyp = TRAPPED_CHEST;
trapglyph = ft ? trap_to_glyph(&dummytrap) : GLYPH_NOTHING;
for (otmp = objlist; otmp; otmp = otmp->nobj) { for (otmp = objlist; otmp; otmp = otmp->nobj) {
if (Is_box(otmp) && otmp->otrapped x = y = 0; /* lint suppression */
&& get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) { if ((Is_box(otmp) && otmp->otrapped) || Has_contents(otmp)) {
/* !get_obj_location and !isok should both be impossible here */
if (!get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)
|| !isok(x, y)
|| (ft && (x != ft->ft_cc.x || y != ft->ft_cc.y)))
continue;
}
if (Is_box(otmp) && otmp->otrapped) {
result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE; result |= u_at(x, y) ? OTRAP_HERE : OTRAP_THERE;
if (ft) {
flash_glyph_at(x, y, trapglyph, FOUND_FLASH_COUNT);
}
if (show_them) { if (show_them) {
dummytrap.tx = x, dummytrap.ty = y; dummytrap.tx = x, dummytrap.ty = y;
sense_trap(&dummytrap, x, y, how); sense_trap(&dummytrap, x, y, how);
} }
if (ft) {
foundone(x, y, trapglyph);
ft->num_traps++;
}
} }
if (Has_contents(otmp)) if (Has_contents(otmp))
result |= detect_obj_traps(otmp->cobj, show_them, how); result |= detect_obj_traps(otmp->cobj, show_them, how, ft);
} }
return result; return result;
} }
@@ -924,7 +954,7 @@ display_trap_map(int cursed_src)
{ {
struct monst *mon; struct monst *mon;
struct trap *ttmp; struct trap *ttmp;
int door, glyph, ter_typ = TER_DETECT | ( cursed_src ? TER_OBJ : TER_TRP ); int door, glyph, ter_typ = TER_DETECT | (cursed_src ? TER_OBJ : TER_TRP);
coord cc; coord cc;
cls(); cls();
@@ -933,14 +963,14 @@ display_trap_map(int cursed_src)
/* show chest traps first, first buried chests then floor chests, so /* show chest traps first, first buried chests then floor chests, so
that subsequent floor trap display will override if both types are that subsequent floor trap display will override if both types are
present at the same location */ present at the same location */
(void) detect_obj_traps(svl.level.buriedobjlist, TRUE, cursed_src); (void) detect_obj_traps(svl.level.buriedobjlist, TRUE, cursed_src, NULL);
(void) detect_obj_traps(fobj, TRUE, cursed_src); (void) detect_obj_traps(fobj, TRUE, cursed_src, NULL);
for (mon = fmon; mon; mon = mon->nmon) { for (mon = fmon; mon; mon = mon->nmon) {
if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) if (DEADMONSTER(mon) || (mon->isgd && !mon->mx))
continue; continue;
(void) detect_obj_traps(mon->minvent, TRUE, cursed_src); (void) detect_obj_traps(mon->minvent, TRUE, cursed_src, NULL);
} }
(void) detect_obj_traps(gi.invent, TRUE, cursed_src); (void) detect_obj_traps(gi.invent, TRUE, cursed_src, NULL);
for (ttmp = gf.ftrap; ttmp; ttmp = ttmp->ntrap) for (ttmp = gf.ftrap; ttmp; ttmp = ttmp->ntrap)
sense_trap(ttmp, 0, 0, cursed_src); sense_trap(ttmp, 0, 0, cursed_src);
@@ -948,7 +978,7 @@ display_trap_map(int cursed_src)
dummytrap.ttyp = TRAPPED_DOOR; dummytrap.ttyp = TRAPPED_DOOR;
for (door = 0; door < gd.doorindex; door++) { for (door = 0; door < gd.doorindex; door++) {
cc = svd.doors[door]; cc = svd.doors[door];
if (levl[cc.x][cc.y].typ == SDOOR) /* see above */ if (levl[cc.x][cc.y].typ == SDOOR) /* can't be trapped; see above */
continue; continue;
if (levl[cc.x][cc.y].doormask & D_TRAPPED) { if (levl[cc.x][cc.y].doormask & D_TRAPPED) {
dummytrap.tx = cc.x, dummytrap.ty = cc.y; dummytrap.tx = cc.x, dummytrap.ty = cc.y;
@@ -997,14 +1027,14 @@ trap_detect(
found = TRUE; found = TRUE;
} }
/* chest traps (might be buried or carried) */ /* chest traps (might be buried or carried) */
if ((tr = detect_obj_traps(fobj, FALSE, 0)) != OTRAP_NONE) { if ((tr = detect_obj_traps(fobj, FALSE, 0, NULL)) != OTRAP_NONE) {
if (tr & OTRAP_THERE) { if (tr & OTRAP_THERE) {
display_trap_map(cursed_src); display_trap_map(cursed_src);
return 0; return 0;
} }
found = TRUE; found = TRUE;
} }
if ((tr = detect_obj_traps(svl.level.buriedobjlist, FALSE, 0)) if ((tr = detect_obj_traps(svl.level.buriedobjlist, FALSE, 0, NULL))
!= OTRAP_NONE) { != OTRAP_NONE) {
if (tr & OTRAP_THERE) { if (tr & OTRAP_THERE) {
display_trap_map(cursed_src); display_trap_map(cursed_src);
@@ -1015,7 +1045,8 @@ trap_detect(
for (mon = fmon; mon; mon = mon->nmon) { for (mon = fmon; mon; mon = mon->nmon) {
if (DEADMONSTER(mon) || (mon->isgd && !mon->mx)) if (DEADMONSTER(mon) || (mon->isgd && !mon->mx))
continue; continue;
if ((tr = detect_obj_traps(mon->minvent, FALSE, 0)) != OTRAP_NONE) { if ((tr = detect_obj_traps(mon->minvent, FALSE, 0, NULL))
!= OTRAP_NONE) {
if (tr & OTRAP_THERE) { if (tr & OTRAP_THERE) {
display_trap_map(cursed_src); display_trap_map(cursed_src);
return 0; return 0;
@@ -1023,7 +1054,7 @@ trap_detect(
found = TRUE; found = TRUE;
} }
} }
if (detect_obj_traps(gi.invent, FALSE, 0) != OTRAP_NONE) if (detect_obj_traps(gi.invent, FALSE, 0, NULL) != OTRAP_NONE)
found = TRUE; found = TRUE;
/* door traps */ /* door traps */
for (door = 0; door < gd.doorindex; door++) { for (door = 0; door < gd.doorindex; door++) {
@@ -1568,58 +1599,113 @@ cvt_sdoor_to_door(struct rm *lev)
lev->doormask = newmask; lev->doormask = newmask;
} }
/* find something at one location; it should find all somethings there /* update the map for something which has just been found by wand of secret
door detection or wizard mode ^E; will be called multiple times during a
single operation if multiple things of interest are discovered */
staticfn void
foundone(coordxy zx, coordxy zy, int glyph)
{
if (glyph_is_cmap(glyph) || glyph_is_unexplored(glyph))
levl[zx][zy].seenv = SVALL;
if (!Blind) {
seenV save_viz = gv.viz_array[zy][zx];
gv.viz_array[zy][zx] = COULD_SEE | IN_SIGHT;
newsym(zx, zy);
gv.viz_array[zy][zx] = save_viz;
}
#if FOUND_FLASH_COUNT == 0
/*
* This works [for non-monsters at present] but flash_glyph_at()
* seems preferrable because the tmp_at() variation requires that
* the player respond to --More-- at the end, the flash_glyph
* variation doesn't.
*/
tmp_at(DISP_CHANGE, glyph);
tmp_at(zx, zy);
#endif
}
/* find something at one location; this should find all somethings there
since it is used for magical detection rather than physical searching */ since it is used for magical detection rather than physical searching */
staticfn void staticfn void
findone(coordxy zx, coordxy zy, genericptr_t whatfound) findone(coordxy zx, coordxy zy, genericptr_t whatfound)
{ {
struct trap *ttmp; struct rm *lev = &levl[zx][zy];
struct monst *mtmp; struct trap *ttmp = t_at(zx, zy);
struct monst *mtmp = m_at(zx, zy);
struct found_things *found_p = (struct found_things *) whatfound; struct found_things *found_p = (struct found_things *) whatfound;
/* if (mtmp && (DEADMONSTER(mtmp) || (mtmp->isgd && !mtmp->mx)))
* This used to use if/else-if/else-if/else/end-if but that only mtmp = (struct monst *) NULL;
* found the first hidden thing at the location. Two hidden things found_p->ft_cc.x = zx; /* needed by detect_obj_traps() */
* at the same spot is uncommon, but it's possible for an undetected found_p->ft_cc.y = zy;
* monster to be hiding at the location of an unseen trap.
*/
if (levl[zx][zy].typ == SDOOR) { if (lev->typ == SDOOR) {
cvt_sdoor_to_door(&levl[zx][zy]); /* .typ = DOOR */ nhsym sym = lev->horizontal ? S_hcdoor : S_vcdoor;
flash_glyph_at(zx, zy, cmap_to_glyph(sym), FOUND_FLASH_COUNT);
cvt_sdoor_to_door(lev); /* set lev->typ = DOOR */
magic_map_background(zx, zy, 0); magic_map_background(zx, zy, 0);
newsym(zx, zy); foundone(zx, zy, back_to_glyph(zx, zy));
found_p->num_sdoors++; found_p->num_sdoors++;
} else if (levl[zx][zy].typ == SCORR) { } else if (lev->typ == SCORR) {
levl[zx][zy].typ = CORR; flash_glyph_at(zx, zy, cmap_to_glyph(S_corr), FOUND_FLASH_COUNT);
lev->typ = CORR;
unblock_point(zx, zy); unblock_point(zx, zy);
magic_map_background(zx, zy, 0); magic_map_background(zx, zy, 0);
newsym(zx, zy); foundone(zx, zy, cmap_to_glyph(S_corr));
found_p->num_scorrs++; found_p->num_scorrs++;
} }
if ((ttmp = t_at(zx, zy)) != 0 && !ttmp->tseen if (ttmp && !ttmp->tseen
/* [shouldn't successful 'find' reveal and activate statue traps?] */ /* [shouldn't successful 'find' reveal and activate statue traps?] */
&& ttmp->ttyp != STATUE_TRAP) { && ttmp->ttyp != STATUE_TRAP) {
flash_glyph_at(zx, zy, trap_to_glyph(ttmp), FOUND_FLASH_COUNT);
ttmp->tseen = 1; ttmp->tseen = 1;
newsym(zx, zy); foundone(zx, zy, trap_to_glyph(ttmp));
found_p->num_traps++; found_p->num_traps++;
} }
if (closed_door(zx, zy) && (lev->doormask & D_TRAPPED) != 0) {
dummytrap.ttyp = TRAPPED_DOOR;
dummytrap.tx = zx, dummytrap.ty = zy;
flash_glyph_at(zx, zy, trap_to_glyph(&dummytrap), FOUND_FLASH_COUNT);
dummytrap.tseen = 1;
map_trap(&dummytrap, 1);
sense_trap(&dummytrap, zx, zy, 0); /* handles Hallucination */
foundone(zx, zy, trap_to_glyph(&dummytrap));
found_p->num_traps++;
}
/* trapped chests */
(void) detect_obj_traps(svl.level.buriedobjlist, TRUE, 0, found_p);
(void) detect_obj_traps(fobj, TRUE, 0, found_p);
if (mtmp)
(void) detect_obj_traps(mtmp->minvent, TRUE, 0, found_p);
if (u_at(zx, zy))
(void) detect_obj_traps(gi.invent, TRUE, 0, found_p);
if ((mtmp = m_at(zx, zy)) != 0 if (mtmp && (!canspotmon(mtmp) || mtmp->mundetected || M_AP_TYPE(mtmp))) {
/* brings hidden monster out of hiding even if already sensed */
&& (!canspotmon(mtmp) || mtmp->mundetected || M_AP_TYPE(mtmp))) {
if (M_AP_TYPE(mtmp)) { if (M_AP_TYPE(mtmp)) {
flash_glyph_at(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng),
FOUND_FLASH_COUNT);
seemimic(mtmp); seemimic(mtmp);
/*foundone(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng);*/
found_p->num_mons++; found_p->num_mons++;
} else if (mtmp->mundetected && (is_hider(mtmp->data) } else if (mtmp->mundetected && (is_hider(mtmp->data)
|| hides_under(mtmp->data) || hides_under(mtmp->data)
|| mtmp->data->mlet == S_EEL)) { || mtmp->data->mlet == S_EEL)) {
flash_glyph_at(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng),
FOUND_FLASH_COUNT);
mtmp->mundetected = 0; mtmp->mundetected = 0;
/*foundone(zx, zy, mon_to_glyph(mtmp, rn2_on_display_rng);*/
newsym(zx, zy); newsym(zx, zy);
found_p->num_mons++; found_p->num_mons++;
} }
if (!glyph_is_invisible(levl[zx][zy].glyph)) { if (!glyph_is_invisible(lev->glyph)) {
if (!canspotmon(mtmp)) { if (!canspotmon(mtmp)) {
flash_glyph_at(zx, zy, GLYPH_INVISIBLE, FOUND_FLASH_COUNT);
map_invisible(zx, zy); map_invisible(zx, zy);
found_p->num_invis++; found_p->num_invis++;
} }
@@ -1627,6 +1713,8 @@ findone(coordxy zx, coordxy zy, genericptr_t whatfound)
found_p->num_kept_invis++; found_p->num_kept_invis++;
} }
} else if (unmap_invisible(zx, zy)) { } else if (unmap_invisible(zx, zy)) {
/* flash the invisible monster glyph because it is already gone */
flash_glyph_at(zx, zy, GLYPH_INVISIBLE, FOUND_FLASH_COUNT);
found_p->num_cleared_invis++; found_p->num_cleared_invis++;
} }
} }
@@ -1702,20 +1790,21 @@ findit(void)
struct found_things found; struct found_things found;
/* /*
* FIXME: * findit() -> do_clear_area(findone) -> findone() -> foundone()
* When things are found, this should show the updated map and allow * is used to notify player where various things have been found.
* browsing. * Changing FOUND_FLASH_COUNT to 0 will switch to tmp_at() to
* * highlight all discoveries for the current operation, but requires
* Currently, "You reveal a trap!" will map the trap but not reveal it * player to respond to --More-- when done. Neither allows browsing
* if that trap is covered by something, which is fairly common for * the map via getpos() autodescribe (until after it has reverted to
* early levels where corpses of fake heroes usually hide the traps * normal display, where found traps might be covered by objects).
* that killed them. That's most likely to occur for wizard mode ^E
* but can happen in normal play by using wand of secret door detection.
*/ */
if (u.uswallow) if (u.uswallow)
return 0; return 0;
#if FOUND_FLASH_COUNT == 0 /* _COUNT > 0 doesn't need to init tmp_at() */
tmp_at(DISP_ALL, GLYPH_NOTHING);
#endif
(void) memset((genericptr_t) &found, 0, sizeof found); (void) memset((genericptr_t) &found, 0, sizeof found);
do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &found); do_clear_area(u.ux, u.uy, BOLT_LIM, findone, (genericptr_t) &found);
/* count that controls "reveal" punctuation; 0..4 */ /* count that controls "reveal" punctuation; 0..4 */
@@ -1751,6 +1840,12 @@ findit(void)
Strcat(buf, "a trap"); Strcat(buf, "a trap");
num += found.num_traps; num += found.num_traps;
} }
#if FOUND_FLASH_COUNT == 0
int tmp_num;
tmp_num = num; /* sdoors, scorrs, and traps call tmp_at() */
#endif
if (found.num_mons) { if (found.num_mons) {
if (*buf) if (*buf)
Strcat(buf, (k > 2) ? ", and " : " and "); Strcat(buf, (k > 2) ? ", and " : " and ");
@@ -1765,10 +1860,10 @@ findit(void)
if (found.num_invis) { if (found.num_invis) {
if (found.num_invis > 1) if (found.num_invis > 1)
Sprintf(buf, "%d%s invisible monsters", found.num_invis, Sprintf(buf, "%d%s unseen monsters", found.num_invis,
found.num_kept_invis ? " other" : ""); found.num_kept_invis ? " other" : "");
else else
Sprintf(buf, "%s invisible monster", Sprintf(buf, "%s unseen monster",
found.num_kept_invis ? "another" : "an"); found.num_kept_invis ? "another" : "an");
You("detect %s!", buf); You("detect %s!", buf);
num += found.num_invis; num += found.num_invis;
@@ -1783,6 +1878,16 @@ findit(void)
} }
/* note: num_kept_invis is not included in the final result */ /* note: num_kept_invis is not included in the final result */
if (!num)
You("don't find anything.");
#if FOUND_FLASH_COUNT == 0
else if (tmp_num) {
flush_screen(1);
display_nhwindow(WIN_MAP, TRUE);
}
tmp_at(DISP_END, GLYPH_NOTHING); /* note: outside of 'if (tmp_num) { }' */
#endif
return num; return num;
} }
@@ -2283,4 +2388,6 @@ reveal_terrain(
return; return;
} }
#undef FOUND_FLASH_COUNT
/*detect.c*/ /*detect.c*/