diff --git a/dat/opthelp b/dat/opthelp index 73e55ab78..1048a19f9 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -34,6 +34,8 @@ legacy print introductory message [TRUE] lit_corridor show a dark corridor as lit if in sight [FALSE] lootabc use a/b/c rather than o/i/b when looting [FALSE] mail enable the mail daemon [TRUE] +mention_decor give feedback when walking across stairs, altars, [FALSE] + fountains, and such even when not obscured by objects mention_walls give feedback when walking against a wall [FALSE] menu_objsyms show object symbols in menus if it is selectable [FALSE] menu_overlay overlay menus on the screen and align to right [TRUE] diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 3a8db5a84..3d4ea8e66 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1784,6 +1784,81 @@ any other customers. If a shop is \(lqclosed for inventory,\(rq it will not open of its own accord. .lp * 2 Shops do not get restocked with new items, regardless of inventory depletion. +.hn 2 +Movement feedback +.pg +Moving around the map usually provides no feedback--other than drawing the +hero at the new location--unless you step on an object or pile of objects, +or on a trap, or attempt to move onto a spot where a monster is located. +There are several options which can be used to augment the normal feedback. +.pg +The +.op pile_limit +option controls how many objects can be in a pile--sharing the same map +location--for the game to state \(lqthere are objects here\(rq +instead of listing them. +The default is \f(CR5\fP. +Setting it to \f(CR1\fP would always give that message instead of listing +any objects. +Setting it to \f(CR0\fP is a special case which will always list all +objects no matter how big a pile is. +Note that the number refers to the count of separate stacks of objects +present rather than the sum of the quantities of those stacks (so +\f(CR7 arrows\fP or \f(CR25 gold pieces\fP will each count as 1 rather +than as 7 and 25, respectively, and total to 2 when both are at the +same location). +.pg +The \(lqnopickup\(rq command prefix (default \(oq\f(CRm\fP\(cq) before +a movement direction can be used to step on objects without attempting +auto-pickup and without giving feedback about them. +.pg +The +.op mention_walls +option controls whether you get feedback if you try to walk into a wall +or solid stone or off the edge of the map. +Normally nothing happens (unless the hero is blind and no wall is shown, +then the wall that is being bumped into will be drawn on the map). +This option also gives feedback when the various rush or run variations +of movement stop for some non-obvious reason. +.pg +The +.op mention_decor +option controls whether you get feedback when walking on \(lqfurniture.\(rq +Normally stepping onto stairs or a fountain or an altar or various other +things doesn't elicit anything unless it is covered by one or more objects +so is obscured on the map. +Doorless doorways and open doors aren't considered worthy of mention; +closed doors (if you can move onto their spots) and broken doors are. +Assuming that you're able to do so, moving onto water or lava or ice +will give feedback if not yet on that type of terrain but not repeat it +(unless there has been some intervening message) when moving from water +to another water spot, or lava to lava, or ice to ice. +Moving off of any of those back onto \(lqnormal\(rq terrain will give one +message too, unless there is feedback about one or more objects, in which +case the back on land circumstance is implied. +.pg +The +.op confirm +and +.op safe_pet +options control what happens when you try to move onto a peaceful monster's +spot or a tame one's spot. +.\" getting away from "Movement feedback" here; oh well... +.pg +The \(lqnopickup\(rq command prefix (default \(oq\f(CRm\fP\(cq) is +also the move-without-attacking prefix and can be used to try to step +onto a visible monster's spot without the move being considered an attack +(see the \fIFighting\fP subsection of \fIMonsters\fP below). +The \(lqfight\(rq command prefix (default \(oq\f(CRF\fP\(cq; +also \(oq\f(CR\-\fP\(cq if +.op number_pad +is on) can be used to force an attack, when guessing where an unseen +monster is or when deliberately attacking a peaceful or tame creature. +.pg +The +.op run_mode +option controls how frequently the map gets redrawn when moving more +than one step in a single command (so when rushing, running, or traveling). . .hn 1 Monsters @@ -1809,8 +1884,10 @@ shopkeeper or the Oracle of Delphi can produce useful results. Fighting .pg If you see a monster and you wish to fight it, just attempt to walk -into it. Many monsters you find will mind their own business unless -you attack them. Some of them are very dangerous when angered. +into it. +Many monsters you find will mind their own business unless +you attack them. +Some of them are very dangerous when angered. Remember: discretion is the better part of valor. .pg In most circumstances, if you attempt to attack a peaceful monster by @@ -3182,6 +3259,11 @@ Enable mail delivery during the game (default on). Persistent. .lp "male " An obsolete synonym for \(lqgender:male\(rq. Cannot be set with the \(oqO\(cq command. +.lp mention_decor +Give feedback when walking on 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 mention_walls Give feedback when walking against a wall (default off). Persistent. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 6647e521b..5fd6f62bc 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -1966,6 +1966,90 @@ If a shop is ``closed for inventory,'' it will not open of its own accord. Shops do not get restocked with new items, regardless of inventory depletion. \end{itemize} +%.hn 2 +\subsubsection*{Movement feedback} + +%.pg +Moving around the map usually provides no feedback--other than drawing the +hero at the new location--unless you step on an object or pile of objects, +or on a trap, or attempt to move onto a spot where a monster is located. +There are several options which can be used to augment the normal feedback. + +%.pg +The +{\it pile\verb+_+limit\/} +option controls how many objects can be in a pile--sharing the same map +location--for the game to state ``there are objects here'' +instead of listing them. +The default is {\tt 5}. +Setting it to {\tt 1} would always give that message instead of listing +any objects. +Setting it to {\tt 0} is a special case which will always list all +objects no matter how big a pile is. +Note that the number refers to the count of separate stacks of objects +present rather than the sum of the quantities of those stacks (so +{\tt 7 arrows} or {\tt 25 gold pieces} will each count as 1 rather +than as 7 and 25, respectively, and total to 2 when both are at the +same location). + +%.pg +The \{\tt nopickup} command prefix (default `\{\tt m}') before +a movement direction can be used to step on objects without attempting +auto-pickup and without giving feedback about them. + +%.pg +The +{\it mention\verb+_+walls\/} +option controls whether you get feedback if you try to walk into a wall +or solid stone or off the edge of the map. +Normally nothing happens (unless the hero is blind and no wall is shown, +then the wall that is being bumped into will be drawn on the map). +This option also gives feedback when the various rush or run variations +of movement stop for some non-obvious reason. + +%.pg +The +{\it mention\verb+_+decor\/} +option controls whether you get feedback when walking on ``furniture.'' +Normally stepping onto stairs or a fountain or an altar or various other +things doesn't elicit anything unless it is covered by one or more objects +so is obscured on the map. +Doorless doorways and open doors aren't considered worthy of mention; +closed doors (if you can move onto their spots) and broken doors are. +Assuming that you're able to do so, moving onto water or lava or ice +will give feedback if not yet on that type of terrain but not repeat it +(unless there has been some intervening message) when moving from water +to another water spot, or lava to lava, or ice to ice. +Moving off of any of those back onto ``normal'' terrain will give one +message too, unless there is feedback about one or more objects, in which +case the back on land circumstance is implied. + +%.pg +The +{\it confirm\/} +and +{\it safe\verb+_+pet\/} +options control what happens when you try to move onto a peaceful monster's +spot or a tame one's spot. + +%.\" getting away from "Movement feedback" here; oh well... +%.pg +The {\tt nopickup} command prefix (default `{\tt m}' is +also the move-without-attacking prefix and can be used to try to step +onto a visible monster's spot without the move being considered an attack +(see the {\it Fighting\/} subsection of {\it Monsters\/} below). +The `{\tt fight}' command prefix (default `{\tt F}'; +also `{\tt -}' if +{\it number\verb+_+pad\/} +is on) can be used to force an attack, when guessing where an unseen +monster is or when deliberately attacking a peaceful or tame creature. + +%.pg +The +{\it run\verb+_+mode} +option controls how frequently the map gets redrawn when moving more +than one step in a single command (so when rushing, running, or traveling). + %.hn 1 \section{Monsters} @@ -3489,6 +3573,11 @@ Enable mail delivery during the game (default on). Persistent. An obsolete synonym for ``{\tt gender:male}''. Cannot be set with the `{\tt O}' command. %.lp +\item[\ib{mention\verb+_+decor}] +Give feedback when walking on 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+_+walls}] Give feedback when walking against a wall (default off). Persistent. %.lp diff --git a/include/flag.h b/include/flag.h index d92bd5dc8..28e9fdefe 100644 --- a/include/flag.h +++ b/include/flag.h @@ -41,6 +41,7 @@ struct flag { boolean invlet_constant; /* let objects keep their inventory symbol */ boolean legacy; /* print game entry "story" */ boolean lit_corridor; /* show a dark corr as lit if it is in sight */ + boolean mention_decor; /* give feedback for unobscured furniture */ boolean mention_walls; /* give feedback when bumping walls */ boolean nap; /* `timed_delay' option for display effects */ boolean null; /* OK to send nulls to the terminal */ @@ -283,6 +284,7 @@ struct instance_flags { boolean zerocomp; /* write zero-compressed save files */ boolean rlecomp; /* alternative to zerocomp; run-length encoding * compression of levels when writing savefile */ + schar prev_decor; /* 'mention_decor' just mentioned this */ uchar num_pad_mode; uchar bouldersym; /* symbol for boulder display */ char prevmsg_window; /* type of old message window to use */ diff --git a/include/patchlevel.h b/include/patchlevel.h index 77c6954d5..081ac58eb 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -14,7 +14,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 11 +#define EDITLEVEL 12 #define COPYRIGHT_BANNER_A "NetHack, Copyright 1985-2020" #define COPYRIGHT_BANNER_B \ diff --git a/src/options.c b/src/options.c index 4779cc442..638222145 100644 --- a/src/options.c +++ b/src/options.c @@ -158,6 +158,7 @@ static const struct Bool_Opt { #else { "mail", (boolean *) 0, TRUE, SET_IN_FILE }, #endif + { "mention_decor", &flags.mention_decor, FALSE, SET_IN_GAME }, { "mention_walls", &flags.mention_walls, FALSE, SET_IN_GAME }, { "menucolors", &iflags.use_menu_color, FALSE, SET_IN_GAME }, /* for menu debugging only*/ @@ -4168,6 +4169,8 @@ boolean tinitial, tfrom_file; || boolopt[i].addr == &iflags.wc2_guicolor) { update_inventory(); #endif /* TEXTCOLOR */ + } else if (boolopt[i].addr == &flags.mention_decor) { + iflags.prev_decor = STONE; } return retval; } diff --git a/src/pickup.c b/src/pickup.c index 94e79554a..e880fb411 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -16,6 +16,7 @@ static boolean FDECL(query_classes, (char *, boolean *, boolean *, const char *, struct obj *, BOOLEAN_P, int *)); static boolean FDECL(fatal_corpse_mistake, (struct obj *, BOOLEAN_P)); +static void NDECL(describe_decor); static void FDECL(check_here, (BOOLEAN_P)); static boolean FDECL(n_or_more, (struct obj *)); static boolean FDECL(all_but_uchain, (struct obj *)); @@ -285,6 +286,60 @@ boolean remotely; return TRUE; } +/* handle 'mention_decor' (when walking onto a dungeon feature such as + stairs or altar, describe it even if it isn't covered up by an object) */ +static void +describe_decor() +{ + char outbuf[BUFSZ], fbuf[QBUFSZ]; + boolean doorhere, waterhere, do_norep; + const char *dfeature = dfeature_at(u.ux, u.uy, fbuf); + int ltyp = levl[u.ux][u.uy].typ; + + if (ltyp == DRAWBRIDGE_UP) /* surface for spot in front of closed db */ + ltyp = db_under_typ(levl[u.ux][u.uy].drawbridgemask); + + /* we don't mention "ordinary" doors but do mention broken ones */ + doorhere = dfeature && (!strcmp(dfeature, "open door") + || !strcmp(dfeature, "doorway")); + waterhere = dfeature && !strcmp(dfeature, "pool of water"); + if (doorhere || (waterhere && Underwater)) + dfeature = 0; + + if (dfeature) { + if (waterhere) + dfeature = strcpy(fbuf, waterbody_name(u.ux, u.uy)); + if (strcmp(dfeature, "swamp")) + dfeature = an(dfeature); + + if (flags.verbose) { + Sprintf(outbuf, "There is %s here.", dfeature); + } else { + if (dfeature != fbuf) + Strcpy(fbuf, dfeature); + Sprintf(outbuf, "%s.", upstart(fbuf)); + } + do_norep = (ltyp == iflags.prev_decor + && (waterhere + || !strcmp(dfeature, "molten lava") + || !strcmp(dfeature, "ice"))); + if (!do_norep) + pline("%s", outbuf); + else + Norep("%s", outbuf); + } else { + if ((IS_POOL(iflags.prev_decor) + || iflags.prev_decor == LAVAPOOL + || iflags.prev_decor == ICE)) { + pline("%s %s %s.", + flags.verbose ? "You are back" : "Back", + (Levitation || Flying) ? "over" : "on", + surface(u.ux, u.uy)); + } + } + iflags.prev_decor = ltyp; +} + /* look at the objects at our location, unless there are too many of them */ static void check_here(picked_some) @@ -305,7 +360,11 @@ boolean picked_some; nomul(0); flush_screen(1); (void) look_here(ct, picked_some); + + iflags.prev_decor = STONE; } else { + if (flags.mention_decor) + describe_decor(); read_engr_at(u.ux, u.uy); } } @@ -320,7 +379,6 @@ struct obj *obj; return (boolean) (obj->quan >= g.val_for_n_or_more); } - /* check valid_menu_classes[] for an entry; also used by askchain() */ boolean menu_class_present(c) @@ -487,8 +545,10 @@ int what; /* should be a long */ and read_engr_at in addition to bypassing autopickup itself [probably ought to check whether hero is using a cockatrice corpse for a pillow here... (also at initial faint/sleep)] */ - if (autopickup && g.multi < 0 && unconscious()) + if (autopickup && g.multi < 0 && unconscious()) { + iflags.prev_decor = STONE; return 0; + } if (what < 0) /* pick N of something */ count = -what; @@ -496,20 +556,23 @@ int what; /* should be a long */ count = 0; if (!u.uswallow) { - struct trap *ttmp; + struct trap *t; /* no auto-pick if no-pick move, nothing there, or in a pool */ if (autopickup && (g.context.nopick || !OBJ_AT(u.ux, u.uy) || (is_pool(u.ux, u.uy) && !Underwater) || is_lava(u.ux, u.uy))) { + if (flags.mention_decor) + describe_decor(); read_engr_at(u.ux, u.uy); return 0; } /* no pickup if levitating & not on air or water level */ if (!can_reach_floor(TRUE)) { + describe_decor(); /* even when !flags.mention_decor */ if ((g.multi && !g.context.run) || (autopickup && !flags.pickup) - || ((ttmp = t_at(u.ux, u.uy)) != 0 - && (uteetering_at_seen_pit(ttmp) || uescaped_shaft(ttmp)))) + || ((t = t_at(u.ux, u.uy)) != 0 + && (uteetering_at_seen_pit(t) || uescaped_shaft(t)))) read_engr_at(u.ux, u.uy); return 0; } @@ -517,15 +580,13 @@ int what; /* should be a long */ * action, or possibly paralyzed, sleeping, etc.... and they just * teleported onto the object. They shouldn't pick it up. */ - if ((g.multi && !g.context.run) || (autopickup && !flags.pickup)) { + if ((g.multi && !g.context.run) + || (autopickup && !flags.pickup) + || notake(g.youmonst.data)) { check_here(FALSE); - return 0; - } - if (notake(g.youmonst.data)) { - if (!autopickup) + if (notake(g.youmonst.data) && OBJ_AT(u.ux, u.uy) + && (autopickup || flags.pickup)) You("are physically incapable of picking anything up."); - else - check_here(FALSE); return 0; } @@ -534,6 +595,8 @@ int what; /* should be a long */ && !g.context.nopick) nomul(0); } + /* for describe_decor()'s Norep handling */ + iflags.prev_decor = STONE; add_valid_menu_class(0); /* reset */ if (!u.uswallow) {