From 4ed0e5fd6d4d92c542d2a7dd5ec77e8cbaf7487c Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 11 May 2017 16:26:35 -0700 Subject: [PATCH 01/19] 'A' vs blindfold Fix an inconsistency with blindfold removal, eliminating possibility of a panic (3.6.0) or an impossible (since mid-December, 2016). The 'A' command treated blindfold removal as a multi-turn operation, the rest of the wear/unwear code as single-turn operation so didn't try to guard against it being disrupted. --- doc/fixes36.1 | 2 ++ src/do_wear.c | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index ddad2f371..f0da1ea2d 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -378,6 +378,8 @@ levitation vs encumbrance message sequencing issues: putting on boots of levitation reported reduction of encumbrance before finish-wearing and float-up messages, taking off such boots didn't report increase of encumbrance until player took another action +removing a blindfold with 'A' took two turns, with 'R' (and 'T') only one, + and could result in a panic if the blindfold was stolen during removal Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/do_wear.c b/src/do_wear.c index f0c1038dc..e60df04dc 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_wear.c $NHDT-Date: 1494107204 2017/05/06 21:46:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.94 $ */ +/* NetHack 3.6 do_wear.c $NHDT-Date: 1494545163 2017/05/11 23:26:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1231,6 +1231,16 @@ struct obj *otmp; else if (otmp == uarms) result = (afternmv == Shield_on || afternmv == Shield_off || what == WORN_SHIELD); + /* these 1-turn items don't need 'afternmv' checks + [and may not actually need 'what' checks] */ + else if (otmp == uamul) + result = (what == WORN_AMUL); + else if (otmp == uleft) + result = (what == LEFT_RING); + else if (otmp == uright) + result = (what == RIGHT_RING); + else if (otmp == ublindf) + result = (what == WORN_BLINDF); return result; } @@ -1259,6 +1269,16 @@ struct obj *otmp; result = (afternmv == Gloves_off || what == WORN_GLOVES); else if (otmp == uarms) result = (afternmv == Shield_off || what == WORN_SHIELD); + /* these 1-turn items don't need 'afternmv' checks + [and may not actually need 'what' checks] */ + else if (otmp == uamul) + result = (what == WORN_AMUL); + else if (otmp == uleft) + result = (what == LEFT_RING); + else if (otmp == uright) + result = (what == RIGHT_RING); + else if (otmp == ublindf) + result = (what == WORN_BLINDF); return result; } @@ -2427,7 +2447,9 @@ take_off(VOID_ARGS) } else if (doff->what == RIGHT_RING) { doff->delay = 1; } else if (doff->what == WORN_BLINDF) { - doff->delay = 2; + /* [this used to be 2, but 'R' (and 'T') only require 1 turn to + remove a blindfold, so 'A' shouldn't have been requiring 2] */ + doff->delay = 1; } else { impossible("take_off: taking off %lx", doff->what); return 0; /* force done */ From 9b3587204a1258d1198a0d43f94271f6070d058e Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 16 May 2017 16:22:43 -0700 Subject: [PATCH 02/19] fix #H5462 - failed F-attack gives misleading msg Report was for 'F' followed by '.' reporting "cmdassist: Invalid direction key!" and then by a direction grid (which happened to include '.' for self). That behavior applied for all the movement prefix keys ('m', 'G', &c). When 'cmdassist' was off, "F." would yield "Unknown command 'F.'." instead. Now you'll get "You can't fight yourself.", either instead of the "invalid direction key" part of cmdassist feedback (followed by a direction grid which excludes up, down, and self since they aren't applicable for prefix keys) or of the "unknown command" result. Likewise, "You can't run upward." or "You can't rush downward." for "G<" and "g>", respectively. --- doc/fixes36.1 | 2 + src/cmd.c | 182 ++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 141 insertions(+), 43 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index f0da1ea2d..37b7fac4d 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -380,6 +380,8 @@ levitation vs encumbrance message sequencing issues: putting on boots of of encumbrance until player took another action removing a blindfold with 'A' took two turns, with 'R' (and 'T') only one, and could result in a panic if the blindfold was stolen during removal +cmdassist help for movement prefix followed by invalid direction was strange + when the direction was up, down, or self disallowed for that prefix Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/cmd.c b/src/cmd.c index 048fc5bbd..d354195bf 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 cmd.c $NHDT-Date: 1494034344 2017/05/06 01:32:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.256 $ */ +/* NetHack 3.6 cmd.c $NHDT-Date: 1494976933 2017/05/16 23:22:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.257 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -184,8 +184,8 @@ static const char *readchar_queue = ""; static coord clicklook_cc; STATIC_DCL char *NDECL(parse); -STATIC_DCL void FDECL(show_direction_keys, (winid, BOOLEAN_P)); -STATIC_DCL boolean FDECL(help_dir, (CHAR_P, const char *)); +STATIC_DCL void FDECL(show_direction_keys, (winid, CHAR_P, BOOLEAN_P)); +STATIC_DCL boolean FDECL(help_dir, (CHAR_P, int, const char *)); STATIC_PTR int doprev_message(VOID_ARGS) @@ -3073,7 +3073,11 @@ dokeylist(VOID_ARGS) boolean keys_used[256] = {0}; winid datawin; int i; - const struct { + static const char + run_desc[] = "Prefix: run until something very interesting is seen", + forcefight_desc[] = + "Prefix: force fight even if you don't see a monster"; + static const struct { int nhkf; const char *desc; boolean numpad; @@ -3081,22 +3085,18 @@ dokeylist(VOID_ARGS) { NHKF_ESC, "escape from the current query/action", FALSE }, { NHKF_RUSH, "Prefix: rush until something interesting is seen", FALSE }, - { NHKF_RUN, - "Prefix: run until something extremely interesting is seen", FALSE }, - { NHKF_RUN2, - "Prefix: run until something extremely interesting is seen", TRUE }, - { NHKF_FIGHT, - "Prefix: force fight even if you don't see a monster", FALSE }, - { NHKF_FIGHT2, - "Prefix: force fight even if you don't see a monster", TRUE }, + { NHKF_RUN, run_desc, FALSE }, + { NHKF_RUN2, run_desc, TRUE }, + { NHKF_FIGHT, forcefight_desc, FALSE }, + { NHKF_FIGHT2, forcefight_desc, TRUE } , { NHKF_NOPICKUP, "Prefix: move without picking up objects/fighting", FALSE }, { NHKF_RUN_NOPICKUP, "Prefix: run without picking up objects/fighting", FALSE }, - { NHKF_DOINV, "inventory (same as #inventory)", TRUE }, + { NHKF_DOINV, "view inventory", TRUE }, { NHKF_REQMENU, "Prefix: request a menu", FALSE }, #ifdef REDO - { NHKF_DOAGAIN , "redo the previous command", FALSE }, + { NHKF_DOAGAIN , "re-do: perform the previous command again", FALSE }, #endif { 0, (const char *) 0, FALSE } }; @@ -3108,7 +3108,7 @@ dokeylist(VOID_ARGS) /* directional keys */ putstr(datawin, 0, ""); putstr(datawin, 0, "Directional keys:"); - show_direction_keys(datawin, FALSE); + show_direction_keys(datawin, '.', FALSE); /* '.'==self in direction grid */ keys_used[(uchar) Cmd.move_NW] = keys_used[(uchar) Cmd.move_N] = keys_used[(uchar) Cmd.move_NE] = keys_used[(uchar) Cmd.move_W] @@ -3942,11 +3942,12 @@ int NDECL((*cmd_func)); } int -ch2spkeys(c, start,end) +ch2spkeys(c, start, end) char c; int start,end; { int i; + for (i = start; i <= end; i++) if (Cmd.spkeys[i] == c) return i; @@ -3957,6 +3958,7 @@ void rhack(cmd) register char *cmd; { + int spkey; boolean do_walk, do_rush, prefix_seen, bad_command, firsttime = (cmd == 0); @@ -3990,7 +3992,9 @@ register char *cmd; /* handle most movement commands */ do_walk = do_rush = prefix_seen = FALSE; context.travel = context.travel1 = 0; - switch (ch2spkeys(*cmd, NHKF_RUN,NHKF_CLICKLOOK)) { + spkey = ch2spkeys(*cmd, NHKF_RUN, NHKF_CLICKLOOK); + + switch (spkey) { case NHKF_RUSH: if (movecmd(cmd[1])) { context.run = 2; @@ -4161,14 +4165,13 @@ register char *cmd; if (bad_command) { char expcmd[20]; /* we expect 'cmd' to point to 1 or 2 chars */ - register char c; + char c, c1 = cmd[1]; expcmd[0] = '\0'; while ((c = *cmd++) != '\0') Strcat(expcmd, visctrl(c)); /* add 1..4 chars plus terminator */ - if (!prefix_seen || !iflags.cmdassist - || !help_dir(0, "Invalid direction key!")) + if (!prefix_seen || !help_dir(c1, spkey, "Invalid direction key!")) Norep("Unknown command '%s'.", expcmd); } /* didn't move */ @@ -4319,7 +4322,8 @@ retry: if (!index(quitchars, dirsym)) { help_requested = (dirsym == Cmd.spkeys[NHKF_GETDIR_HELP]); if (help_requested || iflags.cmdassist) { - did_help = help_dir((s && *s == '^') ? dirsym : 0, + did_help = help_dir((s && *s == '^') ? dirsym : '\0', + NHKF_ESC, help_requested ? (const char *) 0 : "Invalid direction key!"); if (help_requested) @@ -4339,61 +4343,141 @@ retry: } STATIC_OVL void -show_direction_keys(win, nodiag) -winid win; +show_direction_keys(win, centerchar, nodiag) +winid win; /* should specify a window which is using a fixed-width font... */ +char centerchar; /* '.' or '@' or ' ' */ boolean nodiag; { char buf[BUFSZ]; + if (!centerchar) + centerchar = ' '; + if (nodiag) { Sprintf(buf, " %c ", Cmd.move_N); putstr(win, 0, buf); putstr(win, 0, " | "); - Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E); + Sprintf(buf, " %c- %c -%c", + Cmd.move_W, centerchar, Cmd.move_E); putstr(win, 0, buf); putstr(win, 0, " | "); Sprintf(buf, " %c ", Cmd.move_S); putstr(win, 0, buf); } else { - Sprintf(buf, " %c %c %c", Cmd.move_NW, Cmd.move_N, - Cmd.move_NE); + Sprintf(buf, " %c %c %c", + Cmd.move_NW, Cmd.move_N, Cmd.move_NE); putstr(win, 0, buf); putstr(win, 0, " \\ | / "); - Sprintf(buf, " %c- . -%c", Cmd.move_W, Cmd.move_E); + Sprintf(buf, " %c- %c -%c", + Cmd.move_W, centerchar, Cmd.move_E); putstr(win, 0, buf); putstr(win, 0, " / | \\ "); - Sprintf(buf, " %c %c %c", Cmd.move_SW, Cmd.move_S, - Cmd.move_SE); + Sprintf(buf, " %c %c %c", + Cmd.move_SW, Cmd.move_S, Cmd.move_SE); putstr(win, 0, buf); }; } +/* explain choices if player has asked for getdir() help or has given + an invalid direction after a prefix key ('F', 'g', 'm', &c), which + might be bogus but could be up, down, or self when not applicable */ STATIC_OVL boolean -help_dir(sym, msg) +help_dir(sym, spkey, msg) char sym; +int spkey; /* NHKF_ code for prefix key, if one was used, or for ESC */ const char *msg; { static const char wiz_only_list[] = "EFGIVW"; char ctrl; winid win; char buf[BUFSZ], buf2[BUFSZ], *explain; + const char *dothat, *how; + boolean prefixhandling, viawindow; + + /* NHKF_ESC indicates that player asked for help at getdir prompt */ + viawindow = (spkey == NHKF_ESC || iflags.cmdassist); + prefixhandling = (spkey != NHKF_ESC); + /* + * Handling for prefix keys that don't want special directions. + * Delivered via pline if 'cmdassist' is off, or instead of the + * general message if it's on. + */ + dothat = "do that"; + how = " at"; /* for " at yourself"; not used for up/down */ + switch (spkey) { + case NHKF_NOPICKUP: + dothat = "move"; + break; + case NHKF_RUSH: + dothat = "rush"; + break; + case NHKF_RUN2: + if (!Cmd.num_pad) + break; /* else FALLTHRU */ + case NHKF_RUN: + case NHKF_RUN_NOPICKUP: + dothat = "run"; + break; + case NHKF_FIGHT2: + if (!Cmd.num_pad) + break; /* else FALLTHRU */ + case NHKF_FIGHT: + dothat = "fight"; + how = ""; /* avoid "fight at yourself" */ + break; + default: + prefixhandling = FALSE; + break; + } + + buf[0] = '\0'; + /* for movement prefix followed by '.' or (numpad && 's') to mean 'self'; + note: '-' for hands (inventory form of 'self') is not handled here */ + if (prefixhandling + && (sym == Cmd.spkeys[NHKF_GETDIR_SELF] + || (Cmd.num_pad && sym == Cmd.spkeys[NHKF_GETDIR_SELF2]))) { + Sprintf(buf, "You can't %s%s yourself.", dothat, how); + /* for movement prefix followed by up or down */ + } else if (prefixhandling && (sym == '<' || sym == '>')) { + Sprintf(buf, "You can't %s %s.", dothat, + /* was "upwards" and "downwards", but they're considered + to be variants of canonical "upward" and "downward" */ + (sym == '<') ? "upward" : "downward"); + } + + /* if 'cmdassist', display via pline() and we're done (note: asking + for help at getdir() prompt forces cmdassist for this operation) */ + if (!viawindow) { + if (*buf) { + pline("%s", buf); + return TRUE; + } + /* when 'cmdassist' is off and caller doesn't insist, do nothing */ + return FALSE; + } win = create_nhwindow(NHW_TEXT); if (!win) return FALSE; - if (msg) { + + if (*buf) { + /* show bad-prefix message instead of general invalid-direction one */ + putstr(win, 0, buf); + putstr(win, 0, ""); + } else if (msg) { Sprintf(buf, "cmdassist: %s", msg); putstr(win, 0, buf); putstr(win, 0, ""); } - if (letter(sym) || sym == '[') { /* 'dat/cmdhelp' shows ESC as ^[ */ + + if (!prefixhandling && (letter(sym) || sym == '[')) { + /* '[': old 'cmdhelp' showed ESC as ^[ */ sym = highc(sym); /* @A-Z[ (note: letter() accepts '@') */ ctrl = (sym - 'A') + 1; /* 0-27 (note: 28-31 aren't applicable) */ if ((explain = dowhatdoes_core(ctrl, buf2)) != 0 && (!index(wiz_only_list, sym) || wizard)) { Sprintf(buf, "Are you trying to use ^%c%s?", sym, - index(wiz_only_list, sym) - ? "" + index(wiz_only_list, sym) ? "" : " as specified in the Guidebook"); putstr(win, 0, buf); putstr(win, 0, ""); @@ -4407,17 +4491,29 @@ const char *msg; } } - Sprintf(buf, "Valid direction keys %sare:", - NODIAG(u.umonnum) ? "in your current form " : ""); + Sprintf(buf, "Valid direction keys%s%s%s are:", + prefixhandling ? " to " : "", prefixhandling ? dothat : "", + NODIAG(u.umonnum) ? " in your current form" : ""); putstr(win, 0, buf); - show_direction_keys(win, NODIAG(u.umonnum)); + show_direction_keys(win, !prefixhandling ? '.' : ' ', NODIAG(u.umonnum)); + + if (!prefixhandling || spkey == NHKF_NOPICKUP) { + /* NOPICKUP: unlike the other prefix keys, 'm' allows up/down for + stair traversal; we won't get here when "m<" or "m>" has been + given but we include up and down for 'm'+invalid_direction; + self is excluded as a viable direction for every prefix */ + putstr(win, 0, ""); + putstr(win, 0, " < up"); + putstr(win, 0, " > down"); + if (!prefixhandling) { + int selfi = Cmd.num_pad ? NHKF_GETDIR_SELF2 : NHKF_GETDIR_SELF; + + Sprintf(buf, " %4s direct at yourself", + visctrl(Cmd.spkeys[selfi])); + putstr(win, 0, buf); + } + } - putstr(win, 0, ""); - putstr(win, 0, " < up"); - putstr(win, 0, " > down"); - Sprintf(buf, " %4s direct at yourself", - visctrl(Cmd.spkeys[NHKF_GETDIR_SELF])); - putstr(win, 0, buf); if (msg) { /* non-null msg means that this wasn't an explicit user request */ putstr(win, 0, ""); From 8964913f569c9f1792c80e1185c6792eabb65316 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 16 May 2017 18:45:04 -0700 Subject: [PATCH 03/19] more movement prefix vs invalid direction If user enters Fz when 'cmdassist' is off, respond with "Invalid direction for 'F' prefix." instead of "Unknown command 'Fz'." --- src/cmd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index d354195bf..fc29aaf6b 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 cmd.c $NHDT-Date: 1494976933 2017/05/16 23:22:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.257 $ */ +/* NetHack 3.6 cmd.c $NHDT-Date: 1494985492 2017/05/17 01:44:52 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.258 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -4445,10 +4445,13 @@ const char *msg; (sym == '<') ? "upward" : "downward"); } - /* if 'cmdassist', display via pline() and we're done (note: asking + /* if '!cmdassist', display via pline() and we're done (note: asking for help at getdir() prompt forces cmdassist for this operation) */ if (!viawindow) { - if (*buf) { + if (prefixhandling) { + if (!*buf) + Sprintf(buf, "Invalid direction for '%s' prefix.", + visctrl(Cmd.spkeys[spkey])); pline("%s", buf); return TRUE; } From 365464d9a48afe42849e1a313ca3578d44722b88 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 19 May 2017 15:19:39 -0700 Subject: [PATCH 04/19] dumplog revisions I saw dumplog text in a newsgroup posting and the only way I could recognize what version generated it was that "You entered the dungeon N turns ago" included the missing-until-recently final period. So, put nethack's one-line version information as the very first dumplog text and follow it with the dumped game's start and end date+time. (That information is useful to know in its own right, but also should prevent the build date+time shown with the version from confusing anybody about when the dump was written.) Along the way, I noticed that the 'counting' phase for artifact_score was being repeated for '#if DUMPLOG' even though it doesn't generate output. That had a side-effect of adding points for artifacts twice (applicable when final score was for an ascension or dungeon escape). --- doc/fixes36.1 | 2 ++ src/end.c | 79 +++++++++++++++++++++++++++++++-------------------- src/windows.c | 8 +++--- 3 files changed, 54 insertions(+), 35 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 37b7fac4d..456defcf8 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -422,6 +422,8 @@ humanoid pet could become hostile but still remain tame if it observed hero attacking a peaceful creature minor ^X/enlightenment bugs: grammar when poly'd into '1 hit dice' critter, missing punctuation for "You entered the dungeon N turns ago" +when configured with DUMPLOG enabled, artifacts were counted twice towards + final score Platform- and/or Interface-Specific Fixes diff --git a/src/end.c b/src/end.c index 7685a8226..7cd3e4304 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1489192539 2017/03/11 00:35:39 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.130 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1495232357 2017/05/19 22:19:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.131 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -59,7 +59,7 @@ STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *)); #ifdef DUMPLOG STATIC_DCL void NDECL(dump_plines); #endif -STATIC_DCL void FDECL(dump_everything, (int)); +STATIC_DCL void FDECL(dump_everything, (int, time_t)); STATIC_DCL int NDECL(num_extinct); #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2) @@ -680,8 +680,7 @@ dump_plines() extern char *saved_plines[]; extern unsigned saved_pline_index; - Strcpy(buf, " "); - putstr(0, 0, ""); + Strcpy(buf, " "); /* one space for indentation */ putstr(0, 0, "Latest messages:"); for (i = 0, j = (int) saved_pline_index; i < DUMPLOG_MSG_COUNT; ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) { @@ -697,27 +696,41 @@ dump_plines() } #endif +/*ARGSUSED*/ STATIC_OVL void -dump_everything(how) +dump_everything(how, when) int how; +time_t when; /* date+time at end of game */ { #ifdef DUMPLOG - struct obj *obj; - char pbuf[BUFSZ]; + char pbuf[BUFSZ], datetimebuf[24]; /* [24]: room for 64-bit bogus value */ dump_redirect(TRUE); if (!iflags.in_dumplog) return; - init_symbols(); + init_symbols(); /* revert to default symbol set */ - for (obj = invent; obj; obj = obj->nobj) { - makeknown(obj->otyp); - obj->known = obj->bknown = obj->dknown = obj->rknown = 1; - if (Is_container(obj) || obj->otyp == STATUE) - obj->cknown = obj->lknown = 1; - } + /* one line version ID, which includes build date+time; + it's conceivable that the game started with a different + build date+time or even with an older nethack version, + but we only have access to the one it finished under */ + putstr(0, 0, getversionstring(pbuf)); + putstr(0, 0, ""); + /* game start and end date+time to disambiguate version date+time */ + Strcpy(datetimebuf, yyyymmddhhmmss(ubirthday)); + Sprintf(pbuf, "Game began %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s", + &datetimebuf[0], &datetimebuf[4], &datetimebuf[6], + &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]); + Strcpy(datetimebuf, yyyymmddhhmmss(when)); + Sprintf(eos(pbuf), ", ended %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s.", + &datetimebuf[0], &datetimebuf[4], &datetimebuf[6], + &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]); + putstr(0, 0, pbuf); + putstr(0, 0, ""); + + /* character name and basic role info */ Sprintf(pbuf, "%s, %s %s %s %s", plname, aligns[1 - u.ualign.type].adj, genders[flags.female].adj, @@ -772,14 +785,7 @@ boolean taken; ask = should_query_disclose_option('i', &defquery); c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery; if (c == 'y') { - struct obj *obj; - - for (obj = invent; obj; obj = obj->nobj) { - makeknown(obj->otyp); - obj->known = obj->bknown = obj->dknown = obj->rknown = 1; - if (Is_container(obj) || obj->otyp == STATUE) - obj->cknown = obj->lknown = 1; - } + /* caller has already ID'd everything */ (void) display_inventory((char *) 0, TRUE); container_contents(invent, TRUE, TRUE, FALSE); } @@ -1166,10 +1172,26 @@ int how; if (have_windows) display_nhwindow(WIN_MESSAGE, FALSE); - if (strcmp(flags.end_disclose, "none") && how != PANICKED) - disclose(how, taken); + if (how != PANICKED) { + struct obj *obj; - dump_everything(how); + /* + * This is needed for both inventory disclosure and dumplog. + * Both are optional, so do it once here instead of duplicating + * it in both of those places. + */ + for (obj = invent; obj; obj = obj->nobj) { + makeknown(obj->otyp); + obj->known = obj->bknown = obj->dknown = obj->rknown = 1; + if (Is_container(obj) || obj->otyp == STATUE) + obj->cknown = obj->lknown = 1; + } + + if (strcmp(flags.end_disclose, "none")) + disclose(how, taken); + + dump_everything(how, endtime); + } /* finish_paybill should be called after disclosure but before bones */ if (bones_ok && taken) @@ -1319,11 +1341,6 @@ int how; /* count the points for artifacts */ artifact_score(invent, TRUE, endwin); -#ifdef DUMPLOG - dump_redirect(TRUE); - artifact_score(invent, TRUE, endwin); - dump_redirect(FALSE); -#endif viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */ mtmp = mydogs; @@ -1418,7 +1435,7 @@ int how; plur(umoney), moves, plur(moves)); dump_forward_putstr(endwin, 0, pbuf, done_stopprint); Sprintf(pbuf, - "You were level %d with a maximum of %d hit point%s when you %s.", + "You were level %d with a maximum of %d hit point%s when you %s.", u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]); dump_forward_putstr(endwin, 0, pbuf, done_stopprint); dump_forward_putstr(endwin, 0, "", done_stopprint); diff --git a/src/windows.c b/src/windows.c index 70c9feee7..55678a0b4 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 windows.c $NHDT-Date: 1488075979 2017/02/26 02:26:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.39 $ */ +/* NetHack 3.6 windows.c $NHDT-Date: 1495232365 2017/05/19 22:19:25 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.41 $ */ /* Copyright (c) D. Cohrs, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1033,7 +1033,7 @@ int behavior UNUSED, under UNUSED, over UNUSED; #endif /* STATUS_VIA_WINDOWPORT */ STATIC_VAR struct window_procs dumplog_windowprocs_backup; -STATIC_PTR FILE *dumplog_file; +STATIC_VAR FILE *dumplog_file; #ifdef DUMPLOG STATIC_VAR time_t dumplog_now; @@ -1152,8 +1152,8 @@ void dump_close_log() { if (dumplog_file) { - fclose(dumplog_file); - dumplog_file = NULL; + (void) fclose(dumplog_file); + dumplog_file = (FILE *) 0; } } From ed572e5e84900de493fd0970e8a7b64a6fb6de0f Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 19 May 2017 16:51:05 -0700 Subject: [PATCH 05/19] high priest extinction This issue has been around for a while but wasn't noticable to players until some post-3.6.0 tweaking of end of game disclosure. When testing the DUMPLOG artifact_score fix, I level teleported to the Astral Plane and performed a cheat ascension. Final disclosure listed high priests as extinct. Same thing would happen after visiting Moloch's Sanctum instead. (The latter didn't interfere with creating the Astral high priests if you got that far, just as creation of the first one there didn't prevent the other two.) I forget why high priests are flagged as unique (something I think I'm responisble for...), but they shouldn't share unique's setting of extinct during monster creation. (They could be set that way after 4 are created, but this fix doesn't do that. It just treats them like ordinary monsters so you'd need 127 or 255 or some such to make them become extinct. Unlike other creatures with a special creation limit, high priests can be produced when aligned priests gain experience--an event that I don't recall ever noticing happen.) --- doc/fixes36.1 | 3 +++ src/makemon.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 456defcf8..0c8a42b91 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -424,6 +424,9 @@ minor ^X/enlightenment bugs: grammar when poly'd into '1 hit dice' critter, missing punctuation for "You entered the dungeon N turns ago" when configured with DUMPLOG enabled, artifacts were counted twice towards final score +once Moloch's Sanctum (or Astral Plane via wizard mode level teleport direct + to end-game) was entered, end of game disclosure would reveal that + high priests had been incorrectly flagged as extinct Platform- and/or Interface-Specific Fixes diff --git a/src/makemon.c b/src/makemon.c index c60adfa1f..232e9d9a9 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 makemon.c $NHDT-Date: 1450451931 2015/12/18 15:18:51 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.106 $ */ +/* NetHack 3.6 makemon.c $NHDT-Date: 1495237801 2017/05/19 23:50:01 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -903,7 +903,7 @@ boolean ghostly; result = (((int) mvitals[mndx].born < lim) && !gone) ? TRUE : FALSE; /* if it's unique, don't ever make it again */ - if (mons[mndx].geno & G_UNIQ) + if ((mons[mndx].geno & G_UNIQ) && mndx != PM_HIGH_PRIEST) mvitals[mndx].mvflags |= G_EXTINCT; if (mvitals[mndx].born < 255 && tally From ead698fd029255581a89aa86b4b9f157d2d6db7a Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 20 May 2017 22:55:08 -0700 Subject: [PATCH 06/19] dumplog of map When writing the known portion of the current level's map into dumplog, discard any blank rows at the top (there will already be one blank line separating it from the text that precedes) and keep at most one blank row at the bottom (sometimes there won't be any). --- include/extern.h | 4 +++- src/detect.c | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/include/extern.h b/include/extern.h index e8e15e701..cabdb51b0 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1494107197 2017/05/06 21:46:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.587 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1495346095 2017/05/21 05:54:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.588 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -270,7 +270,9 @@ E void NDECL(warnreveal); E int FDECL(dosearch0, (int)); E int NDECL(dosearch); E void NDECL(sokoban_detect); +#ifdef DUMPLOG E void NDECL(dump_map); +#endif E void FDECL(reveal_terrain, (int, int)); /* ### dig.c ### */ diff --git a/src/detect.c b/src/detect.c index def22469e..86e3159bf 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 detect.c $NHDT-Date: 1491705573 2017/04/09 02:39:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.76 $ */ +/* NetHack 3.6 detect.c $NHDT-Date: 1495346103 2017/05/21 05:55:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.77 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1799,15 +1799,28 @@ int default_glyph, which_subset; return glyph; } +#ifdef DUMPLOG void dump_map() { - int x, y, glyph; + int x, y, glyph, skippedrows; int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON; int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone); char buf[BUFSZ]; + boolean blankrow, toprow; + /* + * Squeeze out excess vertial space when dumping the map. + * If there are any blank map rows at the top, suppress them + * (our caller has already printed a separator). If there is + * more than one blank map row at the bottom, keep just one. + * Any blank rows within the middle of the map are kept. + * Note: putstr() with winid==0 is for dumplog. + */ + skippedrows = 0; + toprow = TRUE; for (y = 0; y < ROWNO; y++) { + blankrow = TRUE; /* assume blank until we discover otherwise */ for (x = 1; x < COLNO; x++) { int ch, color; unsigned special; @@ -1815,12 +1828,27 @@ dump_map() glyph = reveal_terrain_getglyph(x,y, FALSE, u.uswallow, default_glyph, subset); (void) mapglyph(glyph, &ch, &color, &special, x, y); - buf[x-1] = ch; + buf[x - 1] = ch; + if (ch != ' ') + blankrow = FALSE; + } + if (!blankrow) { + buf[x - 2] = '\0'; + if (toprow) { + skippedrows = 0; + toprow = FALSE; + } + for (x = 0; x < skippedrows; x++) + putstr(0, 0, ""); + putstr(0, 0, buf); /* map row #y */ + } else { + ++skippedrows; } - buf[x-2] = '\0'; - putstr(0,0, buf); } + if (skippedrows) + putstr(0, 0, ""); } +#endif /* DUMPLOG */ /* idea from crawl; show known portion of map without any monsters, objects, or traps occluding the view of the underlying terrain */ From 28fb9857577068c2f8b955e1a4b1f0400f4b246b Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 22 May 2017 16:02:50 -0700 Subject: [PATCH 07/19] fix #H5486 - artifact naming vs illiterate Attempting to name the relevant type of item with an artifact's name (such as a runesword with "Stormbringer") fails with "your hand slips" in order to prevent turning the item into an aritfact. Since that only happens when the name is valid, it indicates that the hero is literate so violate illiterate conduct. (Naming items in general doesn't affect [il]literacy so that player can assign names to inventory or stash items to maintain notes.) --- doc/fixes36.1 | 2 ++ src/do_name.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 0c8a42b91..f8139b1ae 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -427,6 +427,8 @@ when configured with DUMPLOG enabled, artifacts were counted twice towards once Moloch's Sanctum (or Astral Plane via wizard mode level teleport direct to end-game) was entered, end of game disclosure would reveal that high priests had been incorrectly flagged as extinct +attempting to name an item as an artifact and failing via hand slip violates + illiterate conduct Platform- and/or Interface-Specific Fixes diff --git a/src/do_name.c b/src/do_name.c index 34a853159..aa8bdf1e9 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_name.c $NHDT-Date: 1489494376 2017/03/14 12:26:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */ +/* NetHack 3.6 do_name.c $NHDT-Date: 1495494156 2017/05/22 23:02:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.118 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1081,6 +1081,9 @@ register struct obj *obj; pline("While engraving, your %s slips.", body_part(HAND)); display_nhwindow(WIN_MESSAGE, FALSE); You("engrave: \"%s\".", buf); + /* violate illiteracy conduct since hero attempted to write + a valid artifact name */ + u.uconduct.literate++; } ++via_naming; /* This ought to be an argument rather than a static... */ obj = oname(obj, buf); From a791b4b16c1b435240ca4ca30fae7989daacbc81 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 26 May 2017 15:01:43 -0700 Subject: [PATCH 08/19] fix #H5507 - vampire death message bug Fix a couple of weird messages issued when a shape-shifted vampire is "killed" and reverts to regular vampire form instead of dying. First weird one was when vampire has been given a name, as reported. Second was noticed while fixing that: when cause of death destroys the creature so thoroughly that there'd be no corpse, the alternate phrasing for noncorporeal or amorphous form should be used. old: The Dracula suddenly transforms and rises as Dracula! The vampire bat is disintegrated. The seemingly dead vampire bat suddenly transforms and rises as a vampire! new: Dracula suddenly transforms and rises as a vampire! The vampire bat is disintegrated. The seemingly dead vampire bat suddenly reconstitutes and rises as a vampire! --- doc/fixes36.1 | 7 +++++++ src/mon.c | 38 +++++++++++++++++++++++--------------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index f8139b1ae..27c588e5e 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -382,6 +382,13 @@ removing a blindfold with 'A' took two turns, with 'R' (and 'T') only one, and could result in a panic if the blindfold was stolen during removal cmdassist help for movement prefix followed by invalid direction was strange when the direction was up, down, or self disallowed for that prefix +poor message when shape-shifted vampire reverts to vampire if it has a name: + The Dracula suddenly transforms and rises as Dracula. +poor message when shape-shifted vampire reverts if cause of 'death' was + disintegration or digestion and shifted form wasn't amorphous: + The vampire bat is disintegrated. The vampire bat suddenly transforms + and rises as a vampire. (fix: switch to existing alternate phrasing + used for amorphous form, "reconstitute" rather than "transform") Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/mon.c b/src/mon.c index 8276d4792..25a2a1322 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1492733171 2017/04/21 00:06:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.237 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1495836090 2017/05/26 22:01:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.238 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -11,7 +11,7 @@ #include "mfndpos.h" #include -STATIC_VAR boolean vamp_rise_msg; +STATIC_VAR boolean vamp_rise_msg, disintegested; STATIC_DCL void FDECL(sanity_check_single_mon, (struct monst *, BOOLEAN_P, const char *)); @@ -1826,16 +1826,21 @@ register struct monst *mtmp; /* alternate message phrasing for some monster types */ spec_mon = (nonliving(mtmp->data) || noncorporeal(mtmp->data) - || amorphous(mtmp->data)); + || amorphous(mtmp->data)), + spec_death = (disintegested /* disintegrated or digested */ + || noncorporeal(mtmp->data) + || amorphous(mtmp->data)); - /* construct a format string before transformation */ - Sprintf(buf, "The %s%s suddenly %s and rises as %%s!", - spec_mon ? "" : "seemingly dead ", - x_monnam(mtmp, ARTICLE_NONE, (char *) 0, + /* construct a format string before transformation; + will be capitalized when used, expects one %s arg */ + Sprintf(buf, "%s suddenly %s and rises as %%s!", + x_monnam(mtmp, + has_mname(mtmp) ? ARTICLE_NONE : ARTICLE_THE, + spec_mon ? (char *) 0 : "seemingly dead", SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION | SUPPRESS_INVISIBLE | SUPPRESS_IT, FALSE), - spec_mon ? "reconstitutes" : "transforms"); + spec_death ? "reconstitutes" : "transforms"); mtmp->mcanmove = 1; mtmp->mfrozen = 0; if (mtmp->mhpmax <= 0) @@ -1857,7 +1862,9 @@ register struct monst *mtmp; else mtmp->cham = mndx; if (canspotmon(mtmp)) { - pline(buf, a_monnam(mtmp)); + /* was using a_monnam(mtmp) but that's weird if mtmp is named: + "Dracula suddenly transforms and rises as Dracula" */ + pline(upstart(buf), an(mtmp->data->mname)); vamp_rise_msg = TRUE; } newsym(x, y); @@ -1887,9 +1894,7 @@ register struct monst *mtmp; else if (mtmp->data == &mons[PM_WERERAT]) set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT], -1); - /* if MAXMONNO monsters of a given type have died, and it - * can be done, extinguish that monster. - * + /* * mvitals[].died does double duty as total number of dead monsters * and as experience factor for the player killing more monsters. * this means that a dragon dying by other means reduces the @@ -2145,7 +2150,8 @@ int how; be_sad = (mdef->mtame != 0); /* no corpses if digested or disintegrated */ - if (how == AD_DGST || how == -AD_RBRE) + disintegested = (how == AD_DGST || how == -AD_RBRE); + if (disintegested) mondead(mdef); else mondied(mdef); @@ -2241,16 +2247,18 @@ int xkill_flags; /* 1: suppress message, 2: suppress corpse, 4: pacifist */ thrownobj = 0; } - vamp_rise_msg = FALSE; /* might get set in mondead() */ + vamp_rise_msg = FALSE; /* might get set in mondead(); only checked below */ + disintegested = nocorpse; /* alternate vamp_rise message needed if true */ /* dispose of monster and make cadaver */ if (stoned) monstone(mtmp); else mondead(mtmp); + disintegested = FALSE; /* reset */ if (mtmp->mhp > 0) { /* monster lifesaved */ /* Cannot put the non-visible lifesaving message in - * lifesaved_monster() since the message appears only when you + * lifesaved_monster() since the message appears only when _you_ * kill it (as opposed to visible lifesaving which always appears). */ stoned = FALSE; From 75c7af025c789dea050ebb69a56272aa286435a6 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 26 May 2017 18:51:37 -0700 Subject: [PATCH 09/19] simplify vampshift message Simlify the earlier revision for "the seemingly dead creature suddenly transforms and turns into a vampire". x_monnam() already handles "the" the way we want here: it's suppressed when the monster has a name assigned and the "seemingly dead" phrase is omitted. --- src/mon.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/mon.c b/src/mon.c index 25a2a1322..ddd8e3a8a 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1495836090 2017/05/26 22:01:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.238 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1495849874 2017/05/27 01:51:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.239 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1834,8 +1834,7 @@ register struct monst *mtmp; /* construct a format string before transformation; will be capitalized when used, expects one %s arg */ Sprintf(buf, "%s suddenly %s and rises as %%s!", - x_monnam(mtmp, - has_mname(mtmp) ? ARTICLE_NONE : ARTICLE_THE, + x_monnam(mtmp, ARTICLE_THE, spec_mon ? (char *) 0 : "seemingly dead", SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION | SUPPRESS_INVISIBLE | SUPPRESS_IT, From f5c157cb4bfe1fd7912ce268a43e39826cffa8a4 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 29 May 2017 12:47:40 -0700 Subject: [PATCH 10/19] fix #H5524 - green dragon breath > Green dragons should probably not cough in their own breath. They took no damage, but they did become blind. Make creatures who breath poison gas (adult green dragons, the Chromatic Dragon) be immune to gas clouds like non-breathing creatures are. There's no way to distinguish gas left behind by dragon breath from that created by scroll of stinking cloud, so gas breathers are no longer affected by the latter. --- doc/fixes36.1 | 1 + src/region.c | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 27c588e5e..320666239 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -389,6 +389,7 @@ poor message when shape-shifted vampire reverts if cause of 'death' was The vampire bat is disintegrated. The vampire bat suddenly transforms and rises as a vampire. (fix: switch to existing alternate phrasing used for amorphous form, "reconstitute" rather than "transform") +adult green dragons and the Chromatic Dragon were blinded by gas clouds Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/src/region.c b/src/region.c index b38936c81..d91b9fa39 100644 --- a/src/region.c +++ b/src/region.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 region.c $NHDT-Date: 1446892454 2015/11/07 10:34:14 $ $NHDT-Branch: master $:$NHDT-Revision: 1.36 $ */ +/* NetHack 3.6 region.c $NHDT-Date: 1496087244 2017/05/29 19:47:24 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.40 $ */ /* Copyright (c) 1996 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -969,9 +969,15 @@ genericptr_t p2; } else { /* A monster is inside the cloud */ mtmp = (struct monst *) p2; - /* Non living and non breathing monsters are not concerned */ + /* Non living and non breathing monsters are not concerned; + adult green dragon is not affected by gas cloud, baby one is */ if (!(nonliving(mtmp->data) || is_vampshifter(mtmp)) - && !breathless(mtmp->data)) { + && !breathless(mtmp->data) + /* exclude monsters with poison gas breath attack: + adult green dragon and Chromatic Dragon (and iron golem, + but nonliving() and breathless() tests also catch that) */ + && !(attacktype_fordmg(mtmp->data, AT_BREA, AD_DRST) + || attacktype_fordmg(mtmp->data, AT_BREA, AD_RBRE))) { if (cansee(mtmp->mx, mtmp->my)) pline("%s coughs!", Monnam(mtmp)); if (heros_fault(reg)) From 0e18cf31e87d3c57af2b20195c6428431590919d Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 29 May 2017 16:37:30 -0700 Subject: [PATCH 11/19] displaying detected tame monsters This started out as a fix for '#H5460 - minor monster detection bug' but that report turned out to be wrong. It claimed that pets weren't highlighted as pets if the only way to observe them was via extended monster detection, but the code (both 3.6.0 and current) indicates otherwise. Detected monster highlighting is bypassed for pets. Reorganize the code slightly to emphasize that this is intentional: tameness trumps remote detection when choosing which highlight method. For tty, if hilite_pet and use_inverse are both enabled or both disabled, you can't see the difference anyway. At least I can't.... That report also wanted the use_inverse option to be changed (I guess it's overloaded for multiple things) so I haven't marked #H5460 as closed. --- src/display.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/display.c b/src/display.c index d3a4e50ae..bede97c3c 100644 --- a/src/display.c +++ b/src/display.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 display.c $NHDT-Date: 1463614572 2016/05/18 23:36:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.86 $ */ +/* NetHack 3.6 display.c $NHDT-Date: 1496101037 2017/05/29 23:37:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.87 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -424,24 +424,29 @@ xchar worm_tail; /* mon is actually a worm tail */ } } - /* If the mimic is unsuccessfully mimicing something, display the monster + /* If the mimic is unsuccessfully mimicing something, display the monster. */ if (!mon_mimic || sensed) { int num; /* [ALI] Only use detected glyphs when monster wouldn't be * visible by any other means. + * + * There are no glyphs for "detected pets" so we have to + * decide whether to display such things as detected or as tame. + * If both are being highlighted in the same way, it doesn't + * matter, but if not, showing them as pets is preferrable. */ - if (sightflags == DETECTED && !mon->mtame) { - if (worm_tail) - num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); - else - num = detected_mon_to_glyph(mon); - } else if (mon->mtame && !Hallucination) { + if (mon->mtame && !Hallucination) { if (worm_tail) num = petnum_to_glyph(PM_LONG_WORM_TAIL); else num = pet_to_glyph(mon); + } else if (sightflags == DETECTED) { + if (worm_tail) + num = detected_monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); + else + num = detected_mon_to_glyph(mon); } else { if (worm_tail) num = monnum_to_glyph(what_mon(PM_LONG_WORM_TAIL)); @@ -666,7 +671,7 @@ xchar x, y; show_glyph(x, y, lev->glyph = cmap_to_glyph(S_corr)); } /* draw monster on top if we can sense it */ - if ((x != u.ux || y != u.uy) && (mon = m_at(x, y)) && sensemon(mon)) + if ((x != u.ux || y != u.uy) && (mon = m_at(x, y)) != 0 && sensemon(mon)) display_monster(x, y, mon, (tp_sensemon(mon) || MATCH_WARN_OF_MON(mon)) ? PHYSICALLY_SEEN From b60465607751efb3e4ab6c6df3fd7f5c46acbd16 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 29 May 2017 17:17:36 -0700 Subject: [PATCH 12/19] explode.c bits Update a comment prompted by '#H5459 - explosions and steeds'. Also a couple of formatting changes. This doesn't attempt to address the reported issue: hero's and steed's resistances are intermingled when exploding at the hero's coordinates and influence the effect on both. Inherited from the behavior for an engulfed hero, and might not necessarily be a bug. --- src/explode.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/explode.c b/src/explode.c index e26e58d8a..74dfcdae2 100644 --- a/src/explode.c +++ b/src/explode.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 explode.c $NHDT-Date: 1450915435 2015/12/24 00:03:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.45 $ */ +/* NetHack 3.6 explode.c $NHDT-Date: 1496103440 2017/05/30 00:17:20 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.49 $ */ /* Copyright (C) 1990 by Ken Arromdee */ /* NetHack may be freely redistributed. See license for details. */ @@ -174,7 +174,7 @@ int expltype; break; } } - /* can be both you and mtmp if you're swallowed */ + /* can be both you and mtmp if you're swallowed or riding */ mtmp = m_at(i + x - 1, j + y - 1); if (!mtmp && i + x - 1 == u.ux && j + y - 1 == u.uy) mtmp = u.usteed; @@ -392,8 +392,7 @@ int expltype; } else { /* call resist with 0 and do damage manually so 1) we can * get out the message before doing the damage, and 2) we - * can - * call mondied, not killed, if it's not your blast + * can call mondied, not killed, if it's not your blast */ int mdam = dam; @@ -512,12 +511,10 @@ int expltype; } if (shopdamage) { - pay_for_damage(adtyp == AD_FIRE - ? "burn away" - : adtyp == AD_COLD - ? "shatter" - : adtyp == AD_DISN ? "disintegrate" - : "destroy", + pay_for_damage((adtyp == AD_FIRE) ? "burn away" + : (adtyp == AD_COLD) ? "shatter" + : (adtyp == AD_DISN) ? "disintegrate" + : "destroy", FALSE); } From 3512297f595d9873802582c9c490e82656523cac Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 3 Jun 2017 16:05:23 -0700 Subject: [PATCH 13/19] fix #H5547 - named vampire shapeshifting message Report was about "Pet vampire" but the relevant aspect was that the vampire had been assigned a name, not that it was tame: You observe a Hilda where a Hilda was. Investigating this has uncovered two other bugs, one potentially serious. m_monnam() overrides hallucination but seems to be getting used to some situations where hallucination should be honored (several instances). Dynamically constructed format strings are including monster or object names in the format (rather than the usual use as arguments), so player assigned names containing percent signs could cause havoc (a few instances). This fixes some of the former and one of the latter, but doesn't deal with various other cases revealed by grep. --- doc/fixes36.1 | 2 ++ include/decl.h | 3 ++- include/extern.h | 3 ++- src/do_name.c | 24 +++++++++++++++++++----- src/mon.c | 17 +++-------------- src/monmove.c | 18 ++++++++++-------- 6 files changed, 38 insertions(+), 29 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 320666239..5f13a649c 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -389,6 +389,8 @@ poor message when shape-shifted vampire reverts if cause of 'death' was The vampire bat is disintegrated. The vampire bat suddenly transforms and rises as a vampire. (fix: switch to existing alternate phrasing used for amorphous form, "reconstitute" rather than "transform") +poor message when named vampire shifts shape within view: + You observe a Dracula where a Dracula was. adult green dragons and the Chromatic Dragon were blinded by gas clouds diff --git a/include/decl.h b/include/decl.h index 21edb3cac..811324abb 100644 --- a/include/decl.h +++ b/include/decl.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 decl.h $NHDT-Date: 1432512782 2015/05/25 00:13:02 $ $NHDT-Branch: master $:$NHDT-Revision: 1.76 $ */ +/* NetHack 3.6 decl.h $NHDT-Date: 1496531104 2017/06/03 23:05:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.82 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -317,6 +317,7 @@ E const char *materialnm[]; #define SUPPRESS_HALLUCINATION 0x04 #define SUPPRESS_SADDLE 0x08 #define EXACT_NAME 0x0F +#define SUPPRESS_NAME 0x10 /* Vision */ E NEARDATA boolean vision_full_recalc; /* TRUE if need vision recalc */ diff --git a/include/extern.h b/include/extern.h index cabdb51b0..98a258cef 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1495346095 2017/05/21 05:54:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.588 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1496531111 2017/06/03 23:05:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.589 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -404,6 +404,7 @@ E char *FDECL(mon_nam, (struct monst *)); E char *FDECL(noit_mon_nam, (struct monst *)); E char *FDECL(Monnam, (struct monst *)); E char *FDECL(noit_Monnam, (struct monst *)); +E char *FDECL(noname_monnam, (struct monst *, int)); E char *FDECL(m_monnam, (struct monst *)); E char *FDECL(y_monnam, (struct monst *)); E char *FDECL(Adjmonnam, (struct monst *, const char *)); diff --git a/src/do_name.c b/src/do_name.c index aa8bdf1e9..452f6c555 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_name.c $NHDT-Date: 1495494156 2017/05/22 23:02:36 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.118 $ */ +/* NetHack 3.6 do_name.c $NHDT-Date: 1496531112 2017/06/03 23:05:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.119 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1392,6 +1392,8 @@ rndghostname() * a_monnam: a newt it an invisible orc Fido * m_monnam: newt xan orc Fido * y_monnam: your newt your xan your invisible orc Fido + * noname_monnam(mon,article): + * article newt art xan art invisible orc art dog */ /* Bug: if the monster is a priest or shopkeeper, not every one of these @@ -1412,13 +1414,14 @@ const char *adjective; int suppress; /* SUPPRESS_IT, SUPPRESS_INVISIBLE, SUPPRESS_HALLUCINATION, SUPPRESS_SADDLE. * EXACT_NAME: combination of all the above + * SUPPRESS_NAME: omit monster's assigned name (unless uniq w/ pname). */ boolean called; { char *buf = nextmbuf(); struct permonst *mdat = mtmp->data; const char *pm_name = mdat->mname; - boolean do_hallu, do_invis, do_it, do_saddle; + boolean do_hallu, do_invis, do_it, do_saddle, do_name; boolean name_at_start, has_adjectives; char *bp; @@ -1433,6 +1436,7 @@ boolean called; && !program_state.gameover && mtmp != u.usteed && !(u.uswallow && mtmp == u.ustuck) && !(suppress & SUPPRESS_IT); do_saddle = !(suppress & SUPPRESS_SADDLE); + do_name = !(suppress & SUPPRESS_NAME) || type_is_pname(mdat); buf[0] = '\0'; @@ -1513,7 +1517,7 @@ boolean called; Strcat(buf, rname); name_at_start = bogon_is_pname(rnamecode); - } else if (has_mname(mtmp)) { + } else if (do_name && has_mname(mtmp)) { char *name = MNAME(mtmp); if (mdat == &mons[PM_GHOST]) { @@ -1608,7 +1612,7 @@ struct monst *mtmp; { return x_monnam(mtmp, ARTICLE_THE, (char *) 0, (has_mname(mtmp)) ? (SUPPRESS_SADDLE | SUPPRESS_IT) - : SUPPRESS_IT, + : SUPPRESS_IT, FALSE); } @@ -1632,7 +1636,17 @@ struct monst *mtmp; return bp; } -/* monster's own name */ +/* return "a dog" rather than "Fido", honoring hallucination and visibility */ +char * +noname_monnam(mtmp, article) +struct monst *mtmp; +int article; +{ + return x_monnam(mtmp, article, (char *) 0, SUPPRESS_NAME, FALSE); +} + +/* monster's own name -- overrides hallucination and [in]visibility + so shouldn't be used in ordinary messages (mainly for disclosure) */ char * m_monnam(mtmp) struct monst *mtmp; diff --git a/src/mon.c b/src/mon.c index ddd8e3a8a..33a84372e 100644 --- a/src/mon.c +++ b/src/mon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mon.c $NHDT-Date: 1495849874 2017/05/27 01:51:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.239 $ */ +/* NetHack 3.6 mon.c $NHDT-Date: 1496531114 2017/06/03 23:05:14 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.240 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -3458,7 +3458,7 @@ boolean msg; /* "The oldmon turns into a newmon!" */ if (is_vampshifter(mtmp)) { Sprintf(msgtrail, " which was a shapeshifted %s", - m_monnam(mtmp)); + noname_monnam(mtmp, ARTICLE_NONE)); } else if (is_animal(mdat)) { Strcpy(msgtrail, "'s stomach"); } else { @@ -3500,16 +3500,7 @@ boolean msg; /* "The oldmon turns into a newmon!" */ newsym(mtmp->mx, mtmp->my); if (msg) { - char *save_mname = 0; - - if (has_mname(mtmp)) { - save_mname = MNAME(mtmp); - MNAME(mtmp) = (char *) 0; - } - Strcpy(newname, (mdat == &mons[PM_GREEN_SLIME]) - ? "slime" - : x_monnam(mtmp, ARTICLE_A, (char *) 0, - SUPPRESS_SADDLE, FALSE)); + Strcpy(newname, noname_monnam(mtmp, ARTICLE_A)); /* oldname was capitalized above; newname will be lower case */ if (!strcmpi(newname, "it")) { /* can't see or sense it now */ if (!!strcmpi(oldname, "it")) /* could see or sense it before */ @@ -3521,8 +3512,6 @@ boolean msg; /* "The oldmon turns into a newmon!" */ else pline("%s turns into %s!", oldname, newname); } - if (save_mname) - MNAME(mtmp) = save_mname; } /* when polymorph trap/wand/potion produces a vampire, turn in into diff --git a/src/monmove.c b/src/monmove.c index 0b60d0c15..ece138bba 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 monmove.c $NHDT-Date: 1463704424 2016/05/20 00:33:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.87 $ */ +/* NetHack 3.6 monmove.c $NHDT-Date: 1496531115 2017/06/03 23:05:15 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.90 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1625,13 +1625,11 @@ struct permonst *ptr; boolean domsg; { int reslt = 0; - char fmtstr[BUFSZ]; + char oldmtype[BUFSZ]; + + /* remember current monster type before shapechange */ + Strcpy(oldmtype, domsg ? noname_monnam(mon, ARTICLE_THE) : ""); - if (domsg) { - Sprintf(fmtstr, "You %s %%s where %s was.", - sensemon(mon) ? "now detect" : "observe", - an(m_monnam(mon))); - } if (mon->data == ptr) { /* already right shape */ reslt = 1; @@ -1639,9 +1637,13 @@ boolean domsg; } else if (is_vampshifter(mon)) { reslt = newcham(mon, ptr, FALSE, FALSE); } + if (reslt && domsg) { - pline(fmtstr, an(m_monnam(mon))); + pline("You %s %s where %s was.", + !canseemon(mon) ? "now detect" : "observe", + noname_monnam(mon, ARTICLE_A), oldmtype); } + return reslt; } From 5d8269a8818b0d4015bf5c6b88dcbebf6ead7365 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 3 Jun 2017 17:05:40 -0700 Subject: [PATCH 14/19] vampshifting fog could vs door Noticed while composing a reply to the #H5547 report about named vampire shape-shift message. The message when a vampire (possibly in vampire bat form) turned into a fog cloud in order to pass under a closed door was using a stale cached mon->data value when choosing the verb. So normal fog cloud or vampire already in fog cloud shape would "flow" under the door, but newly shifted fog cloud would "ooze" under the door. I saw this while testing the previous patch but its significance didn't register at the time. --- doc/fixes36.1 | 1 + src/monmove.c | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 5f13a649c..d010c2541 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -391,6 +391,7 @@ poor message when shape-shifted vampire reverts if cause of 'death' was used for amorphous form, "reconstitute" rather than "transform") poor message when named vampire shifts shape within view: You observe a Dracula where a Dracula was. +vampire shifting into fog cloud to pass under door "oozed" rather than "flowed" adult green dragons and the Chromatic Dragon were blinded by gas clouds diff --git a/src/monmove.c b/src/monmove.c index ece138bba..92cdac8dd 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 monmove.c $NHDT-Date: 1496531115 2017/06/03 23:05:15 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.90 $ */ +/* NetHack 3.6 monmove.c $NHDT-Date: 1496534703 2017/06/04 00:05:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.91 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1250,10 +1250,12 @@ postmov: || (can_fog(mtmp) && vamp_shift(mtmp, &mons[PM_FOG_CLOUD], canspotmon(mtmp))))) { + /* update cached value for vamp_shift() case */ + ptr = mtmp->data; if (flags.verbose && canseemon(mtmp)) pline("%s %s under the door.", Monnam(mtmp), (ptr == &mons[PM_FOG_CLOUD] - || ptr == &mons[PM_YELLOW_LIGHT]) + || ptr->mlet == S_LIGHT) ? "flows" : "oozes"); } else if (here->doormask & D_LOCKED && can_unlock) { From 50e5047855f6d946e309500e78771c05d617390c Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 3 Jun 2017 20:34:03 -0700 Subject: [PATCH 15/19] 'A' interruption Extend the 'A' blindfold fix from three weeks ago to cover weapons too. This might fix the problem being caught via curse(NULL) when using 'A' to remove multiple items. The blindfold bug was straight- forward since it was requiring two turns but not checking for loss of blindfold when interrupted by theft. Weapon/alt-weapon/quiver each only need one turn so I'm not sure what's really happening to trigger problems for them. --- src/do_wear.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/do_wear.c b/src/do_wear.c index e60df04dc..197e69e80 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_wear.c $NHDT-Date: 1494545163 2017/05/11 23:26:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.95 $ */ +/* NetHack 3.6 do_wear.c $NHDT-Date: 1496547229 2017/06/04 03:33:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.96 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1241,6 +1241,12 @@ struct obj *otmp; result = (what == RIGHT_RING); else if (otmp == ublindf) result = (what == WORN_BLINDF); + else if (otmp == uwep) + result = (what == W_WEP); + else if (otmp == uswapwep) + result = (what == W_SWAPWEP); + else if (otmp == uquiver) + result = (what == W_QUIVER); return result; } @@ -1279,6 +1285,12 @@ struct obj *otmp; result = (what == RIGHT_RING); else if (otmp == ublindf) result = (what == WORN_BLINDF); + else if (otmp == uwep) + result = (what == W_WEP); + else if (otmp == uswapwep) + result = (what == W_SWAPWEP); + else if (otmp == uquiver) + result = (what == W_QUIVER); return result; } @@ -1323,7 +1335,7 @@ struct obj *stolenobj; /* no message if stolenobj is already being doffing */ cancel_don(); /* don't want _on() or _off() being called by unmul() since the on or off action isn't completing */ - afternmv = 0; + afternmv = (int NDECL((*))) 0; if (putting_on || otmp != stolenobj) { Sprintf(buf, "You stop %s %s.", putting_on ? "putting on" : "taking off", From 3046b1d7ecd9bbf70c0134d5feeaef76e00dc4b5 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 4 Jun 2017 15:22:00 -0700 Subject: [PATCH 16/19] 'A' again - avoid stale takeoff.mask The do_wear.c part just eliminates some redundant code but shouldn't produce any change in behavior. The steal.c part should fix problems with 'A' when outer items are taken off during theft in order to steal an inner item, where the outer item is next to be removed (call to cancel_don() wasn't being made). The wield.c part matches the X_off() behavior and is needed to handle a weapon item that's slated for removal but isn't next (so wouldn't pass the donning()/doffing() test to trigger cancel_don()). If this seems a lot like trial and error, it is.... --- src/do_wear.c | 47 ++++++++++++----------------------------------- src/steal.c | 15 ++++++++------- src/wield.c | 8 +++++++- 3 files changed, 27 insertions(+), 43 deletions(-) diff --git a/src/do_wear.c b/src/do_wear.c index 197e69e80..2d16d34d1 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_wear.c $NHDT-Date: 1496547229 2017/06/04 03:33:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.96 $ */ +/* NetHack 3.6 do_wear.c $NHDT-Date: 1496614914 2017/06/04 22:21:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.97 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1200,53 +1200,30 @@ struct obj *obj; /* if null, do all worn items; otherwise just obj itself */ } /* check whether the target object is currently being put on (or taken off-- - also checks for doffing) */ + also checks for doffing--[why?]) */ boolean donning(otmp) struct obj *otmp; { - /* long what = (occupation == take_off) ? context.takeoff.what : 0L; */ - long what = context.takeoff.what; /* if nonzero, occupation is implied */ boolean result = FALSE; /* 'W' and 'T' set afternmv, 'A' sets context.takeoff.what */ - if (otmp == uarm) - result = (afternmv == Armor_on || afternmv == Armor_off - || what == WORN_ARMOR); + if (doffing(otmp)) + result = TRUE; + else if (otmp == uarm) + result = (afternmv == Armor_on); else if (otmp == uarmu) - result = (afternmv == Shirt_on || afternmv == Shirt_off - || what == WORN_SHIRT); + result = (afternmv == Shirt_on); else if (otmp == uarmc) - result = (afternmv == Cloak_on || afternmv == Cloak_off - || what == WORN_CLOAK); + result = (afternmv == Cloak_on); else if (otmp == uarmf) - result = (afternmv == Boots_on || afternmv == Boots_off - || what == WORN_BOOTS); + result = (afternmv == Boots_on); else if (otmp == uarmh) - result = (afternmv == Helmet_on || afternmv == Helmet_off - || what == WORN_HELMET); + result = (afternmv == Helmet_on); else if (otmp == uarmg) - result = (afternmv == Gloves_on || afternmv == Gloves_off - || what == WORN_GLOVES); + result = (afternmv == Gloves_on); else if (otmp == uarms) - result = (afternmv == Shield_on || afternmv == Shield_off - || what == WORN_SHIELD); - /* these 1-turn items don't need 'afternmv' checks - [and may not actually need 'what' checks] */ - else if (otmp == uamul) - result = (what == WORN_AMUL); - else if (otmp == uleft) - result = (what == LEFT_RING); - else if (otmp == uright) - result = (what == RIGHT_RING); - else if (otmp == ublindf) - result = (what == WORN_BLINDF); - else if (otmp == uwep) - result = (what == W_WEP); - else if (otmp == uswapwep) - result = (what == W_SWAPWEP); - else if (otmp == uquiver) - result = (what == W_QUIVER); + result = (afternmv == Shield_on); return result; } diff --git a/src/steal.c b/src/steal.c index a973bf7a2..203bf331a 100644 --- a/src/steal.c +++ b/src/steal.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 steal.c $NHDT-Date: 1456618998 2016/02/28 00:23:18 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.67 $ */ +/* NetHack 3.6 steal.c $NHDT-Date: 1496614914 2017/06/04 22:21:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.69 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -560,18 +560,19 @@ struct monst *mtmp; /* take off outer gear if we're targetting [hypothetical] quest artifact suit, shirt, gloves, or rings */ if ((otmp == uarm || otmp == uarmu) && uarmc) - (void) Cloak_off(); + remove_worn_item(uarmc, FALSE); if (otmp == uarmu && uarm) - (void) Armor_off(); + remove_worn_item(uarm, FALSE); if ((otmp == uarmg || ((otmp == uright || otmp == uleft) && uarmg)) && uwep) { /* gloves are about to be unworn; unwield weapon(s) first */ - if (u.twoweap) - uswapwepgone(); /* will clear u.twoweap */ - uwepgone(); + if (u.twoweap) /* remove_worn_item(uswapwep) indirectly */ + remove_worn_item(uswapwep, FALSE); /* clears u.twoweap */ + remove_worn_item(uwep, FALSE); } if ((otmp == uright || otmp == uleft) && uarmg) - (void) Gloves_off(); /* handles wielded cockatrice corpse */ + /* calls Gloves_off() to handle wielded cockatrice corpse */ + remove_worn_item(uarmg, FALSE); /* finally, steal the target item */ if (otmp->owornmask) diff --git a/src/wield.c b/src/wield.c index 585e971c9..d2f51a2ee 100644 --- a/src/wield.c +++ b/src/wield.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wield.c $NHDT-Date: 1461967849 2016/04/29 22:10:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.49 $ */ +/* NetHack 3.6 wield.c $NHDT-Date: 1496614915 2017/06/04 22:21:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.53 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -676,6 +676,10 @@ uwepgone() pline("%s shining.", Tobjnam(uwep, "stop")); } setworn((struct obj *) 0, W_WEP); + /* update takeoff mask in case uwep has just gone away while + its removal via 'A' was pending, similar to what the various + Xyzzy_off(do_wear.c) routines do for armor and accessories */ + context.takeoff.mask &= ~W_WEP; unweapon = TRUE; update_inventory(); } @@ -686,6 +690,7 @@ uswapwepgone() { if (uswapwep) { setworn((struct obj *) 0, W_SWAPWEP); + context.takeoff.mask &= ~W_SWAPWEP; update_inventory(); } } @@ -695,6 +700,7 @@ uqwepgone() { if (uquiver) { setworn((struct obj *) 0, W_QUIVER); + context.takeoff.mask &= ~W_QUIVER; update_inventory(); } } From c377b584fcb28113234f513b097e0998bbf27bd2 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 4 Jun 2017 16:32:17 -0700 Subject: [PATCH 17/19] m_monnam() usage m_monnam() overrides hallucination, which is appropriate in some situations but not others. This fixes one instance where it was being misused: discovering a hidden monster when another monster attacks it was calling either m_monnam() or a_monnam(); one ignores hallucination and the other doesn't, so accurate or inaccurate monster type depended on the condition tested. Figurine activation and egg hatching are using m_monnam(), which seems suspect, but I left them as is. --- src/apply.c | 3 ++- src/hack.c | 3 ++- src/mhitm.c | 17 +++++++++++------ src/mhitu.c | 8 +++++--- src/timeout.c | 4 +++- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/apply.c b/src/apply.c index e74160b9e..546d2cefd 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 apply.c $NHDT-Date: 1457397477 2016/03/08 00:37:57 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.224 $ */ +/* NetHack 3.6 apply.c $NHDT-Date: 1496619131 2017/06/04 23:32:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.232 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2060,6 +2060,7 @@ long timeout; char and_vanish[BUFSZ]; struct obj *mshelter = level.objects[mtmp->mx][mtmp->my]; + /* [m_monnam() yields accurate mon type, overriding hallucination] */ Sprintf(monnambuf, "%s", an(m_monnam(mtmp))); and_vanish[0] = '\0'; if ((mtmp->minvis && !See_invisible) diff --git a/src/hack.c b/src/hack.c index a250d86de..8683fda77 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 hack.c $NHDT-Date: 1494107206 2017/05/06 21:46:46 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.174 $ */ +/* NetHack 3.6 hack.c $NHDT-Date: 1496619131 2017/06/04 23:32:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.175 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1526,6 +1526,7 @@ domove() && !sensemon(mtmp)) stumble_onto_mimic(mtmp); else if (mtmp->mpeaceful && !Hallucination) + /* m_monnam(): "dog" or "Fido", no "invisible dog" or "it" */ pline("Pardon me, %s.", m_monnam(mtmp)); else You("move right into %s.", mon_nam(mtmp)); diff --git a/src/mhitm.c b/src/mhitm.c index 39407c3a8..38a56404b 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mhitm.c $NHDT-Date: 1470819842 2016/08/10 09:04:02 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.92 $ */ +/* NetHack 3.6 mhitm.c $NHDT-Date: 1496619132 2017/06/04 23:32:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.96 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -324,11 +324,16 @@ register struct monst *magr, *mdef; mdef->mundetected = 0; newsym(mdef->mx, mdef->my); if (canseemon(mdef) && !sensemon(mdef)) { - if (Unaware) - You("dream of %s.", (mdef->data->geno & G_UNIQ) - ? a_monnam(mdef) - : makeplural(m_monnam(mdef))); - else + if (Unaware) { + boolean justone = (mdef->data->geno & G_UNIQ) != 0L; + const char *montype; + + montype = noname_monnam(mdef, justone ? ARTICLE_THE + : ARTICLE_NONE); + if (!justone) + montype = makeplural(montype); + You("dream of %s.", montype); + } else pline("Suddenly, you notice %s.", a_monnam(mdef)); } } diff --git a/src/mhitu.c b/src/mhitu.c index 86a064f84..df5541e19 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mhitu.c $NHDT-Date: 1470819843 2016/08/10 09:04:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.144 $ */ +/* NetHack 3.6 mhitu.c $NHDT-Date: 1496619132 2017/06/04 23:32:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.146 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -486,6 +486,8 @@ register struct monst *mtmp; if (obj->otyp == EGG) obj->spe = 0; } + /* note that m_monnam() overrides hallucination, which is + what we want when message is from mtmp's perspective */ if (youmonst.data->mlet == S_EEL || u.umonnum == PM_TRAPPER) pline( @@ -515,7 +517,7 @@ register struct monst *mtmp; map_invisible(mtmp->mx, mtmp->my); if (sticky && !youseeit) pline("It gets stuck on you."); - else + else /* see note about m_monnam() above */ pline("Wait, %s! That's a %s named %s!", m_monnam(mtmp), youmonst.data->mname, plname); if (sticky) @@ -536,7 +538,7 @@ register struct monst *mtmp; && youmonst.mappearance == GOLD_PIECE) ? "tries to pick you up" : "disturbs you"); - else + else /* see note about m_monnam() above */ pline("Wait, %s! That %s is really %s named %s!", m_monnam(mtmp), mimic_obj_name(&youmonst), an(mons[u.umonnum].mname), plname); diff --git a/src/timeout.c b/src/timeout.c index e5187e93b..508fe9609 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 timeout.c $NHDT-Date: 1493510119 2017/04/29 23:55:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.70 $ */ +/* NetHack 3.6 timeout.c $NHDT-Date: 1496619133 2017/06/04 23:32:13 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.71 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -613,6 +613,8 @@ long timeout; boolean siblings = (hatchcount > 1), redraw = FALSE; if (cansee_hatchspot) { + /* [bug? m_monnam() yields accurate monster type + regardless of hallucination] */ Sprintf(monnambuf, "%s%s", siblings ? "some " : "", siblings ? makeplural(m_monnam(mon)) : an(m_monnam(mon))); /* we don't learn the egg type here because learning From 964fd0fdbd16d22421a2591d8c907a8f5cc1214a Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 7 Jun 2017 11:39:24 -0700 Subject: [PATCH 18/19] dynamic format strings vulnerable to user input This adds new utility routine strNsubst(), a more versatile version of the existing strsubst(), that can replace the Nth occurrence of a substring rather than just the first, and replaces all occurrences if N is 0. When working on vampire shape-shifting messages a few days ago I noticed that a constructed pline/sprintf format was vulnerable to the player giving the vampire a name with '%' in it and included a fix for that. This fixes two other instances of the same vulnerability: a monster with reflection triggering a floating eye's gaze and the hero using a silver weapon against a silver- hating monster. I didn't do a lot of experimenting with the failure, just assigned the name "foo%s" to the floating eye or the weapon. The resulting feedback for the relevant messages was garbled due to parameters being substituted in the wrong place. When that caused there to be too few arguments to satisfy the format, the final message included "null" for the missing one rather than triggering a crash while trying to format something arbitrary from the stack. I don't think these bugs provided sufficient user control to be vulnerable to stack manipulation that does something naughty. I found the dynamic format strings by searching for "%%". There may be others scattered around the code which don't have that as an indicator.... --- doc/fixes36.1 | 3 +++ include/extern.h | 3 ++- src/hacklib.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++-- src/mhitm.c | 8 +++++--- src/uhitm.c | 8 ++++++-- 5 files changed, 66 insertions(+), 8 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index d010c2541..4a43125d1 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -393,6 +393,9 @@ poor message when named vampire shifts shape within view: You observe a Dracula where a Dracula was. vampire shifting into fog cloud to pass under door "oozed" rather than "flowed" adult green dragons and the Chromatic Dragon were blinded by gas clouds +named floating eye (when hit by another monster with reflection) or named + silver weapon (when hero hits silver-hating monster) could disrupt + message formatting and conceivably trigger crash if name had '%' in it Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 98a258cef..58a451c67 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1496531111 2017/06/03 23:05:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.589 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1496860756 2017/06/07 18:39:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.590 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -876,6 +876,7 @@ E boolean FDECL(onlyspace, (const char *)); E char *FDECL(tabexpand, (char *)); E char *FDECL(visctrl, (CHAR_P)); E char *FDECL(strsubst, (char *, const char *, const char *)); +E int FDECL(strNsubst, (char *, const char *, const char *, int)); E const char *FDECL(ordin, (int)); E char *FDECL(sitoa, (int)); E int FDECL(sgn, (int)); diff --git a/src/hacklib.c b/src/hacklib.c index 8cb76021b..1c4fe63d6 100644 --- a/src/hacklib.c +++ b/src/hacklib.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 hacklib.c $NHDT-Date: 1472006251 2016/08/24 02:37:31 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.48 $ */ +/* NetHack 3.6 hacklib.c $NHDT-Date: 1496860756 2017/06/07 18:39:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.50 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* Copyright (c) Robert Patrick Rankin, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -33,6 +33,7 @@ char * tabexpand (char *) char * visctrl (char) char * strsubst (char *, const char *, const char *) + int strNsubst (char *,const char *,const char *,int) const char * ordin (int) char * sitoa (int) int sgn (int) @@ -45,7 +46,7 @@ boolean pmatchz (const char *, const char *) int strncmpi (const char *, const char *, int) char * strstri (const char *, const char *) - boolean fuzzymatch (const char *,const char *, + boolean fuzzymatch (const char *, const char *, const char *, boolean) void setrandom (void) time_t getnow (void) @@ -442,6 +443,7 @@ const char *orig, *replacement; char *found, buf[BUFSZ]; if (bp) { + /* [this could be replaced by strNsubst(bp, orig, replacement, 1)] */ found = strstr(bp, orig); if (found) { Strcpy(buf, found + strlen(orig)); @@ -452,6 +454,52 @@ const char *orig, *replacement; return bp; } +/* substitute the Nth occurrence of a substring within a string (in place); + if N is 0, substitute all occurrences; returns the number of subsitutions; + maximum output length is BUFSZ (BUFSZ-1 chars + terminating '\0') */ +int +strNsubst(inoutbuf, orig, replacement, n) +char *inoutbuf; /* current string, and result buffer */ +const char *orig, /* old substring; if "" then insert in front of Nth char */ + *replacement; /* new substring; if "" then delete old substring */ +int n; /* which occurrence to replace; 0 => all */ +{ + char *bp, *op, workbuf[BUFSZ]; + const char *rp; + unsigned len = (unsigned) strlen(orig); + int ocount = 0, /* number of times 'orig' has been matched */ + rcount = 0; /* number of subsitutions made */ + + for (bp = inoutbuf, op = workbuf; *bp && op < &workbuf[BUFSZ - 1]; ) { + if ((!len || !strncmp(bp, orig, len)) && (++ocount == n || n == 0)) { + /* Nth match found */ + for (rp = replacement; *rp && op < &workbuf[BUFSZ - 1]; ) + *op++ = *rp++; + ++rcount; + if (len) { + bp += len; /* skip 'orig' */ + continue; + } + } + /* no match (or len==0) so retain current character */ + *op++ = *bp++; + } + if (!len && n == ocount + 1) { + /* special case: orig=="" (!len) and n==strlen(inoutbuf)+1, + insert in front of terminator (in other words, append); + [when orig=="", ocount will have been incremented once for + each input char] */ + for (rp = replacement; *rp && op < &workbuf[BUFSZ - 1]; ) + *op++ = *rp++; + ++rcount; + } + if (rcount) { + *op = '\0'; + Strcpy(inoutbuf, workbuf); + } + return rcount; +} + /* return the ordinal suffix of a number */ const char * ordin(n) diff --git a/src/mhitm.c b/src/mhitm.c index 38a56404b..00ac76cb1 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mhitm.c $NHDT-Date: 1496619132 2017/06/04 23:32:12 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.96 $ */ +/* NetHack 3.6 mhitm.c $NHDT-Date: 1496860757 2017/06/07 18:39:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.97 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1558,8 +1558,10 @@ int mdead; tmp = 127; if (magr->mcansee && haseyes(madat) && mdef->mcansee && (perceives(madat) || !mdef->minvis)) { - Sprintf(buf, "%s gaze is reflected by %%s %%s.", - s_suffix(Monnam(mdef))); + /* construct format string; guard against '%' in Monnam */ + Strcpy(buf, s_suffix(Monnam(mdef))); + (void) strNsubst(buf, "%", "%%", 0); + Strcat(buf, " gaze is reflected by %s %s."); if (mon_reflects(magr, canseemon(magr) ? buf : (char *) 0)) return (mdead | mhit); diff --git a/src/uhitm.c b/src/uhitm.c index a4a31cde0..b11cb0fd8 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 uhitm.c $NHDT-Date: 1470819843 2016/08/10 09:04:03 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.164 $ */ +/* NetHack 3.6 uhitm.c $NHDT-Date: 1496860757 2017/06/07 18:39:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.166 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1123,9 +1123,13 @@ int thrown; /* HMON_xxx (0 => hand-to-hand, other => ranged) */ else if (barehand_silver_rings == 2) fmt = "Your silver rings sear %s!"; else if (silverobj && saved_oname[0]) { - Sprintf(silverobjbuf, "Your %s%s %s %%s!", + /* guard constructed format string against '%' in + saved_oname[] from xname(via cxname()) */ + Sprintf(silverobjbuf, "Your %s%s %s", strstri(saved_oname, "silver") ? "" : "silver ", saved_oname, vtense(saved_oname, "sear")); + (void) strNsubst(silverobjbuf, "%", "%%", 0); + Strcat(silverobjbuf, " %s!"); fmt = silverobjbuf; } else fmt = "The silver sears %s!"; From 743d3a1eb59d7fffe0a1bb3a9afd2576d411d9bf Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 8 Jun 2017 15:05:24 -0700 Subject: [PATCH 19/19] yet another pass at 'A' bugs I think this finally quashes the "cursed without otmp" issue. Various ways of destroying wielded weapon used setnotworn() rather than unwield(), so the previous change to have unwield() clear the pending W_WEP bit from takeoff.mask wasn't sufficient to prevent 'A' moving on from another item (blindfold--it's the only thing processed before primary weapon) to weapon which wasn't there any more. Also, if weapon was already set in takeoff.what to be processed on the next move, clearing W_WEP from takeoff.mask wasn't sufficient either. Move the previous unwield() 'fix' to setworn() and setnotworn() and extend it to include cancel_don() if the item being replaced or removed is in progress or scheduled for next. (Most of the time, remove_worn_item() has already done that before setworn() or setnotworn() is called.) --- doc/fixes36.1 | 5 ++++- include/extern.h | 3 ++- src/do_wear.c | 41 ++++++++++++++++++++++++++++++++--------- src/wield.c | 8 +------- src/worn.c | 9 ++++++++- 5 files changed, 47 insertions(+), 19 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index 4a43125d1..bf5df58cb 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -379,7 +379,9 @@ levitation vs encumbrance message sequencing issues: putting on boots of and float-up messages, taking off such boots didn't report increase of encumbrance until player took another action removing a blindfold with 'A' took two turns, with 'R' (and 'T') only one, - and could result in a panic if the blindfold was stolen during removal + and could result in a crash if the blindfold was stolen during removal +removing a blindfold and wielded weapon with 'A' could result in crash if the + weapon was destroyed by various methods cmdassist help for movement prefix followed by invalid direction was strange when the direction was up, down, or self disallowed for that prefix poor message when shape-shifted vampire reverts to vampire if it has a name: @@ -396,6 +398,7 @@ adult green dragons and the Chromatic Dragon were blinded by gas clouds named floating eye (when hit by another monster with reflection) or named silver weapon (when hero hits silver-hating monster) could disrupt message formatting and conceivably trigger crash if name had '%' in it +crashes for 'A' above were downgraded to impossible "cursed without otmp" Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 58a451c67..72df6d58d 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1496860756 2017/06/07 18:39:16 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.590 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1496959470 2017/06/08 22:04:30 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.591 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -436,6 +436,7 @@ E void FDECL(off_msg, (struct obj *)); E void FDECL(set_wear, (struct obj *)); E boolean FDECL(donning, (struct obj *)); E boolean FDECL(doffing, (struct obj *)); +E void FDECL(cancel_doff, (struct obj *, long)); E void NDECL(cancel_don); E int FDECL(stop_donning, (struct obj *)); E int NDECL(Armor_off); diff --git a/src/do_wear.c b/src/do_wear.c index 2d16d34d1..71599fd7a 100644 --- a/src/do_wear.c +++ b/src/do_wear.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 do_wear.c $NHDT-Date: 1496614914 2017/06/04 22:21:54 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.97 $ */ +/* NetHack 3.6 do_wear.c $NHDT-Date: 1496959478 2017/06/08 22:04:38 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.98 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1207,7 +1207,7 @@ struct obj *otmp; { boolean result = FALSE; - /* 'W' and 'T' set afternmv, 'A' sets context.takeoff.what */ + /* 'W' (or 'P' used for armor) sets afternmv */ if (doffing(otmp)) result = TRUE; else if (otmp == uarm) @@ -1237,7 +1237,7 @@ struct obj *otmp; long what = context.takeoff.what; boolean result = FALSE; - /* 'T' (also 'W') sets afternmv, 'A' sets context.takeoff.what */ + /* 'T' (or 'R' used for armor) sets afternmv, 'A' sets takeoff.what */ if (otmp == uarm) result = (afternmv == Armor_off || what == WORN_ARMOR); else if (otmp == uarmu) @@ -1252,8 +1252,7 @@ struct obj *otmp; result = (afternmv == Gloves_off || what == WORN_GLOVES); else if (otmp == uarms) result = (afternmv == Shield_off || what == WORN_SHIELD); - /* these 1-turn items don't need 'afternmv' checks - [and may not actually need 'what' checks] */ + /* these 1-turn items don't need 'afternmv' checks */ else if (otmp == uamul) result = (what == WORN_AMUL); else if (otmp == uleft) @@ -1272,6 +1271,29 @@ struct obj *otmp; return result; } +/* despite their names, cancel_don() and cancel_doff() both apply to both + donning and doffing... */ +void +cancel_doff(obj, slotmask) +struct obj *obj; +long slotmask; +{ + /* Called by setworn() for old item in specified slot or by setnotworn() + * for specified item. We don't want to call cancel_don() if we got + * here via _off() -> setworn((struct obj *)0) -> cancel_doff() + * because that would stop the 'A' command from continuing with next + * selected item. So do_takeoff() sets a flag in takeoff.mask for us. + * [For taking off an individual item with 'T'/'R'/'w-', it doesn't + * matter whether cancel_don() gets called here--the item has already + * been removed by now.] + */ + if (!(context.takeoff.mask & I_SPECIAL) && donning(obj)) + cancel_don(); /* applies to doffing too */ + context.takeoff.mask &= ~slotmask; +} + +/* despite their names, cancel_don() and cancel_doff() both apply to both + donning and doffing... */ void cancel_don() { @@ -1308,7 +1330,7 @@ struct obj *stolenobj; /* no message if stolenobj is already being doffing */ /* donning() returns True when doffing too; doffing() is more specific */ putting_on = !doffing(otmp); - /* cancel_don() looks at afternmv; it also serves as cancel_doff() */ + /* cancel_don() looks at afternmv; it can also cancel doffing */ cancel_don(); /* don't want _on() or _off() being called by unmul() since the on or off action isn't completing */ @@ -2302,6 +2324,7 @@ do_takeoff() struct obj *otmp = (struct obj *) 0; struct takeoff_info *doff = &context.takeoff; + context.takeoff.mask |= I_SPECIAL; /* set flag for cancel_doff() */ if (doff->what == W_WEP) { if (!cursed(uwep)) { setuwep((struct obj *) 0); @@ -2361,6 +2384,7 @@ do_takeoff() } else { impossible("do_takeoff: taking off %lx", doff->what); } + context.takeoff.mask &= ~I_SPECIAL; /* clear cancel_doff() flag */ return otmp; } @@ -2378,10 +2402,9 @@ take_off(VOID_ARGS) if (doff->delay > 0) { doff->delay--; return 1; /* still busy */ - } else { - if ((otmp = do_takeoff())) - off_msg(otmp); } + if ((otmp = do_takeoff()) != 0) + off_msg(otmp); doff->mask &= ~doff->what; doff->what = 0L; } diff --git a/src/wield.c b/src/wield.c index d2f51a2ee..bd176a812 100644 --- a/src/wield.c +++ b/src/wield.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wield.c $NHDT-Date: 1496614915 2017/06/04 22:21:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.53 $ */ +/* NetHack 3.6 wield.c $NHDT-Date: 1496959480 2017/06/08 22:04:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.54 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -676,10 +676,6 @@ uwepgone() pline("%s shining.", Tobjnam(uwep, "stop")); } setworn((struct obj *) 0, W_WEP); - /* update takeoff mask in case uwep has just gone away while - its removal via 'A' was pending, similar to what the various - Xyzzy_off(do_wear.c) routines do for armor and accessories */ - context.takeoff.mask &= ~W_WEP; unweapon = TRUE; update_inventory(); } @@ -690,7 +686,6 @@ uswapwepgone() { if (uswapwep) { setworn((struct obj *) 0, W_SWAPWEP); - context.takeoff.mask &= ~W_SWAPWEP; update_inventory(); } } @@ -700,7 +695,6 @@ uqwepgone() { if (uquiver) { setworn((struct obj *) 0, W_QUIVER); - context.takeoff.mask &= ~W_QUIVER; update_inventory(); } } diff --git a/src/worn.c b/src/worn.c index a99d1336d..abc1d4c3e 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 worn.c $NHDT-Date: 1493510127 2017/04/29 23:55:27 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.48 $ */ +/* NetHack 3.6 worn.c $NHDT-Date: 1496959481 2017/06/08 22:04:41 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.49 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ @@ -77,6 +77,9 @@ long mask; if (oobj->oartifact) set_artifact_intrinsic(oobj, 0, mask); } + /* in case wearing or removal is in progress or removal + is pending (via 'A' command for multiple items) */ + cancel_doff(oobj, wp->w_mask); } *(wp->w_obj) = obj; if (obj) { @@ -119,6 +122,10 @@ register struct obj *obj; u.twoweap = 0; for (wp = worn; wp->w_mask; wp++) if (obj == *(wp->w_obj)) { + /* in case wearing or removal is in progress or removal + is pending (via 'A' command for multiple items) */ + cancel_doff(obj, wp->w_mask); + *(wp->w_obj) = 0; p = objects[obj->otyp].oc_oprop; u.uprops[p].extrinsic = u.uprops[p].extrinsic & ~wp->w_mask;