From ea8248e3c6ab2f7a176b4f23e766f80f9c432ce2 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 29 Jan 2020 09:47:36 -0800 Subject: [PATCH] new 'mention_decor' option Somewhat similar to 'mention_walls', 'mention_decor' is a way to request additional feedback when moving around the map. It reports furniture or unusual terrain when you step on that. Normally stepping on furniture only mentions it when it is covered by object(s). And moving onto (rather than into) water or lava or ice doesn't bother saying anything at all. With the new option set there will be a message. It uses Norep so won't repeat when moving from one water spot to another or one lava spot to another or one ice spot to another unless there has been at least one intervening message. There is also a one-shot message when moving from water or lava or ice onto ordinary terrain (not Norep, just once since there's no land to land message). Having the verbose flag Off doesn't inhibit these new messages but it does shorten them: "A fountain." instead of "There is a fountain here." The Guidebook gets a new subsection "Movement feedback" of the "Rooms and corridors" section and it covers more than just 'mention_decor'. As usual, Guidebook.tex is untested. 'mention_decor' persists across save/restore, so 'struct flags' has changed and EDITLEVEL is being bumped, hence save files are invalided. --- dat/opthelp | 2 + doc/Guidebook.mn | 86 +++++++++++++++++++++++++++++++++++++++++- doc/Guidebook.tex | 89 ++++++++++++++++++++++++++++++++++++++++++++ include/flag.h | 2 + include/patchlevel.h | 2 +- src/options.c | 3 ++ src/pickup.c | 87 +++++++++++++++++++++++++++++++++++++------ 7 files changed, 256 insertions(+), 15 deletions(-) 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) {