diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 57a7cd3a8..924e933bb 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1390,6 +1390,8 @@ Debug mode only. .lp "#look " Look at what is here, under you. Default key is \(oq:\(cq. +.lp #lookaround +Describe what you can see, or remember, of your surroundings. .lp "#loot " Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. @@ -4091,6 +4093,8 @@ fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Cannot be set with the \(oq\f(CRO\fP\(cq command. Persistent. +.lp mention_map +Give feedback when interesting map locations change (default off). .lp mention_walls Give feedback when walking against a wall (default off). Persistent. @@ -5977,6 +5981,8 @@ Shows a message when hero notices a monster movement; combine with spot_monsters and accessiblemsg. .lp autodescribe Automatically describe the terrain under the cursor when targeting. +.lp mention_map +Give feedback messages when interesting map locations change. .lp mention_walls Give feedback messages when walking towards a wall or when travel command was interrupted. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 9729006d6..c24bc8ea9 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1506,6 +1506,9 @@ Debug mode only. \item[\tb{\#look}] Look at what is here, under you. Default key is `{\tt :}'. %.lp +\item[\tb{\#lookaround}] +Describe what you can see, or remember, of your surroundings. +%.lp \item[\tb{\#loot}] Loot a box or bag on the floor beneath you, or the saddle from a steed standing next to you. Autocompletes. @@ -4474,6 +4477,9 @@ Give feedback when walking onto various dungeon features such as stairs, fountains, or altars which are ordinarily only described when covered by one or more objects (default off). Persistent. %.lp +\item[\ib{mention\verb+_+map}] +Give feedback when interesting map locations change (default off). +%.lp \item[\ib{mention\verb+_+walls}] Give feedback when walking against a wall (default off). Persistent. %.lp @@ -6593,6 +6599,9 @@ combine with spot\verb+_+monsters and accessiblemsg. \item[\ib{autodescribe}] Automatically describe the terrain under the cursor when targeting. %.lp +\item[\ib{mention\verb+_+map}] +Give feedback messages when interesting map locations change. +%.lp \item[\ib{mention\verb+_+walls}] Give feedback messages when walking towards a wall or when travel command was interrupted. diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 217bc3d5f..5cdf29f6e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -2498,7 +2498,9 @@ erinyes overhaul; they now attempt to punish heroes who have violated their alignment and get stronger with greater alignment abuse accessibility options to tell where on map a message happened (accessiblemsg), to notify when a monster is spotted (spot_monsters), and when - a monster moved (mon_movement), when hero takes damage (showdamage) + a monster moved (mon_movement), when hero takes damage (showdamage), + when interesting map locations change (mention_map) +accessibility command #lookaround which will describe what hero can see if hero is punished or tethered to a buried iron ball and has no inventory (or only carries gold or embedded dragon scales or both), a nymph might remove the chain when finding nothing else to steal diff --git a/include/extern.h b/include/extern.h index a59fc9f0f..de0fbd1ef 100644 --- a/include/extern.h +++ b/include/extern.h @@ -324,6 +324,7 @@ extern char *cmd_from_ecname(const char *); extern const char *cmdname_from_func(int(*)(void), char *, boolean); extern boolean redraw_cmd(char); extern const char *levltyp_to_name(int); +extern int dolookaround(void); extern void reset_occupations(void); extern void set_occupation(int(*)(void), const char *, cmdcount_nht); extern void cmdq_add_ec(int, int(*)(void)); @@ -634,6 +635,7 @@ extern const char *pmname(struct permonst *, int) NONNULLARG1; extern const char *mon_pmname(struct monst *) NONNULLARG1; extern const char *obj_pmname(struct obj *) NONNULLARG1; extern boolean mapxy_valid(coordxy, coordxy); +extern boolean gather_locs_interesting(coordxy, coordxy, int); /* ### do_wear.c ### */ diff --git a/include/flag.h b/include/flag.h index e71e8c6da..078ded85c 100644 --- a/include/flag.h +++ b/include/flag.h @@ -194,6 +194,7 @@ struct accessibility_data { boolean mon_notices; /* msg when hero notices a monster */ int mon_notices_blocked; /* temp disable mon_notices */ boolean mon_movement; /* msg when hero sees monster move */ + boolean glyph_updates; /* msg when map glyphs change */ }; /* Use notice_mon_off() / notice_mon_on() to temporarily disable diff --git a/include/optlist.h b/include/optlist.h index 6b3a5c425..8b66f9385 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -408,6 +408,9 @@ static int optfn_##a(int, int, boolean, char *, char *); NHOPTB(mention_decor, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.mention_decor, Term_False, "give feedback when walking over interesting features") + NHOPTB(mention_map, Advanced, 0, opt_in, set_in_game, + Off, Yes, No, No, NoAlias, &a11y.glyph_updates, Term_False, + "give feedback when interesting map locations change") NHOPTB(mention_walls, Advanced, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &flags.mention_walls, Term_False, "give feedback when walking into walls") diff --git a/src/allmain.c b/src/allmain.c index db8d2c5a9..a0dfd803f 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -769,7 +769,10 @@ newgame(void) /* Success! */ welcome(TRUE); notice_mon_on(); /* now we can notice monsters */ - notice_all_mons(TRUE); + if (a11y.glyph_updates) + (void) dolookaround(); + else + notice_all_mons(TRUE); return; } diff --git a/src/cmd.c b/src/cmd.c index 19a32fb3d..dc8aac76f 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -111,6 +111,13 @@ static int dotravel_target(void); static int doclicklook(void); static int domouseaction(void); static int doterrain(void); +static boolean u_have_seen_whole_selection(struct selectionvar *); +static boolean u_have_seen_bounds_selection(struct selectionvar *); +static boolean u_can_see_whole_selection(struct selectionvar *); +static boolean selection_is_irregular(struct selectionvar *); +static char *selection_size_description(struct selectionvar *, char *); +static int dolookaround_floodfill_findroom(coordxy, coordxy); +static void lookaround_known_room(coordxy, coordxy); static int wiz_wish(void); static int wiz_identify(void); static int wiz_map(void); @@ -1224,6 +1231,7 @@ wiz_map(void) struct engr *ep; long save_Hconf = HConfusion, save_Hhallu = HHallucination; + notice_mon_off(); HConfusion = HHallucination = 0L; for (t = gf.ftrap; t != 0; t = t->ntrap) { t->tseen = 1; @@ -1233,6 +1241,7 @@ wiz_map(void) map_engraving(ep, TRUE); } do_mapping(); + notice_mon_on(); HConfusion = save_Hconf; HHallucination = save_Hhallu; } else @@ -2251,6 +2260,210 @@ doterrain(void) return ECMD_OK; /* no time elapses */ } +/* has hero seen all locations in selection? */ +static boolean +u_have_seen_whole_selection(struct selectionvar *sel) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + + return TRUE; +} + +/* has hero seen all location of the rectangular outline in the selection */ +static boolean +u_have_seen_bounds_selection(struct selectionvar *sel) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) { + y = rect.ly; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + y = rect.hy; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + } + for (y = rect.ly; y <= rect.hy; y++) { + x = rect.lx; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + x = rect.hx; + if (isok(x,y) && selection_getpoint(x, y, sel) + && glyph_at(x, y) == GLYPH_UNEXPLORED) + return FALSE; + } + + return TRUE; +} + +/* can hero currently see all locations in the selection */ +static boolean +u_can_see_whole_selection(struct selectionvar *sel) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && selection_getpoint(x, y, sel) && !cansee(x, y)) + return FALSE; + + return TRUE; +} + +/* selection is not rectangular, or has holes in it */ +static boolean +selection_is_irregular(struct selectionvar *sel) +{ + coordxy x, y; + NhRect rect = cg.zeroNhRect; + + selection_getbounds(sel, &rect); + + for (x = rect.lx; x <= rect.hx; x++) + for (y = rect.ly; y <= rect.hy; y++) + if (isok(x,y) && !selection_getpoint(x, y, sel)) + return TRUE; + + return FALSE; +} + +/* return a description of the selection size */ +static char * +selection_size_description(struct selectionvar *sel, char *buf) +{ + NhRect rect = cg.zeroNhRect; + coordxy dx, dy; + + selection_getbounds(sel, &rect); + dx = rect.hx - rect.lx + 1; + dy = rect.hy - rect.ly + 1; + Sprintf(buf, "%s %i by %i", selection_is_irregular(sel) ? "irregularly shaped" + : (dx == dy) ? "square" + : "rectangular", + dx, dy); + return buf; +} + +/* selection_floofill callback to get all locations in a room */ +static int +dolookaround_floodfill_findroom(coordxy x, coordxy y) +{ + schar typ = levl[x][y].typ; + + if (IS_STWALL(typ) || IS_DOOR(typ) || IS_TREE(typ) + || IS_WATERWALL(typ) || typ == LAVAWALL || typ == IRONBARS + || typ == SCORR || typ == SDOOR || typ == DRAWBRIDGE_UP) + return FALSE; + return TRUE; +} + +/* describe the room at x,y */ +static void +lookaround_known_room(coordxy x, coordxy y) +{ + struct selectionvar *sel = selection_new(); + int rmno = u.urooms[0] - ROOMOFFSET; + char qbuf[QBUFSZ]; + + set_selection_floodfillchk(dolookaround_floodfill_findroom); + selection_floodfill(sel, x, y, TRUE); + + if (!u_at(x, y)) + set_msg_xy(x, y); + + if (u_have_seen_whole_selection(sel)) { + boolean u_in = (boolean) selection_getpoint(x, y, sel); + + You("%s %s %s.", + u_at(x, y) && u_in && u_can_see_whole_selection(sel) ? "are in" + : (u_at(x, y)) ? "remember this as" : "remember that as", + an(selection_size_description(sel, qbuf)), + rmno >= 0 ? "room" : "area"); + } else if (u_have_seen_bounds_selection(sel)) { + You("guess %s to be %s %s.", + u_at(x, y) ? "this" : "that", + an(selection_size_description(sel, qbuf)), + rmno >= 0 ? "room" : "area"); + } else { + You("can't guess the size of %s area.", + u_at(x, y) ? "this" : "that"); + } + selection_free(sel, TRUE); +} + +/* #lookaround - describe what the hero can see, in text */ +int +dolookaround(void) +{ + coordxy x, y; + int tmp_getloc_filter = iflags.getloc_filter; + boolean tmp_accessiblemsg = a11y.accessiblemsg; + boolean corr_next2u = FALSE; + + a11y.accessiblemsg = TRUE; + if (levl[u.ux][u.uy].typ == CORR) { + /* In a corridor, mention corridors next to you. */ + corr_next2u = TRUE; + /* TODO: if we know, describe where the corridor goes, + perhaps by describing the rooms? */ + } else if (IS_DOOR(levl[u.ux][u.uy].typ)) { + /* In a doorway, describe the rooms next to you */ + int i; + + for (i = DIR_W; i < N_DIRS; i += 2) { + x = u.ux + xdir[i]; + y = u.uy + ydir[i]; + if (isok(x, y) && IS_ROOM(levl[x][y].typ)) + lookaround_known_room(x, y); + } + corr_next2u = TRUE; + } else { + lookaround_known_room(u.ux, u.uy); + } + + /* TODO: maybe describe stuff outside the current room differently? */ + + iflags.getloc_filter = GFILTER_VIEW; + for (y = 0; y < ROWNO; y++) + for (x = 1; x < COLNO; x++) + if (!u_at(x, y) + && (gather_locs_interesting(x, y, GLOC_INTERESTING) || + (corr_next2u && (glyph_at(x,y) == cmap_to_glyph(S_corr) + || glyph_at(x,y) == cmap_to_glyph(S_litcorr))))) { + char buf[BUFSZ]; + coord cc; + int sym = 0; + const char *firstmatch = 0; + + cc.x = x, cc.y = y; + do_screen_description(cc, TRUE, sym, buf, &firstmatch, NULL); + pline_xy(x, y, "%s.", firstmatch); + } + + iflags.getloc_filter = tmp_getloc_filter; + a11y.accessiblemsg = tmp_accessiblemsg; + + return ECMD_OK; +} + void set_move_cmd(int dir, int run) { @@ -2625,6 +2838,8 @@ struct ext_func_tab extcmdlist[] = { wiz_light_sources, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { ':', "look", "look at what is here", dolook, IFBURIED, NULL }, + { '\0', "lookaround", "describe what you can see", + dolookaround, IFBURIED | GENERALCMD, NULL }, { M('l'), "loot", "loot a box on the floor", doloot, AUTOCOMPLETE | CMD_M_PREFIX, NULL }, { '\0', "migratemons", diff --git a/src/display.c b/src/display.c index 524ed68b5..1892c2da9 100644 --- a/src/display.c +++ b/src/display.c @@ -1816,6 +1816,8 @@ show_glyph(coordxy x, coordxy y, int glyph) #ifndef UNBUFFERED_GLYPHINFO glyph_info glyphinfo; #endif + boolean show_glyph_change = FALSE; + int oldglyph; /* * Check for bad positions and glyphs. @@ -1935,6 +1937,24 @@ show_glyph(coordxy x, coordxy y, int glyph) map_glyphinfo(x, y, glyph, 0, &glyphinfo); #endif + oldglyph = gg.gbuf[y][x].glyphinfo.glyph; + + if (a11y.glyph_updates && !a11y.mon_notices_blocked + && (oldglyph != glyph || gg.gbuf[y][x].gnew)) { + int c = glyph_to_cmap(glyph); + if ((glyph_is_nothing(oldglyph) || glyph_is_unexplored(oldglyph) + || is_cmap_furniture(c)) + && !is_cmap_wall(c) && !is_cmap_room(c)) { + if ((a11y.mon_notices && glyph_is_monster(glyph)) + || (glyph_is_monster(oldglyph)) + || u_at(x, y)) { + /* nothing */ + } else { + show_glyph_change = TRUE; + } + } + } + if (gg.gbuf[y][x].glyphinfo.glyph != glyph #ifndef UNBUFFERED_GLYPHINFO /* flags might change (single object vs pile, monster tamed or pet @@ -1961,6 +1981,17 @@ show_glyph(coordxy x, coordxy y, int glyph) if (gg.gbuf_stop[y] < x) gg.gbuf_stop[y] = x; } + + if (show_glyph_change) { + char buf[BUFSZ]; + coord cc; + int sym = 0; + const char *firstmatch = 0; + + cc.x = x, cc.y = y; + do_screen_description(cc, TRUE, sym, buf, &firstmatch, NULL); + pline_xy(x, y, "%s.", firstmatch); + } } /* diff --git a/src/do_name.c b/src/do_name.c index 59f988447..d050359ad 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -18,7 +18,6 @@ static int gloc_filter_floodfill_matcharea(coordxy, coordxy); static void gloc_filter_floodfill(coordxy, coordxy); static void gloc_filter_init(void); static void gloc_filter_done(void); -static boolean gather_locs_interesting(coordxy, coordxy, int); static void gather_locs(coord **, int *, int); static void truncate_to_map(coordxy *, coordxy *, schar, schar); static void getpos_refresh(void); @@ -453,7 +452,7 @@ gloc_filter_done(void) DISABLE_WARNING_UNREACHABLE_CODE -static boolean +boolean gather_locs_interesting(coordxy x, coordxy y, int gloc) { int glyph, sym; diff --git a/src/read.c b/src/read.c index 5d5a5efc1..0f52b886d 100644 --- a/src/read.c +++ b/src/read.c @@ -2031,7 +2031,9 @@ seffect_magic_mapping(struct obj **sobjp) cval = (scursed && !confused); if (cval) HConfusion = 1; /* to screw up map */ + notice_mon_off(); do_mapping(); + notice_mon_on(); if (cval) { HConfusion = 0; /* restore */ pline("Unfortunately, you can't grasp the details.");