From 5c6245c312f1f6c67aa7552228050bc98e653c69 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 18 May 2018 16:57:44 -0700 Subject: [PATCH 01/23] commit test for renamed branch Locally I've committed to NetHack-3.6.0 and haven't yet pulled from upstream to get the branch rename. I expect this commit to be rejected but it could conceivably go through to the new name. --- doc/fixes36.2 | 6 +++ sys/unix/Makefile.src | 32 ++++++++++------ sys/unix/Makefile.utl | 36 ++++++++++++----- sys/unix/unixmain.c.diff | 83 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 sys/unix/unixmain.c.diff diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4547d4273..518b76483 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -40,6 +40,12 @@ windows-tty: Use nhraykey by default if the players keyboard layout is windows-tty: We now support changing altkeyhandler in game windows: Added ntassert() mechanism for Windows based port use tty: significant optimizations for performance and per field rendering +unix: Makefile.src and Makefile.utl inadvertently relied on a 'gnu make' + extension when using $(VERBOSEMAKE) to reduce build-time feedback; + replace with $(QUIETCC) which operates the same but defaults to + verbose so doesn't use '$<' for multi-prerequisite targets unless + specifically requested; use 'make QUIETCC=1 ' to get the + 3.6.1 behavior back General New Features diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 47af438b4..98c80a2fd 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -1,5 +1,5 @@ # NetHack Makefile. -# NetHack 3.6 Makefile.src $NHDT-Date: 1524689449 2018/04/25 20:50:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.54 $ +# NetHack 3.6 Makefile.src $NHDT-Date: 1526687360 2018/05/18 23:49:20 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.55 $ # Copyright (c) 2018 by Pasi Kallinen # NetHack may be freely redistributed. See license for details. @@ -343,7 +343,13 @@ AWK = nawk # will proceed without it GITINFO=1 -#VERBOSEMAKE = 1 +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 # ---------------------------------------- # @@ -353,7 +359,7 @@ GITINFO=1 # {unixconf.h, pcconf.h, tosconf.h}, and possibly system.h # Verbosity definitions, begin -# Set VERBOSEMAKE=1 to output more stuff. +# Set QUIETCC=1 above to output less feedback while building. # CC and CXX obey verbosity, LD and LINK don't. # AT is @ when not verbose, empty otherwise ACTUAL_CC := $(CC) @@ -361,23 +367,25 @@ ACTUAL_CXX := $(CXX) ACTUAL_LD := $(LD) ACTUAL_LINK := $(LINK) -CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V0 = $(ACTUAL_CC) CC_V = $(CC_V0) -CC_V1 = $(ACTUAL_CC) -CC = $(CC_V$(VERBOSEMAKE)) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) -CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V0 = $(ACTUAL_CXX) CXX_V = $(CXX_V0) -CXX_V1 = $(ACTUAL_CXX) -CXX = $(CXX_V$(VERBOSEMAKE)) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) LD = $(ACTUAL_LD) LINK = $(ACTUAL_LINK) -AT_V0 := @ +AT_V0 := AT_V := $(AT_V0) -AT_V1 := -AT = $(AT_V$(VERBOSEMAKE)) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) # Verbosity, end MAKEDEFS = ../util/makedefs diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 9119cef82..78deeb23e 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -1,5 +1,5 @@ # Makefile for NetHack's utility programs. -# NetHack 3.6 Makefile.utl $NHDT-Date: 1524689449 2018/04/25 20:50:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.35 $ +# NetHack 3.6 Makefile.utl $NHDT-Date: 1526687364 2018/05/18 23:49:24 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.36 $ # Copyright (c) 2018 by Robert Patrick Rankin # NetHack may be freely redistributed. See license for details. @@ -127,34 +127,50 @@ LEXYYC = lex.yy.c # LEXYYC = lexyy.c +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 + +# TODO? the link/load commands below are handled differently from the ones +# in Makefile.src; these use '$(CC) $(LFLAGS)' and ought to be changed to use +# $(LD) or $(LINK) as appropriate [quiet mode echoes a misleading $< value] # ---------------------------------------- # # Nothing below this line should have to be changed. # Verbosity definitions, begin +# Set QUIETCC=1 above to output less feedback while building. +# CC and CXX obey verbosity, LD and LINK don't. +# AT is @ when not verbose, empty otherwise ACTUAL_CC := $(CC) ACTUAL_CXX := $(CXX) ACTUAL_LD := $(LD) ACTUAL_LINK := $(LINK) -CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V0 = $(ACTUAL_CC) CC_V = $(CC_V0) -CC_V1 = $(ACTUAL_CC) -CC = $(CC_V$(VERBOSEMAKE)) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) -CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V0 = $(ACTUAL_CXX) CXX_V = $(CXX_V0) -CXX_V1 = $(ACTUAL_CXX) -CXX = $(CXX_V$(VERBOSEMAKE)) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) LD = $(ACTUAL_LD) LINK = $(ACTUAL_LINK) -AT_V0 := @ +AT_V0 := AT_V := $(AT_V0) -AT_V1 := -AT = V$(AT_$(VERBOSEMAKE)) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) # Verbosity, end # timestamps for primary header files, matching src/Makefile diff --git a/sys/unix/unixmain.c.diff b/sys/unix/unixmain.c.diff new file mode 100644 index 000000000..caf6d7cb2 --- /dev/null +++ b/sys/unix/unixmain.c.diff @@ -0,0 +1,83 @@ +diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c +index c1069144..1d51de1b 100644 +--- a/sys/unix/unixmain.c ++++ b/sys/unix/unixmain.c +@@ -44,6 +44,39 @@ static void NDECL(wd_message); + static boolean wiz_error_flag = FALSE; + static struct passwd *NDECL(get_unix_pw); + ++#if defined(__APPLE__) /* a much more convoluted version of this ++ * code used to be inline within main() */ ++static void FDECL(osx_finder, (const char *)); ++ ++/* special hack to change working directory to a resource fork when running ++ from 'Finder' on MacOSX (recognized via "/" as current dir) --sam */ ++static void ++osx_finder(arg0) ++const char *arg0; ++{ ++ /* #define MAC_PATH_VALUE ".app/Contents/MacOS/" */ ++ char mac_cwd[1024], *mac_path, *p; ++ ++ /* ++ * If current working directory is "/", switch to same directory ++ * as nethack. [This overrides !CHDIR config.] ++ */ ++ getcwd(mac_cwd, 1024); ++ if (*arg0 == '/' && !strcmp(mac_cwd, "/")) { ++ /* copy executable path/name into modifiable buffer ++ [we know there's a path since the value starts with '/'] */ ++ mac_path = dupstr(arg0); ++ /* strip off the name portion, leaving path */ ++ p = rindex(mac_path, '/'); ++ *(p + 1) = '\0'; ++ /* change working directory */ ++ chdir(mac_path); ++ /* done */ ++ free((genericptr_t) mac_path); ++ } ++} ++#endif /* __APPLE__ */ ++ + int + main(argc, argv) + int argc; +@@ -57,37 +90,8 @@ char *argv[]; + boolean resuming = FALSE; /* assume new game */ + + sys_early_init(); +- + #if defined(__APPLE__) +- { +-/* special hack to change working directory to a resource fork when +- running from finder --sam */ +-#define MAC_PATH_VALUE ".app/Contents/MacOS/" +- char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; +- int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0; +- getcwd(mac_cwd, 1024); +- if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { +- if ((mac_exe = strrchr(mac_exe, '/'))) +- mac_exe++; +- else +- mac_exe = argv[0]; +- mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); +- if (mac_tmp_len <= arg0_len) { +- mac_tmp = malloc(mac_tmp_len + 1); +- sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); +- if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { +- mac_lhs_len = +- (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; +- if (mac_lhs_len > mac_tmp_len - 1) +- mac_tmp = realloc(mac_tmp, mac_lhs_len); +- strncpy(mac_tmp, argv[0], mac_lhs_len); +- mac_tmp[mac_lhs_len] = '\0'; +- chdir(mac_tmp); +- } +- free(mac_tmp); +- } +- } +- } ++ osx_finder(argv[0]); + #endif + + hname = argv[0]; From 2fb9d6c94e371446e77fecc5d9201885844807f5 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 18 May 2018 21:42:41 -0400 Subject: [PATCH 02/23] save Pat's change --- sys/unix/unixmain.c.diff | 83 ---------------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 sys/unix/unixmain.c.diff diff --git a/sys/unix/unixmain.c.diff b/sys/unix/unixmain.c.diff deleted file mode 100644 index caf6d7cb2..000000000 --- a/sys/unix/unixmain.c.diff +++ /dev/null @@ -1,83 +0,0 @@ -diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c -index c1069144..1d51de1b 100644 ---- a/sys/unix/unixmain.c -+++ b/sys/unix/unixmain.c -@@ -44,6 +44,39 @@ static void NDECL(wd_message); - static boolean wiz_error_flag = FALSE; - static struct passwd *NDECL(get_unix_pw); - -+#if defined(__APPLE__) /* a much more convoluted version of this -+ * code used to be inline within main() */ -+static void FDECL(osx_finder, (const char *)); -+ -+/* special hack to change working directory to a resource fork when running -+ from 'Finder' on MacOSX (recognized via "/" as current dir) --sam */ -+static void -+osx_finder(arg0) -+const char *arg0; -+{ -+ /* #define MAC_PATH_VALUE ".app/Contents/MacOS/" */ -+ char mac_cwd[1024], *mac_path, *p; -+ -+ /* -+ * If current working directory is "/", switch to same directory -+ * as nethack. [This overrides !CHDIR config.] -+ */ -+ getcwd(mac_cwd, 1024); -+ if (*arg0 == '/' && !strcmp(mac_cwd, "/")) { -+ /* copy executable path/name into modifiable buffer -+ [we know there's a path since the value starts with '/'] */ -+ mac_path = dupstr(arg0); -+ /* strip off the name portion, leaving path */ -+ p = rindex(mac_path, '/'); -+ *(p + 1) = '\0'; -+ /* change working directory */ -+ chdir(mac_path); -+ /* done */ -+ free((genericptr_t) mac_path); -+ } -+} -+#endif /* __APPLE__ */ -+ - int - main(argc, argv) - int argc; -@@ -57,37 +90,8 @@ char *argv[]; - boolean resuming = FALSE; /* assume new game */ - - sys_early_init(); -- - #if defined(__APPLE__) -- { --/* special hack to change working directory to a resource fork when -- running from finder --sam */ --#define MAC_PATH_VALUE ".app/Contents/MacOS/" -- char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; -- int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0; -- getcwd(mac_cwd, 1024); -- if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { -- if ((mac_exe = strrchr(mac_exe, '/'))) -- mac_exe++; -- else -- mac_exe = argv[0]; -- mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); -- if (mac_tmp_len <= arg0_len) { -- mac_tmp = malloc(mac_tmp_len + 1); -- sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); -- if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { -- mac_lhs_len = -- (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; -- if (mac_lhs_len > mac_tmp_len - 1) -- mac_tmp = realloc(mac_tmp, mac_lhs_len); -- strncpy(mac_tmp, argv[0], mac_lhs_len); -- mac_tmp[mac_lhs_len] = '\0'; -- chdir(mac_tmp); -- } -- free(mac_tmp); -- } -- } -- } -+ osx_finder(argv[0]); - #endif - - hname = argv[0]; From 546321d05503594b97fcba3b5c1cabb0c3c8f0fc Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 18 May 2018 16:57:44 -0700 Subject: [PATCH 03/23] Unix Makefile changes --- doc/fixes36.2 | 6 +++ sys/unix/Makefile.src | 32 ++++++++++------ sys/unix/Makefile.utl | 36 ++++++++++++----- sys/unix/unixmain.c.diff | 83 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 22 deletions(-) create mode 100644 sys/unix/unixmain.c.diff diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 4547d4273..518b76483 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -40,6 +40,12 @@ windows-tty: Use nhraykey by default if the players keyboard layout is windows-tty: We now support changing altkeyhandler in game windows: Added ntassert() mechanism for Windows based port use tty: significant optimizations for performance and per field rendering +unix: Makefile.src and Makefile.utl inadvertently relied on a 'gnu make' + extension when using $(VERBOSEMAKE) to reduce build-time feedback; + replace with $(QUIETCC) which operates the same but defaults to + verbose so doesn't use '$<' for multi-prerequisite targets unless + specifically requested; use 'make QUIETCC=1 ' to get the + 3.6.1 behavior back General New Features diff --git a/sys/unix/Makefile.src b/sys/unix/Makefile.src index 47af438b4..98c80a2fd 100644 --- a/sys/unix/Makefile.src +++ b/sys/unix/Makefile.src @@ -1,5 +1,5 @@ # NetHack Makefile. -# NetHack 3.6 Makefile.src $NHDT-Date: 1524689449 2018/04/25 20:50:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.54 $ +# NetHack 3.6 Makefile.src $NHDT-Date: 1526687360 2018/05/18 23:49:20 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.55 $ # Copyright (c) 2018 by Pasi Kallinen # NetHack may be freely redistributed. See license for details. @@ -343,7 +343,13 @@ AWK = nawk # will proceed without it GITINFO=1 -#VERBOSEMAKE = 1 +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 # ---------------------------------------- # @@ -353,7 +359,7 @@ GITINFO=1 # {unixconf.h, pcconf.h, tosconf.h}, and possibly system.h # Verbosity definitions, begin -# Set VERBOSEMAKE=1 to output more stuff. +# Set QUIETCC=1 above to output less feedback while building. # CC and CXX obey verbosity, LD and LINK don't. # AT is @ when not verbose, empty otherwise ACTUAL_CC := $(CC) @@ -361,23 +367,25 @@ ACTUAL_CXX := $(CXX) ACTUAL_LD := $(LD) ACTUAL_LINK := $(LINK) -CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V0 = $(ACTUAL_CC) CC_V = $(CC_V0) -CC_V1 = $(ACTUAL_CC) -CC = $(CC_V$(VERBOSEMAKE)) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) -CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V0 = $(ACTUAL_CXX) CXX_V = $(CXX_V0) -CXX_V1 = $(ACTUAL_CXX) -CXX = $(CXX_V$(VERBOSEMAKE)) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) LD = $(ACTUAL_LD) LINK = $(ACTUAL_LINK) -AT_V0 := @ +AT_V0 := AT_V := $(AT_V0) -AT_V1 := -AT = $(AT_V$(VERBOSEMAKE)) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) # Verbosity, end MAKEDEFS = ../util/makedefs diff --git a/sys/unix/Makefile.utl b/sys/unix/Makefile.utl index 9119cef82..78deeb23e 100644 --- a/sys/unix/Makefile.utl +++ b/sys/unix/Makefile.utl @@ -1,5 +1,5 @@ # Makefile for NetHack's utility programs. -# NetHack 3.6 Makefile.utl $NHDT-Date: 1524689449 2018/04/25 20:50:49 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.35 $ +# NetHack 3.6 Makefile.utl $NHDT-Date: 1526687364 2018/05/18 23:49:24 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.36 $ # Copyright (c) 2018 by Robert Patrick Rankin # NetHack may be freely redistributed. See license for details. @@ -127,34 +127,50 @@ LEXYYC = lex.yy.c # LEXYYC = lexyy.c +# if you change this to 1, feedback while building will omit -Dthis -Wthat +# -Isomewhere so that each file being compiled is listed on one short line; +# it requires support for '$<' in rules with more than one prerequisite +# (rather than just in suffix default rule), such as is implemented by +# gnu make and others which have picked up its extensions; +# allowed values are 0, 1, and empty (which behaves like 0) +QUIETCC=0 + +# TODO? the link/load commands below are handled differently from the ones +# in Makefile.src; these use '$(CC) $(LFLAGS)' and ought to be changed to use +# $(LD) or $(LINK) as appropriate [quiet mode echoes a misleading $< value] # ---------------------------------------- # # Nothing below this line should have to be changed. # Verbosity definitions, begin +# Set QUIETCC=1 above to output less feedback while building. +# CC and CXX obey verbosity, LD and LINK don't. +# AT is @ when not verbose, empty otherwise ACTUAL_CC := $(CC) ACTUAL_CXX := $(CXX) ACTUAL_LD := $(LD) ACTUAL_LINK := $(LINK) -CC_V0 = @echo "[CC] $<"; $(ACTUAL_CC) +CC_V0 = $(ACTUAL_CC) CC_V = $(CC_V0) -CC_V1 = $(ACTUAL_CC) -CC = $(CC_V$(VERBOSEMAKE)) +CC_V1 = @echo "[CC] $<"; $(ACTUAL_CC) +CC = $(CC_V$(QUIETCC)) -CXX_V0 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX_V0 = $(ACTUAL_CXX) CXX_V = $(CXX_V0) -CXX_V1 = $(ACTUAL_CXX) -CXX = $(CXX_V$(VERBOSEMAKE)) +CXX_V1 = @echo "[CXX] $<"; $(ACTUAL_CXX) +CXX = $(CXX_V$(QUIETCC)) +# LD and LINK might be based on invoking CC and may not be able to substitute +# for QUIETCC, so feedback from them is handled differently (via $AT) LD = $(ACTUAL_LD) LINK = $(ACTUAL_LINK) -AT_V0 := @ +AT_V0 := AT_V := $(AT_V0) -AT_V1 := -AT = V$(AT_$(VERBOSEMAKE)) +AT_V1 := @ +AT = $(AT_V$(QUIETCC)) # Verbosity, end # timestamps for primary header files, matching src/Makefile diff --git a/sys/unix/unixmain.c.diff b/sys/unix/unixmain.c.diff new file mode 100644 index 000000000..caf6d7cb2 --- /dev/null +++ b/sys/unix/unixmain.c.diff @@ -0,0 +1,83 @@ +diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c +index c1069144..1d51de1b 100644 +--- a/sys/unix/unixmain.c ++++ b/sys/unix/unixmain.c +@@ -44,6 +44,39 @@ static void NDECL(wd_message); + static boolean wiz_error_flag = FALSE; + static struct passwd *NDECL(get_unix_pw); + ++#if defined(__APPLE__) /* a much more convoluted version of this ++ * code used to be inline within main() */ ++static void FDECL(osx_finder, (const char *)); ++ ++/* special hack to change working directory to a resource fork when running ++ from 'Finder' on MacOSX (recognized via "/" as current dir) --sam */ ++static void ++osx_finder(arg0) ++const char *arg0; ++{ ++ /* #define MAC_PATH_VALUE ".app/Contents/MacOS/" */ ++ char mac_cwd[1024], *mac_path, *p; ++ ++ /* ++ * If current working directory is "/", switch to same directory ++ * as nethack. [This overrides !CHDIR config.] ++ */ ++ getcwd(mac_cwd, 1024); ++ if (*arg0 == '/' && !strcmp(mac_cwd, "/")) { ++ /* copy executable path/name into modifiable buffer ++ [we know there's a path since the value starts with '/'] */ ++ mac_path = dupstr(arg0); ++ /* strip off the name portion, leaving path */ ++ p = rindex(mac_path, '/'); ++ *(p + 1) = '\0'; ++ /* change working directory */ ++ chdir(mac_path); ++ /* done */ ++ free((genericptr_t) mac_path); ++ } ++} ++#endif /* __APPLE__ */ ++ + int + main(argc, argv) + int argc; +@@ -57,37 +90,8 @@ char *argv[]; + boolean resuming = FALSE; /* assume new game */ + + sys_early_init(); +- + #if defined(__APPLE__) +- { +-/* special hack to change working directory to a resource fork when +- running from finder --sam */ +-#define MAC_PATH_VALUE ".app/Contents/MacOS/" +- char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; +- int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0; +- getcwd(mac_cwd, 1024); +- if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { +- if ((mac_exe = strrchr(mac_exe, '/'))) +- mac_exe++; +- else +- mac_exe = argv[0]; +- mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); +- if (mac_tmp_len <= arg0_len) { +- mac_tmp = malloc(mac_tmp_len + 1); +- sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); +- if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { +- mac_lhs_len = +- (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; +- if (mac_lhs_len > mac_tmp_len - 1) +- mac_tmp = realloc(mac_tmp, mac_lhs_len); +- strncpy(mac_tmp, argv[0], mac_lhs_len); +- mac_tmp[mac_lhs_len] = '\0'; +- chdir(mac_tmp); +- } +- free(mac_tmp); +- } +- } +- } ++ osx_finder(argv[0]); + #endif + + hname = argv[0]; From 3b77543bcc16c7590681cfe7fb0afc23dc0fd496 Mon Sep 17 00:00:00 2001 From: nhmall Date: Fri, 18 May 2018 21:42:41 -0400 Subject: [PATCH 04/23] preserve change from old branch --- sys/unix/unixmain.c.diff | 83 ---------------------------------------- 1 file changed, 83 deletions(-) delete mode 100644 sys/unix/unixmain.c.diff diff --git a/sys/unix/unixmain.c.diff b/sys/unix/unixmain.c.diff deleted file mode 100644 index caf6d7cb2..000000000 --- a/sys/unix/unixmain.c.diff +++ /dev/null @@ -1,83 +0,0 @@ -diff --git a/sys/unix/unixmain.c b/sys/unix/unixmain.c -index c1069144..1d51de1b 100644 ---- a/sys/unix/unixmain.c -+++ b/sys/unix/unixmain.c -@@ -44,6 +44,39 @@ static void NDECL(wd_message); - static boolean wiz_error_flag = FALSE; - static struct passwd *NDECL(get_unix_pw); - -+#if defined(__APPLE__) /* a much more convoluted version of this -+ * code used to be inline within main() */ -+static void FDECL(osx_finder, (const char *)); -+ -+/* special hack to change working directory to a resource fork when running -+ from 'Finder' on MacOSX (recognized via "/" as current dir) --sam */ -+static void -+osx_finder(arg0) -+const char *arg0; -+{ -+ /* #define MAC_PATH_VALUE ".app/Contents/MacOS/" */ -+ char mac_cwd[1024], *mac_path, *p; -+ -+ /* -+ * If current working directory is "/", switch to same directory -+ * as nethack. [This overrides !CHDIR config.] -+ */ -+ getcwd(mac_cwd, 1024); -+ if (*arg0 == '/' && !strcmp(mac_cwd, "/")) { -+ /* copy executable path/name into modifiable buffer -+ [we know there's a path since the value starts with '/'] */ -+ mac_path = dupstr(arg0); -+ /* strip off the name portion, leaving path */ -+ p = rindex(mac_path, '/'); -+ *(p + 1) = '\0'; -+ /* change working directory */ -+ chdir(mac_path); -+ /* done */ -+ free((genericptr_t) mac_path); -+ } -+} -+#endif /* __APPLE__ */ -+ - int - main(argc, argv) - int argc; -@@ -57,37 +90,8 @@ char *argv[]; - boolean resuming = FALSE; /* assume new game */ - - sys_early_init(); -- - #if defined(__APPLE__) -- { --/* special hack to change working directory to a resource fork when -- running from finder --sam */ --#define MAC_PATH_VALUE ".app/Contents/MacOS/" -- char mac_cwd[1024], *mac_exe = argv[0], *mac_tmp; -- int arg0_len = strlen(mac_exe), mac_tmp_len, mac_lhs_len = 0; -- getcwd(mac_cwd, 1024); -- if (mac_exe[0] == '/' && !strcmp(mac_cwd, "/")) { -- if ((mac_exe = strrchr(mac_exe, '/'))) -- mac_exe++; -- else -- mac_exe = argv[0]; -- mac_tmp_len = (strlen(mac_exe) * 2) + strlen(MAC_PATH_VALUE); -- if (mac_tmp_len <= arg0_len) { -- mac_tmp = malloc(mac_tmp_len + 1); -- sprintf(mac_tmp, "%s%s%s", mac_exe, MAC_PATH_VALUE, mac_exe); -- if (!strcmp(argv[0] + (arg0_len - mac_tmp_len), mac_tmp)) { -- mac_lhs_len = -- (arg0_len - mac_tmp_len) + strlen(mac_exe) + 5; -- if (mac_lhs_len > mac_tmp_len - 1) -- mac_tmp = realloc(mac_tmp, mac_lhs_len); -- strncpy(mac_tmp, argv[0], mac_lhs_len); -- mac_tmp[mac_lhs_len] = '\0'; -- chdir(mac_tmp); -- } -- free(mac_tmp); -- } -- } -- } -+ osx_finder(argv[0]); - #endif - - hname = argv[0]; From bed79b83913c91911c8d7b0d015ebd06edf96dcf Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 18 May 2018 22:56:21 -0700 Subject: [PATCH 05/23] more hilite_status threshold number parsing In addition to leading '-' for negative values, accept explicit '+' for positive values as a no-op. --- doc/fixes36.2 | 3 ++- src/botl.c | 35 ++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 518b76483..db900eae1 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -12,7 +12,8 @@ list MSGTYPE values shows empty strings as reported in H7140 Killing Vlad while he was in bat/fog cloud/wolf form gave poorly worded feedback when he reverted to vampire form spaces in hilite_status option title text field not working -numeric hilite_status values didn't allow negative numbers (needed for AC) +numeric hilite_status values didn't allow negative numbers (needed for AC); + change them to accept leading '-', also accept unary '+' as a no-op Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/botl.c b/src/botl.c index 2bd11768c..60c13815a 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526597284 2018/05/17 22:48:04 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1526709371 2018/05/19 05:56:11 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.94 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1515,7 +1515,7 @@ boolean from_configfile; return TRUE; } -/* is str in the format of "[<>]?-?[0-9]+%?" regex */ +/* is str in the format of "[<>]?[-+]?[0-9]+%?" regex */ STATIC_OVL boolean is_ltgt_percentnumber(str) const char *str; @@ -1524,10 +1524,10 @@ const char *str; if (*s == '<' || *s == '>') s++; - if (*s == '-') + if (*s == '-' || *s == '+') s++; - /* note: this doesn't match the regexp shown above since it doesn't - require at least one digit; but it's adequate for how it gets used */ + if (!digit(*s)) + return FALSE; while (digit(*s)) s++; if (*s == '%') @@ -1699,10 +1699,10 @@ boolean from_configfile; enum statusfields fld = BL_FLUSH; struct hilite_s hilite; char tmpbuf[BUFSZ]; - const char *aligntxt[] = {"chaotic", "neutral", "lawful"}; + static const char *aligntxt[] = { "chaotic", "neutral", "lawful" }; /* hu_stat[] from eat.c has trailing spaces which foul up comparisons */ - const char *hutxt[] = {"Satiated", "", "Hungry", "Weak", - "Fainting", "Fainted", "Starved"}; + static const char *hutxt[] = { "Satiated", "", "Hungry", "Weak", + "Fainting", "Fainted", "Starved" }; /* Examples: 3.6.1: @@ -1737,7 +1737,7 @@ boolean from_configfile; return parse_condition(s, sidx); ++sidx; - while(s[sidx]) { + while (s[sidx]) { char buf[BUFSZ], **subfields; int sf = 0; /* subfield count */ int kidx; @@ -1752,14 +1752,14 @@ boolean from_configfile; if (!s[sidx][0]) return TRUE; - memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s)); + memset((genericptr_t) &hilite, 0, sizeof (struct hilite_s)); hilite.set = FALSE; /* mark it "unset" */ hilite.fld = fld; - if (*s[sidx+1] == '\0' || !strcmpi(s[sidx], "always")) { + if (*s[sidx + 1] == '\0' || !strcmpi(s[sidx], "always")) { /* "field/always/color" OR "field/color" */ always = TRUE; - if (*s[sidx+1] == '\0') + if (*s[sidx + 1] == '\0') sidx--; goto do_rel; } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) { @@ -1771,7 +1771,8 @@ boolean from_configfile; goto do_rel; } else if (fld == BL_CAP && is_fld_arrayvalues(s[sidx], enc_stat, - SLT_ENCUMBER, OVERLOADED+1, &kidx)) { + SLT_ENCUMBER, OVERLOADED + 1, + &kidx)) { txt = enc_stat[kidx]; txtval = TRUE; goto do_rel; @@ -1782,7 +1783,7 @@ boolean from_configfile; goto do_rel; } else if (fld == BL_HUNGER && is_fld_arrayvalues(s[sidx], hutxt, - SATIATED, STARVED+1, &kidx)) { + SATIATED, STARVED + 1, &kidx)) { txt = hu_stat[kidx]; /* store hu_stat[] val, not hutxt[] */ txtval = TRUE; goto do_rel; @@ -1793,11 +1794,11 @@ boolean from_configfile; tmp = s[sidx]; if (strchr(tmp, '%')) percent = TRUE; - if (strchr(tmp, '<')) + if (*tmp == '<') lt = TRUE; - if (strchr(tmp, '>')) + else if (*tmp == '>') gt = TRUE; - (void) stripchars(tmpbuf, "%<>", tmp); + (void) stripchars(tmpbuf, "%<>+", tmp); tmp = tmpbuf; while (*tmp) { if (!index("0123456789", *tmp) From e9f1e032717921cb7a51c0f2822628355a35ec36 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 19 May 2018 04:19:18 -0700 Subject: [PATCH 06/23] fix some of #H7156 - perm_invent issues > [1. perm_invent is kept in flags so persists across save/restore, but > perm_invent capability can change if player restores with a different > interface--or same one running on a different-sized display--so it > ought to be in iflags instead.] Not addressed here. > 2. perm_invent window does not get updated when charging a wand (or > other chargeable item presumably), with a scroll of charging. Most scrolls rely on useup() -> update_inventory(), but charging uses up the scroll early so that it will be gone from inventory when choosing an item to charge. It needed an explicit update_inventory() after charging. > 3. update_inventory(), is called from setworn(), which is called from > dorestore(), when loading a save. Segfaults have been observed in > variants based on this code (though not yet in vanilla 3.6.1), so it's > possible this may be unsafe. The update_inventory() call in setworn() > could be protected with "if (!restoring) ..." tty doesn't support perm_invent, so this might be a win32 issue. I've made the suggested change, but a better fix would be to turn off perm_invent as soon as options processing (new game) or options restore (old game unless/until #1 gets changed) has finished setting things up, then turn it back on at the end of moveloop()'s prolog when play is about to start. = = Most of the read.c change is reordering prototypes to match the order of the corresponding functions. I did this when adding a new static routine, then ended up discarding that routine. --- doc/fixes36.2 | 2 ++ src/read.c | 36 ++++++++++++++++++++++++------------ src/worn.c | 9 ++++++--- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index db900eae1..7aa79d563 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -14,6 +14,8 @@ Killing Vlad while he was in bat/fog cloud/wolf form gave poorly worded spaces in hilite_status option title text field not working numeric hilite_status values didn't allow negative numbers (needed for AC); change them to accept leading '-', also accept unary '+' as a no-op +permanent inventory window was updated too soon when a scroll of charging + was used to [re]charge an item, not reflecting the item's change(s) Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/read.c b/src/read.c index 02cb79b91..f86afe04a 100644 --- a/src/read.c +++ b/src/read.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 read.c $NHDT-Date: 1515802375 2018/01/13 00:12:55 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.150 $ */ +/* NetHack 3.6 read.c $NHDT-Date: 1526728750 2018/05/19 11:19:10 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.155 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -19,19 +19,23 @@ static NEARDATA const char readable[] = { ALL_CLASSES, SCROLL_CLASS, static const char all_count[] = { ALLOW_COUNT, ALL_CLASSES, 0 }; STATIC_DCL boolean FDECL(learnscrolltyp, (SHORT_P)); -STATIC_DCL char * FDECL(erode_obj_text, (struct obj *, char *)); -STATIC_DCL void NDECL(do_class_genocide); +STATIC_DCL char *FDECL(erode_obj_text, (struct obj *, char *)); +STATIC_DCL char *FDECL(apron_text, (struct obj *, char *buf)); STATIC_DCL void FDECL(stripspe, (struct obj *)); STATIC_DCL void FDECL(p_glow1, (struct obj *)); STATIC_DCL void FDECL(p_glow2, (struct obj *, const char *)); -STATIC_DCL void FDECL(randomize, (int *, int)); STATIC_DCL void FDECL(forget_single_object, (int)); +#if 0 /* not used */ +STATIC_DCL void FDECL(forget_objclass, (int)); +#endif +STATIC_DCL void FDECL(randomize, (int *, int)); STATIC_DCL void FDECL(forget, (int)); STATIC_DCL int FDECL(maybe_tame, (struct monst *, struct obj *)); -STATIC_DCL boolean FDECL(is_valid_stinking_cloud_pos, (int, int, BOOLEAN_P)); -STATIC_DCL void FDECL(display_stinking_cloud_positions, (int)); STATIC_DCL boolean FDECL(get_valid_stinking_cloud_pos, (int, int)); +STATIC_DCL boolean FDECL(is_valid_stinking_cloud_pos, (int, int, BOOLEAN_P)); +STATIC_PTR void FDECL(display_stinking_cloud_positions, (int)); STATIC_PTR void FDECL(set_lit, (int, int, genericptr)); +STATIC_DCL void NDECL(do_class_genocide); STATIC_OVL boolean learnscrolltyp(scrolltyp) @@ -56,7 +60,7 @@ struct obj *sobj; (void) learnscrolltyp(sobj->otyp); } -char * +STATIC_OVL char * erode_obj_text(otmp, buf) struct obj *otmp; char *buf; @@ -158,7 +162,7 @@ char *buf; return erode_obj_text(tshirt, buf); } -char * +STATIC_OVL char * apron_text(apron, buf) struct obj *apron; char *buf; @@ -324,6 +328,7 @@ doread() return 0; } else if (Blind && (scroll->otyp != SPE_BOOK_OF_THE_DEAD)) { const char *what = 0; + if (scroll->oclass == SPBOOK_CLASS) what = "mystic runes"; else if (!scroll->dknown) @@ -953,7 +958,7 @@ int x,y; || distu(x, y) >= 32)); } -boolean +STATIC_OVL boolean is_valid_stinking_cloud_pos(x, y, showmsg) int x, y; boolean showmsg; @@ -966,7 +971,7 @@ boolean showmsg; return TRUE; } -void +STATIC_PTR void display_stinking_cloud_positions(state) int state; { @@ -1621,7 +1626,8 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ pline("This is a scroll of fire!"); dam *= 5; pline("Where do you want to center the explosion?"); - getpos_sethilite(display_stinking_cloud_positions, get_valid_stinking_cloud_pos); + getpos_sethilite(display_stinking_cloud_positions, + get_valid_stinking_cloud_pos); (void) getpos(&cc, TRUE, "the desired position"); if (!is_valid_stinking_cloud_pos(cc.x, cc.y, FALSE)) { /* try to reach too far, get burned */ @@ -1693,7 +1699,8 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ already_known ? "stinking " : ""); cc.x = u.ux; cc.y = u.uy; - getpos_sethilite(display_stinking_cloud_positions, get_valid_stinking_cloud_pos); + getpos_sethilite(display_stinking_cloud_positions, + get_valid_stinking_cloud_pos); if (getpos(&cc, TRUE, "the desired position") < 0) { pline1(Never_mind); break; @@ -1707,6 +1714,11 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */ default: impossible("What weird effect is this? (%u)", otyp); } + /* if sobj is gone, we've already called useup() above and the + update_inventory() that it performs might have come too soon + (before charging an item, for instance) */ + if (!sobj) + update_inventory(); return sobj ? 0 : 1; } diff --git a/src/worn.c b/src/worn.c index 612f71bac..cf45277a9 100644 --- a/src/worn.c +++ b/src/worn.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 worn.c $NHDT-Date: 1496959481 2017/06/08 22:04:41 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.49 $ */ +/* NetHack 3.6 worn.c $NHDT-Date: 1526728754 2018/05/19 11:19:14 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.51 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -105,7 +105,8 @@ long mask; } } } - update_inventory(); + if (!restoring) + update_inventory(); } /* called e.g. when obj is destroyed */ @@ -136,7 +137,9 @@ register struct obj *obj; if ((p = w_blocks(obj, wp->w_mask)) != 0) u.uprops[p].blocked &= ~wp->w_mask; } - update_inventory(); + /* setnotworn() isn't called during restore but parallel setworn() */ + if (!restoring) + update_inventory(); } /* return a bitmask of the equipment slot(s) a given item might be worn in */ From 98099863ff23451045bef330874a89fe7f38a34f Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 19 May 2018 11:47:15 -0700 Subject: [PATCH 07/23] fix #H7159 - orc hero can start with lembas wafers Orc heroes get an extra food item ("to compensate for generally inferior equipment") and it could randomly be lembas wafers (or cram rations), and Ranger heroes always started with cram rations even when they're orcs. Fixing the latter was simple, but the normal race-based substitutions weren't applied to randomly generated items, so the fix for the former required a bit of code reorganization in ini_inv(). Elf heroes already get lembas instead of cram; do the reverse for dwarves (although I don't think this case can happen--no role gets lembas wafers and only orcs and always-human tourists get random food); give orc heroes tripe instead of either lembas or cram. --- doc/fixes36.2 | 1 + src/u_init.c | 35 +++++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 7aa79d563..e63c7e266 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -16,6 +16,7 @@ numeric hilite_status values didn't allow negative numbers (needed for AC); change them to accept leading '-', also accept unary '+' as a no-op permanent inventory window was updated too soon when a scroll of charging was used to [re]charge an item, not reflecting the item's change(s) +for starting inventory, don't give an orc hero lembas wafers or cram rations Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/u_init.c b/src/u_init.c index 3087f70d3..44fb9142a 100644 --- a/src/u_init.c +++ b/src/u_init.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 u_init.c $NHDT-Date: 1503960969 2017/08/28 22:56:09 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.40 $ */ +/* NetHack 3.6 u_init.c $NHDT-Date: 1526755625 2018/05/19 18:47:05 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.42 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2017. */ /* NetHack may be freely redistributed. See license for details. */ @@ -219,11 +219,14 @@ static struct inv_sub { { PM_ORC, SMALL_SHIELD, ORCISH_SHIELD }, { PM_ORC, RING_MAIL, ORCISH_RING_MAIL }, { PM_ORC, CHAIN_MAIL, ORCISH_CHAIN_MAIL }, + { PM_ORC, CRAM_RATION, TRIPE_RATION }, + { PM_ORC, LEMBAS_WAFER, TRIPE_RATION }, { PM_DWARF, SPEAR, DWARVISH_SPEAR }, { PM_DWARF, SHORT_SWORD, DWARVISH_SHORT_SWORD }, { PM_DWARF, HELMET, DWARVISH_IRON_HELM }, /* { PM_DWARF, SMALL_SHIELD, DWARVISH_ROUNDSHIELD }, */ /* { PM_DWARF, PICK_AXE, DWARVISH_MATTOCK }, */ + { PM_DWARF, LEMBAS_WAFER, CRAM_RATION }, { PM_GNOME, BOW, CROSSBOW }, { PM_GNOME, ARROW, CROSSBOW_BOLT }, { NON_PM, STRANGE_OBJECT, STRANGE_OBJECT } @@ -972,17 +975,8 @@ register struct trobj *trop; int otyp, i; while (trop->trclass) { - if (trop->trotyp != UNDEF_TYP) { - otyp = (int) trop->trotyp; - if (urace.malenum != PM_HUMAN) { - /* substitute specific items for generic ones */ - for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) - if (inv_subs[i].race_pm == urace.malenum - && otyp == inv_subs[i].item_otyp) { - otyp = inv_subs[i].subs_otyp; - break; - } - } + otyp = (int) trop->trotyp; + if (otyp != UNDEF_TYP) { obj = mksobj(otyp, TRUE, FALSE); } else { /* UNDEF_TYP */ static NEARDATA short nocreate = STRANGE_OBJECT; @@ -1057,6 +1051,23 @@ register struct trobj *trop; nocreate4 = otyp; } + if (urace.malenum != PM_HUMAN) { + /* substitute race-specific items; this used to be in + the 'if (otyp != UNDEF_TYP) { }' block above, but then + substitutions didn't occur for randomly generated items + (particularly food) which have racial substitutes */ + for (i = 0; inv_subs[i].race_pm != NON_PM; ++i) + if (inv_subs[i].race_pm == urace.malenum + && otyp == inv_subs[i].item_otyp) { + debugpline3("ini_inv: substituting %s for %s%s", + OBJ_NAME(objects[inv_subs[i].subs_otyp]), + (trop->trotyp == UNDEF_TYP) ? "random " : "", + OBJ_NAME(objects[otyp])); + otyp = obj->otyp = inv_subs[i].subs_otyp; + break; + } + } + /* nudist gets no armor */ if (u.uroleplay.nudist && obj->oclass == ARMOR_CLASS) { dealloc_obj(obj); From b17c6d0b5407ce7caeb2f74a6a7e79cc0e8206d4 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 19 May 2018 15:46:09 -0700 Subject: [PATCH 08/23] fix #H7155 - polearm can reveal hidden monster The code to choose a likely target when applying a polearm was basing its decision on visible spots which contained monsters, so could expose the location of a hidden monster if there was only one such spot within polearm range. Not mentioned in the report: it also wouldn't pick remembered, unseen monster unless there was a monster still at that spot. I've changed it to choose candidate location based on the glyphs shown rather than on the presence of monsters. --- doc/fixes36.2 | 1 + src/apply.c | 54 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index e63c7e266..f22af782e 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -17,6 +17,7 @@ numeric hilite_status values didn't allow negative numbers (needed for AC); permanent inventory window was updated too soon when a scroll of charging was used to [re]charge an item, not reflecting the item's change(s) for starting inventory, don't give an orc hero lembas wafers or cram rations +targetting with a polearm could give away location of hidden monster Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/apply.c b/src/apply.c index c40d1ac62..d8cbb7a87 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 apply.c $NHDT-Date: 1519598527 2018/02/25 22:42:07 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.243 $ */ +/* NetHack 3.6 apply.c $NHDT-Date: 1526769961 2018/05/19 22:46:01 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.246 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2865,21 +2865,41 @@ coord *pos; int min_range, max_range; { struct monst *mtmp; - struct monst *selmon = (struct monst *) 0; + coord mpos; + boolean impaired; + int x, y, lo_x, hi_x, lo_y, hi_y, rt, glyph; - for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) - if (mtmp && !DEADMONSTER(mtmp) && !mtmp->mtame - && cansee(mtmp->mx, mtmp->my) - && distu(mtmp->mx, mtmp->my) <= max_range - && distu(mtmp->mx, mtmp->my) >= min_range) { - if (selmon) - return FALSE; - selmon = mtmp; + if (Blind) + return FALSE; /* must be able to see target location */ + impaired = (Confusion || Stunned || Hallucination); + mpos.x = mpos.y = 0; /* no candidate location yet */ + rt = isqrt(max_range); + lo_x = max(u.ux - rt, 1), hi_x = min(u.ux + rt, COLNO - 1); + lo_y = max(u.uy - rt, 0), hi_y = min(u.uy + rt, ROWNO - 1); + for (x = lo_x; x <= hi_x; ++x) { + for (y = lo_y; y <= hi_y; ++y) { + if (distu(x, y) < min_range || distu(x, y) > max_range + || !isok(x, y) || !cansee(x, y)) + continue; + glyph = glyph_at(x, y); + if (!impaired + && glyph_is_monster(glyph) + && (mtmp = m_at(x, y)) != 0 + && (mtmp->mtame || (mtmp->mpeaceful && flags.confirm))) + continue; + if (glyph_is_monster(glyph) + || glyph_is_warning(glyph) + || glyph_is_invisible(glyph) + || (glyph_is_statue(glyph) && impaired)) { + if (mpos.x) + return FALSE; /* more than one candidate location */ + mpos.x = x, mpos.y = y; + } } - if (!selmon) - return FALSE; - pos->x = selmon->mx; - pos->y = selmon->my; + } + if (!mpos.x) + return FALSE; /* no candidate location */ + *pos = mpos; return TRUE; } @@ -2887,8 +2907,8 @@ static int polearm_range_min = -1; static int polearm_range_max = -1; STATIC_OVL boolean -get_valid_polearm_position(x,y) -int x,y; +get_valid_polearm_position(x, y) +int x, y; { return (isok(x, y) && ACCESSIBLE(levl[x][y].typ) && distu(x, y) >= polearm_range_min @@ -2998,7 +3018,7 @@ struct obj *obj; return res; } - context.polearm.hitmon = NULL; + context.polearm.hitmon = (struct monst *) 0; /* Attack the monster there */ bhitpos = cc; if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != (struct monst *) 0) { From 4fa455e30662738faba5c33a6009f3f19a0ccbbe Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 20 May 2018 01:20:51 -0700 Subject: [PATCH 09/23] more status_hilite threshold handling Negative AC needed one extra change to support >-N since there was a place in the code that assumed 0 was the lowest possible value. (My earlier testing was with <-N which didn't have that issue.) Make '/N/ work as 'val > N' instead of >=. The <= and >= behavior might have been intentional but the only support for that I could find was that the 'O' menu used "N or less" for '<' and "N or more" for '>' when setting up 'absolute' rules. If we actually want <= and >= (and we probably do...), we should add them as more relationship operators instead of misusing < and >. Simplify the is_ltgt_percentnumber() case when parsing options since input has been fully validated by the point that that test passes. Among other things, /<-0/ and />-0' are now accepted (as synonums for 0; -0 doesn't mean anything special) instead of being silently rejected and then discarding the rest of the config file. (That bad behavior is a separate issue not dealt with here.) --- src/botl.c | 90 +++++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/src/botl.c b/src/botl.c index 60c13815a..daa1dd468 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526709371 2018/05/19 05:56:11 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.94 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1526804444 2018/05/20 08:20:44 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1349,7 +1349,7 @@ int *colorptr; { int bestcolor = NO_COLOR; struct hilite_s *hl; - anything *value = (anything *)vp; + anything *value = (anything *) vp; char *txtstr, *cmpstr; if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS) @@ -1358,11 +1358,12 @@ int *colorptr; if (blstats[idx][fldidx].thresholds) { /* there are hilites set here */ int max_pc = 0, min_pc = 100; - int max_val = 0, min_val = LARGEST_INT; + int max_val = -LARGEST_INT, min_val = LARGEST_INT; boolean exactmatch = FALSE; hl = blstats[idx][fldidx].thresholds; + /* min_/max_ are used to track best fit */ while (hl) { switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: @@ -1371,12 +1372,12 @@ int *colorptr; min_pc = max_pc = hl->value.a_int; exactmatch = TRUE; } else if (hl->rel == LT_VALUE && !exactmatch - && (hl->value.a_int >= pc) + && (pc < hl->value.a_int) && (hl->value.a_int <= min_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = hl->value.a_int; } else if (hl->rel == GT_VALUE && !exactmatch - && (hl->value.a_int <= pc) + && (pc > hl->value.a_int) && (hl->value.a_int >= max_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); max_pc = hl->value.a_int; @@ -1398,13 +1399,13 @@ int *colorptr; min_val = max_val = hl->value.a_int; exactmatch = TRUE; } else if (hl->rel == LT_VALUE && !exactmatch - && (hl->value.a_int >= value->a_int) - && (hl->value.a_int < min_val)) { + && (value->a_int < hl->value.a_int) + && (hl->value.a_int <= min_val)) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = hl->value.a_int; } else if (hl->rel == GT_VALUE && !exactmatch - && (hl->value.a_int <= value->a_int) - && (hl->value.a_int > max_val)) { + && (value->a_int > hl->value.a_int) + && (hl->value.a_int >= max_val)) { merge_bestcolor(&bestcolor, hl->coloridx); max_val = hl->value.a_int; } @@ -1535,7 +1536,7 @@ const char *str; return (*s == '\0'); } -/* does str only contain "<>0-9%" chars */ +/* does str only contain "<>-+0-9%" chars */ STATIC_OVL boolean has_ltgt_percentnumber(str) const char *str; @@ -1543,7 +1544,7 @@ const char *str; const char *s = str; while (*s) { - if (!index("<>-0123456789%", *s)) + if (!index("<>-+0123456789%", *s)) return FALSE; s++; } @@ -1761,73 +1762,80 @@ boolean from_configfile; always = TRUE; if (*s[sidx + 1] == '\0') sidx--; - goto do_rel; } else if (!strcmpi(s[sidx], "up") || !strcmpi(s[sidx], "down")) { if (!strcmpi(s[sidx], "down")) down = TRUE; else up = TRUE; changed = TRUE; - goto do_rel; } else if (fld == BL_CAP && is_fld_arrayvalues(s[sidx], enc_stat, SLT_ENCUMBER, OVERLOADED + 1, &kidx)) { txt = enc_stat[kidx]; txtval = TRUE; - goto do_rel; } else if (fld == BL_ALIGN && is_fld_arrayvalues(s[sidx], aligntxt, 0, 3, &kidx)) { txt = aligntxt[kidx]; txtval = TRUE; - goto do_rel; } else if (fld == BL_HUNGER && is_fld_arrayvalues(s[sidx], hutxt, SATIATED, STARVED + 1, &kidx)) { txt = hu_stat[kidx]; /* store hu_stat[] val, not hutxt[] */ txtval = TRUE; - goto do_rel; } else if (!strcmpi(s[sidx], "changed")) { changed = TRUE; - goto do_rel; } else if (is_ltgt_percentnumber(s[sidx])) { - tmp = s[sidx]; + tmp = s[sidx]; /* is_ltgt_() guarantees [<>]?[-+]?[0-9]+%? */ if (strchr(tmp, '%')) percent = TRUE; if (*tmp == '<') lt = TRUE; else if (*tmp == '>') gt = TRUE; - (void) stripchars(tmpbuf, "%<>+", tmp); - tmp = tmpbuf; - while (*tmp) { - if (!index("0123456789", *tmp) - && (*tmp != '-' || tmp > tmpbuf)) - return FALSE; - tmp++; - } + /* '%', '<', '>' have served their purpose, unary '+' is + just decorative, so get rid of them, leaving -?[0-9]+ */ + tmp = stripchars(tmpbuf, "%<>+", tmp); numeric = TRUE; - tmp = tmpbuf; - if (strlen(tmp) > 0) { - dt = initblstats[fld].anytype; - if (percent) - dt = ANY_INT; - (void) s_to_anything(&hilite.value, tmp, dt); - } else - return FALSE; - if (!hilite.value.a_void && (strcmp(tmp, "0") != 0)) - return FALSE; + dt = percent ? ANY_INT : initblstats[fld].anytype; + (void) s_to_anything(&hilite.value, tmp, dt); + if (dt == ANY_INT + /* AC is the only field where negative values make sense but + accept >-1 for other fields since we don't support >=0 + which someone might want to use in a catch-all rule */ + && (hilite.value.a_int < (fld == BL_AC ? -128 : gt ? -1 : 0) + /* percentages have another more comprehensive check below */ + || hilite.value.a_int > (percent ? 100 : LARGEST_INT))) { + config_error_add( + "hilite_status threshold '%s%d%s' is out of range", + gt ? ">" : lt ? "<" : "", + hilite.value.a_int, + percent ? "%" : ""); + return FALSE; + } + /* + * Note: the only check for ANY_LONG would be + * if (hilite.value.a_long < (gt ? -1L : 0L)) { } + * so we might as well skip it instead of replicating + * the above config_error_add() for hilite.value.a_long. + * The only non-int/non-long numeric is BL_HUNGER + * (unsigned: ANY_UINT) and it should be changed to int + * instead of trying to support one oddball field, + * particularly since users are encouraged to use the + * "satiated"/"hungry"/"weak"/&c strings instead of + * their internal numeric values. + */ } else if (initblstats[fld].anytype == ANY_STR) { txt = s[sidx]; txtval = TRUE; - goto do_rel; } else { config_error_add(has_ltgt_percentnumber(s[sidx]) ? "Wrong format '%s', expected a threshold number or percent" - : "Unknown behavior '%s'", s[sidx]); + : "Unknown behavior '%s'", + s[sidx]); return FALSE; } -do_rel: + /* relationships { LT_VALUE, GT_VALUE, EQ_VALUE} */ if (gt) hilite.rel = GT_VALUE; @@ -2666,7 +2674,7 @@ const char *str; start_menu(tmpwin); if (str) - Sprintf(buf, "%s or less", str); + Sprintf(buf, "%s than %s", (fld == BL_AC) ? "Better" : "Less", str); else Sprintf(buf, "Value goes down"); any = zeroany; @@ -2684,7 +2692,7 @@ const char *str; buf, MENU_UNSELECTED); if (str) - Sprintf(buf, "%s or more", str); + Sprintf(buf, "%s than %s", (fld == BL_AC) ? "Worse" : "More", str); else Sprintf(buf, "Value goes up"); any = zeroany; From 9cb9164ce5eb493b2fafb59fab1d1d6e4aabb98d Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 20 May 2018 15:11:49 -0400 Subject: [PATCH 10/23] update note of appreciation in Guidebooks (dat/history to follow) --- doc/Guidebook.mn | 9 ++++++--- doc/Guidebook.tex | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 8e84445b0..2ca470e00 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4522,9 +4522,12 @@ The official NetHack web site is maintained by \fBKen Lorber\fP at http://www.ne SPECIAL THANKS .pg On behalf of the NetHack community, thank you very much once -again to \fBM. Drew Streib\fP, \fBPasi Kallinen\fP and \fBRobin Bandy\fP -for providing public NetHack servers at nethack.alt.org and devnull.net -and/or for hosting annual NetHack tournaments. +again to \fBM. Drew Streib\fP, \fBPasi Kallinen\fP for providing a +public NetHack server at nethack.alt.org. Thanks to \fBKeith Simpson\fP +and \fBAndy Thomson\fP for hardfought.org. Thanks to all those +unnamed dungeoneers who invest their time and effort into annual +NetHack tournaments such as Junehack and in days past, +/dev/null (gone for now, but not forgotten). .pg .ce - - - - - - - - - -\ \ \ \ \" when centered, the dashes look a little too far diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 3859b4e68..048c81021 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -5297,9 +5297,12 @@ http:{\tt /}{\tt /}www.nethack.org{\tt /}. \subsection*{Special Thanks} \nd On behalf of the {\it NetHack\/} community, thank you very much once -again to {\it M. Drew Streib}, {\it Pasi Kallinen} and {\it Robin Bandy} for -providing public {\it NetHack\/} servers at {\it nethack.alt.org\/} and -{\it devnull.net\/} and/or for hosting annual {\it NetHack\/} tournaments. +again to {\it M. Drew Streib}, {\it Pasi Kallinen} for providing a +public NetHack server at nethack.alt.org. Thanks to {\it Keith Simpson} +and {\it Andy Thomson} for hardfought.org. Thanks to all those +unnamed dungeoneers who invest their time and effort into annual +{\it NetHack\/} tournaments such as {\it Junethack} and in days past, +{\it devnull.net\/} (gone for now, but not forgotten). \clearpage %.hn From 5a63871c834dacd0fd7abafc1654d490c5ec37ec Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 20 May 2018 15:17:57 -0400 Subject: [PATCH 11/23] be consistant with the tex version --- doc/Guidebook.mn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 2ca470e00..30c7e9dbc 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4527,7 +4527,7 @@ public NetHack server at nethack.alt.org. Thanks to \fBKeith Simpson\fP and \fBAndy Thomson\fP for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual NetHack tournaments such as Junehack and in days past, -/dev/null (gone for now, but not forgotten). +devnull.net (gone for now, but not forgotten). .pg .ce - - - - - - - - - -\ \ \ \ \" when centered, the dashes look a little too far From c58b6119caa2d7e1e448d6f57e88daaefc47d7b6 Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 20 May 2018 15:17:57 -0400 Subject: [PATCH 12/23] be consistent with the tex version --- doc/Guidebook.mn | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 2ca470e00..30c7e9dbc 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -4527,7 +4527,7 @@ public NetHack server at nethack.alt.org. Thanks to \fBKeith Simpson\fP and \fBAndy Thomson\fP for hardfought.org. Thanks to all those unnamed dungeoneers who invest their time and effort into annual NetHack tournaments such as Junehack and in days past, -/dev/null (gone for now, but not forgotten). +devnull.net (gone for now, but not forgotten). .pg .ce - - - - - - - - - -\ \ \ \ \" when centered, the dashes look a little too far From dc00df935b751c9bec341bcfd25739103e380ec4 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 21 May 2018 05:58:01 -0700 Subject: [PATCH 13/23] hilite_status support for <=, >=, explicit = Add threshold relationships <= and >= so that the change to make < and > perform their expected comparison can be resolved. "Point release shouldn't force players to update their config files" does not carry sufficient weight given that they already had to do that to turn on status highlighting when going from 3.6.0 to 3.6.1. The 3.6.2 release notes can warn them about the need to update their status highlight options if they're currently using '<' and/or '>'. Entering new hilite rules via the 'O' command accepted '=' prefix for numbers, but rules from config files did not. Now they do. The '=' prefix is optional in both situations. With 'O', percent rules and absolute rules had separate menu entries so picking one was already choosing the rule type, but entering a numeric value without percent sign (for percent) or with one (for absolute) would change the type on the fly. If someone has already picked percentage they shouldn't be required to append '%' to the digits, so that is now optional. If explicitly included with the number after having picked absolute, the value is rejected. It is trivial to back up in those menus and choose the alternate type if someone changes his/her mind part way through. If a status field has both persistent (percent, absolute, always) and temporary highlights (up, down, changed), give the temporary one precedence when the value has changed. To do that with 3.6.1, the rules for temporary had to follow the ones for persistent highlights since whichever matched last was the one used. Now their order relative to each other doesn't matter. If a value increases and there is both an 'up' rule and a 'changed' rule, the more specific 'up' takes precedence, regardless of their relative order; likewise for decreases and 'down' vs 'changed'. There were a couple more tweaks needed to support negative values; I overlooked the 'O' menu handling before. >-1% and <101% now work for both the config file and interactive adding via 'O' methods of defining highlight rules, although new >=0% and <=100% will be clearer to anyone examining a rule set. 'enum relationship' was forcing LT_VALUE to be -1 but that fact was never utilized anywhere, and the code was using magic number -2 to mean "no relationship yet". This adds NO_LTEQGT to replace the latter and gives it value -1. EQ_VALUE is still 0 so effectively the default if a highlight hasn't been fully set up yet. LT_VALUE is now just another positive value along with GT_VALUE, LE_VALUE, &c. The Guidebook hasn't caught up with the code yet. The rule choosing code used when deciding how to highlight something only supports 'int' fields and relies on 'long' having the same bits. It needs to be extended to support 'long' properly. Fixing should be straightforward (except maybe for the initialization of min/max best fit handling) but this doesn't address that. Also, data type for encumbrance/carrying-capacity should be changed from unsigned to plain int so that no extra handling for just one field will be needed. --- doc/fixes36.2 | 4 + include/botl.h | 10 +- src/botl.c | 391 +++++++++++++++++++++++++++++++------------------ 3 files changed, 258 insertions(+), 147 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index f22af782e..3ea0db50f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -56,6 +56,10 @@ unix: Makefile.src and Makefile.utl inadvertently relied on a 'gnu make' General New Features -------------------- integrate aklys feature introduced in 3.6.1 into display +status_hilite options which use comparisons may now use <= and >= in + addition to previous < and >; in 3.6.1 the latter operated as if + they were <= and >= but now behave as conventional less than and + greater than; old highlight rules using them should be updated Code Cleanup and Reorganization diff --git a/include/botl.h b/include/botl.h index ce2a94496..4d9347088 100644 --- a/include/botl.h +++ b/include/botl.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.h $NHDT-Date: 1452660165 2016/01/13 04:42:45 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.15 $ */ +/* NetHack 3.6 botl.h $NHDT-Date: 1526907469 2018/05/21 12:57:49 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.19 $ */ /* Copyright (c) Michael Allison, 2003 */ /* NetHack may be freely redistributed. See license for details. */ @@ -32,11 +32,13 @@ enum statusfields { BL_FLUSH = -1, BL_TITLE = 0, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, /* 1..6 */ BL_ALIGN, BL_SCORE, BL_CAP, BL_GOLD, BL_ENE, BL_ENEMAX, /* 7..12 */ - BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, BL_HPMAX, BL_LEVELDESC, /* 13..20 */ - BL_EXP, BL_CONDITION + BL_XP, BL_AC, BL_HD, BL_TIME, BL_HUNGER, BL_HP, /* 13..18 */ + BL_HPMAX, BL_LEVELDESC, BL_EXP, BL_CONDITION /* 19..22 */ }; -enum relationships { LT_VALUE = -1, EQ_VALUE, GT_VALUE, TXT_VALUE }; +enum relationships { NO_LTEQGT = -1, + EQ_VALUE, LT_VALUE, LE_VALUE, + GE_VALUE, GT_VALUE, TXT_VALUE }; #define MAXBLSTATS (BL_CONDITION + 1) diff --git a/src/botl.c b/src/botl.c index daa1dd468..c219d3db3 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526804444 2018/05/20 08:20:44 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.95 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1526907473 2018/05/21 12:57:53 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.96 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -455,12 +455,15 @@ STATIC_DCL void FDECL(split_clridx, (int, int *, int *)); STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int)); STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *, unsigned long, const char *)); - STATIC_DCL void NDECL(status_hilite_linestr_done); STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int)); STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions); STATIC_DCL void NDECL(status_hilite_linestr_gather); STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *)); +STATIC_DCL int NDECL(status_hilite_menu_choose_field); +STATIC_DCL int FDECL(status_hilite_menu_choose_behavior, (int)); +STATIC_DCL int FDECL(status_hilite_menu_choose_updownboth, (int, + const char *)); STATIC_DCL boolean FDECL(status_hilite_menu_add, (int)); #define has_hilite(i) (blstats[0][(i)].thresholds) #endif @@ -1357,14 +1360,18 @@ int *colorptr; if (blstats[idx][fldidx].thresholds) { /* there are hilites set here */ - int max_pc = 0, min_pc = 100; + int max_pc = -1, min_pc = 101; int max_val = -LARGEST_INT, min_val = LARGEST_INT; - boolean exactmatch = FALSE; - - hl = blstats[idx][fldidx].thresholds; + boolean exactmatch = FALSE, updown = FALSE, changed = FALSE; /* min_/max_ are used to track best fit */ - while (hl) { + for (hl = blstats[idx][fldidx].thresholds; hl; hl = hl->next) { + /* if we've already matched a temporary highlight, it takes + precedence over all persistent ones; we still process + updown rules to get the last one which qualifies */ + if ((updown || changed) && hl->behavior != BL_TH_UPDOWN) + continue; + switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: if (hl->rel == EQ_VALUE && pc == hl->value.a_int) { @@ -1376,24 +1383,43 @@ int *colorptr; && (hl->value.a_int <= min_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = hl->value.a_int; + } else if (hl->rel == LE_VALUE && !exactmatch + && (pc <= hl->value.a_int) + && (hl->value.a_int <= min_pc)) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_pc = hl->value.a_int; } else if (hl->rel == GT_VALUE && !exactmatch && (pc > hl->value.a_int) && (hl->value.a_int >= max_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); max_pc = hl->value.a_int; + } else if (hl->rel == GE_VALUE && !exactmatch + && (pc >= hl->value.a_int) + && (hl->value.a_int >= max_pc)) { + merge_bestcolor(&bestcolor, hl->coloridx); + max_pc = hl->value.a_int; } break; case BL_TH_UPDOWN: + /* specific 'up' or 'down' takes precedence over general + 'changed' regardless of their order in the rule set */ if (chg < 0 && hl->rel == LT_VALUE) { merge_bestcolor(&bestcolor, hl->coloridx); + updown = TRUE; } else if (chg > 0 && hl->rel == GT_VALUE) { merge_bestcolor(&bestcolor, hl->coloridx); - } else if (hl->rel == EQ_VALUE && chg) { + updown = TRUE; + } else if (chg != 0 && hl->rel == EQ_VALUE && !updown) { merge_bestcolor(&bestcolor, hl->coloridx); - min_val = max_val = hl->value.a_int; + changed = TRUE; } break; case BL_TH_VAL_ABSOLUTE: + /* + * TODO: + * This covers data type ANY_INT. We need to handle ANY_LONG + * separately using a_long and new min_lval, max_lval. + */ if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = max_val = hl->value.a_int; @@ -1403,18 +1429,29 @@ int *colorptr; && (hl->value.a_int <= min_val)) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = hl->value.a_int; + } else if (hl->rel == LE_VALUE && !exactmatch + && (value->a_int <= hl->value.a_int) + && (hl->value.a_int <= min_val)) { + merge_bestcolor(&bestcolor, hl->coloridx); + min_val = hl->value.a_int; } else if (hl->rel == GT_VALUE && !exactmatch && (value->a_int > hl->value.a_int) && (hl->value.a_int >= max_val)) { merge_bestcolor(&bestcolor, hl->coloridx); max_val = hl->value.a_int; + } else if (hl->rel == GE_VALUE && !exactmatch + && (value->a_int >= hl->value.a_int) + && (hl->value.a_int >= max_val)) { + merge_bestcolor(&bestcolor, hl->coloridx); + max_val = hl->value.a_int; } break; case BL_TH_TEXTMATCH: txtstr = dupstr(blstats[idx][fldidx].val); cmpstr = txtstr; if (fldidx == BL_TITLE) { - int len = (strlen(plname) + sizeof(" the")); + int len = (int) (strlen(plname) + sizeof (" the")); + cmpstr += len; } (void) trimspaces(cmpstr); @@ -1432,7 +1469,6 @@ int *colorptr; default: break; } - hl = hl->next; } } *colorptr = bestcolor; @@ -1444,9 +1480,9 @@ split_clridx(idx, coloridx, attrib) int idx; int *coloridx, *attrib; { - if (idx && coloridx && attrib) { - *coloridx = idx & 0x00FF; - *attrib = (idx & 0xFF00) >> 8; + if (coloridx && attrib) { + *coloridx = idx & 0x00FF; + *attrib = (idx >> 8) & 0x00FF; } } @@ -1516,7 +1552,7 @@ boolean from_configfile; return TRUE; } -/* is str in the format of "[<>]?[-+]?[0-9]+%?" regex */ +/* is str in the format of "[<>]?=?[-+]?[0-9]+%?" regex */ STATIC_OVL boolean is_ltgt_percentnumber(str) const char *str; @@ -1525,6 +1561,8 @@ const char *str; if (*s == '<' || *s == '>') s++; + if (*s == '=') + s++; if (*s == '-' || *s == '+') s++; if (!digit(*s)) @@ -1536,7 +1574,7 @@ const char *str; return (*s == '\0'); } -/* does str only contain "<>-+0-9%" chars */ +/* does str only contain "<>=-+0-9%" chars */ STATIC_OVL boolean has_ltgt_percentnumber(str) const char *str; @@ -1544,7 +1582,7 @@ const char *str; const char *s = str; while (*s) { - if (!index("<>-+0123456789%", *s)) + if (!index("<>=-+0123456789%", *s)) return FALSE; s++; } @@ -1661,22 +1699,15 @@ struct hilite_s *hilite; return; /* alloc and initialize a new hilite_s struct */ - new_hilite = (struct hilite_s *) alloc(sizeof(struct hilite_s)); + new_hilite = (struct hilite_s *) alloc(sizeof (struct hilite_s)); *new_hilite = *hilite; /* copy struct */ new_hilite->set = TRUE; new_hilite->fld = fld; - new_hilite->next = (struct hilite_s *)0; + new_hilite->next = blstats[0][fld].thresholds; + blstats[0][fld].thresholds = new_hilite; + /* sort_hilites(fld) */ - /* Does that status field currently have any hilite thresholds? */ - if (!blstats[0][fld].thresholds) { - blstats[0][fld].thresholds = new_hilite; - } else { - struct hilite_s *temp_hilite = blstats[0][fld].thresholds; - new_hilite->next = temp_hilite; - blstats[0][fld].thresholds = new_hilite; - /* sort_hilites(fld) */ - } /* current and prev must both point at the same hilites */ blstats[1][fld].thresholds = blstats[0][fld].thresholds; } @@ -1691,11 +1722,8 @@ boolean from_configfile; int sidx = 0, i = -1, dt = -1; int coloridx = -1, successes = 0; int disp_attrib = 0; - boolean percent = FALSE, changed = FALSE, numeric = FALSE; - boolean down= FALSE, up = FALSE; - boolean gt = FALSE, lt = FALSE, eq = FALSE, neq = FALSE; - boolean txtval = FALSE; - boolean always = FALSE; + boolean percent, changed, numeric, down, up, + gt, lt, ge, le, eq, txtval, always; const char *txt; enum statusfields fld = BL_FLUSH; struct hilite_s hilite; @@ -1744,10 +1772,9 @@ boolean from_configfile; int kidx; txt = (const char *)0; - percent = changed = numeric = FALSE; - down = up = FALSE; - gt = eq = lt = neq = txtval = FALSE; - always = FALSE; + percent = numeric = always = FALSE; + down = up = changed = FALSE; + gt = ge = eq = le = lt = txtval = FALSE; /* threshold value */ if (!s[sidx][0]) @@ -1786,29 +1813,38 @@ boolean from_configfile; } else if (!strcmpi(s[sidx], "changed")) { changed = TRUE; } else if (is_ltgt_percentnumber(s[sidx])) { - tmp = s[sidx]; /* is_ltgt_() guarantees [<>]?[-+]?[0-9]+%? */ + tmp = s[sidx]; /* is_ltgt_() guarantees [<>]?=?[-+]?[0-9]+%? */ if (strchr(tmp, '%')) percent = TRUE; - if (*tmp == '<') - lt = TRUE; - else if (*tmp == '>') - gt = TRUE; - /* '%', '<', '>' have served their purpose, unary '+' is + if (*tmp == '<') { + if (tmp[1] == '=') + le = TRUE; + else + lt = TRUE; + } else if (*tmp == '>') { + if (tmp[1] == '=') + ge = TRUE; + else + gt = TRUE; + } + /* '%', '<', '>' have served their purpose, '=' is either + part of '<' or '>' or optional for '=N', unary '+' is just decorative, so get rid of them, leaving -?[0-9]+ */ - tmp = stripchars(tmpbuf, "%<>+", tmp); + tmp = stripchars(tmpbuf, "%<>=+", tmp); numeric = TRUE; dt = percent ? ANY_INT : initblstats[fld].anytype; (void) s_to_anything(&hilite.value, tmp, dt); if (dt == ANY_INT /* AC is the only field where negative values make sense but - accept >-1 for other fields since we don't support >=0 - which someone might want to use in a catch-all rule */ + accept >-1 for other fields */ && (hilite.value.a_int < (fld == BL_AC ? -128 : gt ? -1 : 0) /* percentages have another more comprehensive check below */ - || hilite.value.a_int > (percent ? 100 : LARGEST_INT))) { + || hilite.value.a_int > (percent ? (lt ? 101 : 100) + : LARGEST_INT))) { config_error_add( "hilite_status threshold '%s%d%s' is out of range", - gt ? ">" : lt ? "<" : "", + gt ? ">" : ge ? ">=" + : lt ? "<" : le ? "<=" : "=", hilite.value.a_int, percent ? "%" : ""); return FALSE; @@ -1836,22 +1872,16 @@ boolean from_configfile; return FALSE; } - /* relationships { LT_VALUE, GT_VALUE, EQ_VALUE} */ - if (gt) + /* relationships {LT_VALUE, LE_VALUE, EQ_VALUE, GE_VALUE, GT_VALUE} */ + if (gt || up) hilite.rel = GT_VALUE; - else if (eq) - hilite.rel = EQ_VALUE; - else if (lt) + else if (lt || down) hilite.rel = LT_VALUE; - else if (percent) - hilite.rel = EQ_VALUE; - else if (numeric) - hilite.rel = EQ_VALUE; - else if (down) - hilite.rel = LT_VALUE; - else if (up) - hilite.rel = GT_VALUE; - else if (changed) + else if (ge) + hilite.rel = GE_VALUE; + else if (le) + hilite.rel = LE_VALUE; + else if (eq || percent || numeric || changed) hilite.rel = EQ_VALUE; else if (txtval) hilite.rel = TXT_VALUE; @@ -1869,13 +1899,24 @@ boolean from_configfile; config_error_add("Cannot use percent with '%s'", initblstats[fld].fldname); return FALSE; - } else if ((hilite.value.a_int < 0) + } else if ((hilite.value.a_int < -1) + || (hilite.value.a_int == -1 + && hilite.value.a_int != GT_VALUE) || (hilite.value.a_int == 0 && hilite.rel == LT_VALUE) - || (hilite.value.a_int > 100) || (hilite.value.a_int == 100 - && hilite.rel == GT_VALUE)) { - config_error_add("Illegal percentage value"); + && hilite.rel == GT_VALUE) + || (hilite.value.a_int == 101 + && hilite.value.a_int != LT_VALUE) + || (hilite.value.a_int > 101)) { + config_error_add( + "hilite_status: invalid percentage value '%s%d%%'", + (hilite.rel == LT_VALUE) ? "<" + : (hilite.rel == LE_VALUE) ? "<=" + : (hilite.rel == GT_VALUE) ? ">" + : (hilite.rel == GE_VALUE) ? ">=" + : "=", + hilite.value.a_int); return FALSE; } } @@ -1898,6 +1939,7 @@ boolean from_configfile; for (i = 0; i < sf; ++i) { int a = match_str2attr(subfields[i], FALSE); + if (a == ATR_DIM) disp_attrib |= HL_DIM; else if (a == ATR_BLINK) @@ -1942,7 +1984,7 @@ boolean from_configfile; hilite.anytype = dt; if (hilite.behavior == BL_TH_TEXTMATCH && txt - && strlen(txt) < QBUFSZ-1) { + && strlen(txt) < QBUFSZ - 1) { Strcpy(hilite.textmatch, txt); (void) trimspaces(hilite.textmatch); } @@ -2466,21 +2508,24 @@ struct hilite_s *hl; char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; char *tmpattr; + const char *op; if (!hl) return (char *) 0; behavebuf[0] = '\0'; clrbuf[0] = '\0'; + op = (hl->rel == LT_VALUE) ? "<" + : (hl->rel == LE_VALUE) ? "<=" + : (hl->rel == GT_VALUE) ? ">" + : (hl->rel == GE_VALUE) ? ">=" + : (hl->rel == EQ_VALUE) ? "=" + : 0; switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: - if (hl->rel == LT_VALUE) - Sprintf(behavebuf, "<%i%%", hl->value.a_int); - else if (hl->rel == GT_VALUE) - Sprintf(behavebuf, ">%i%%", hl->value.a_int); - else if (hl->rel == EQ_VALUE) - Sprintf(behavebuf, "%i%%", hl->value.a_int); + if (op) + Sprintf(behavebuf, "%s%d%%", op, hl->value.a_int); else impossible("hl->behavior=percentage, rel error"); break; @@ -2495,12 +2540,8 @@ struct hilite_s *hl; impossible("hl->behavior=updown, rel error"); break; case BL_TH_VAL_ABSOLUTE: - if (hl->rel == LT_VALUE) - Sprintf(behavebuf, "<%i", hl->value.a_int); - else if (hl->rel == GT_VALUE) - Sprintf(behavebuf, ">%i", hl->value.a_int); - else if (hl->rel == EQ_VALUE) - Sprintf(behavebuf, "%i", hl->value.a_int); + if (op) + Sprintf(behavebuf, "%s%d", op, hl->value.a_int); else impossible("hl->behavior=absolute, rel error"); break; @@ -2540,7 +2581,7 @@ struct hilite_s *hl; return buf; } -int +STATIC_OVL int status_hilite_menu_choose_field() { winid tmpwin; @@ -2569,7 +2610,7 @@ status_hilite_menu_choose_field() return fld; } -int +STATIC_OVL int status_hilite_menu_choose_behavior(fld) int fld; { @@ -2615,7 +2656,8 @@ int fld; nopts++; } - if (fld != BL_CAP && fld != BL_HUNGER && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { + if (fld != BL_CAP && fld != BL_HUNGER + && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { any = zeroany; any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE; add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, @@ -2631,7 +2673,8 @@ int fld; nopts++; } - if (initblstats[fld].anytype == ANY_STR || fld == BL_CAP || fld == BL_HUNGER) { + if (initblstats[fld].anytype == ANY_STR + || fld == BL_CAP || fld == BL_HUNGER) { any = zeroany; any.a_int = onlybeh = BL_TH_TEXTMATCH; Sprintf(buf, "%s text match", initblstats[fld].fldname); @@ -2659,12 +2702,12 @@ int fld; return beh; } -int +STATIC_OVL int status_hilite_menu_choose_updownboth(fld, str) int fld; const char *str; { - int res, ret = -2; + int res, ret = NO_LTEQGT; winid tmpwin; char buf[BUFSZ]; anything any; @@ -2674,7 +2717,8 @@ const char *str; start_menu(tmpwin); if (str) - Sprintf(buf, "%s than %s", (fld == BL_AC) ? "Better" : "Less", str); + Sprintf(buf, "%s than %s", + (fld == BL_AC) ? "Better (lower)" : "Less", str); else Sprintf(buf, "Value goes down"); any = zeroany; @@ -2682,6 +2726,15 @@ const char *str; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); + if (str) { + Sprintf(buf, "%s or %s", + str, (fld == BL_AC) ? "better (lower)" : "less"); + any = zeroany; + any.a_int = 10 + LE_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + if (str) Sprintf(buf, "Exactly %s", str); else @@ -2691,8 +2744,18 @@ const char *str; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); + if (str) { + Sprintf(buf, "%s or %s", + str, (fld == BL_AC) ? "worse (higher)" : "more"); + any = zeroany; + any.a_int = 10 + GE_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + if (str) - Sprintf(buf, "%s than %s", (fld == BL_AC) ? "Worse" : "More", str); + Sprintf(buf, "%s than %s", + (fld == BL_AC) ? "Worse (higher)" : "More", str); else Sprintf(buf, "Value goes up"); any = zeroany; @@ -2719,7 +2782,7 @@ int origfld; { int fld; int behavior; - int lt_gt_eq = 0; + int lt_gt_eq = NO_LTEQGT; /* not set up yet */ int clr = NO_COLOR, atr = HL_UNDEF; struct hilite_s hilite; unsigned long cond = 0UL; @@ -2740,55 +2803,63 @@ choose_field: colorqry[0] = '\0'; attrqry[0] = '\0'; - memset((genericptr_t) &hilite, 0, sizeof(struct hilite_s)); + memset((genericptr_t) &hilite, 0, sizeof (struct hilite_s)); hilite.set = FALSE; /* mark it "unset" */ hilite.fld = fld; choose_behavior: - behavior = status_hilite_menu_choose_behavior(fld); - if (behavior == (BL_TH_NONE-1)) { + if (behavior == (BL_TH_NONE - 1)) { return FALSE; } else if (behavior == BL_TH_NONE) { if (origfld == BL_FLUSH) goto choose_field; - else - return FALSE; + return FALSE; } hilite.behavior = behavior; choose_value: - if (behavior == BL_TH_VAL_PERCENTAGE || behavior == BL_TH_VAL_ABSOLUTE) { - char inbuf[BUFSZ] = DUMMY, buf[BUFSZ]; - int val; - boolean skipltgt = FALSE; - boolean gotnum = FALSE; - char *inp = inbuf; - char *numstart = inbuf; + char inbuf[BUFSZ], buf[BUFSZ]; + anything aval; + int val, dt; + boolean skipltgt = FALSE, gotnum = FALSE, + percent = (behavior == BL_TH_VAL_PERCENTAGE); + char *inp, *numstart; inbuf[0] = '\0'; Sprintf(buf, "Enter %svalue for %s threshold:", - (behavior == BL_TH_VAL_PERCENTAGE) ? "percentage " : "", + percent ? "percentage " : "", initblstats[fld].fldname); getlin(buf, inbuf); if (inbuf[0] == '\0' || inbuf[0] == '\033') goto choose_behavior; - inp = trimspaces(inbuf); + inp = numstart = trimspaces(inbuf); if (!*inp) goto choose_behavior; - /* allow user to enter "<50%" or ">50" or just "50" */ + /* allow user to enter "<50%" or ">50" or just "50" + or <=50% or >=50 or =50 */ if (*inp == '>' || *inp == '<' || *inp == '=') { - lt_gt_eq = (*inp == '>') ? GT_VALUE - : (*inp == '<') ? LT_VALUE : EQ_VALUE; + lt_gt_eq = (*inp == '>') ? ((inp[1] == '=') ? GE_VALUE : GT_VALUE) + : (*inp == '<') ? ((inp[1] == '=') ? LE_VALUE : LT_VALUE) + : EQ_VALUE; skipltgt = TRUE; - *inp = ' '; + *inp++ = ' '; + numstart++; + if (lt_gt_eq == GE_VALUE || lt_gt_eq == LE_VALUE) { + *inp++ = ' '; + numstart++; + } + } + if (*inp == '-') { inp++; + } else if (*inp == '+') { + *inp++ = ' '; numstart++; } while (digit(*inp)) { @@ -2796,11 +2867,12 @@ choose_value: gotnum = TRUE; } if (*inp == '%') { - behavior = BL_TH_VAL_PERCENTAGE; - *inp = '\0'; - } else if (!*inp) { - behavior = BL_TH_VAL_ABSOLUTE; - } else { + if (!percent) { + pline("Not expecting a percentage."); + goto choose_behavior; + } + *inp = '\0'; /* strip '%' [this accepts trailing junk!] */ + } else if (*inp) { /* some random characters */ pline("\"%s\" is not a recognized number.", inp); goto choose_value; @@ -2810,64 +2882,92 @@ choose_value: goto choose_value; } - val = atoi(numstart); - if (behavior == BL_TH_VAL_PERCENTAGE) { + aval = zeroany; + dt = percent ? ANY_INT : initblstats[fld].anytype; + (void) s_to_anything(&aval, numstart, dt); + + if (percent) { + val = aval.a_int; if (initblstats[fld].idxmax == -1) { pline("Field '%s' does not support percentage values.", initblstats[fld].fldname); behavior = BL_TH_VAL_ABSOLUTE; goto choose_value; } - if (val < 0 || val > 100) { - pline("Not a valid percent value."); + /* if player only specified a number then lt_gt_eq isn't set + up yet and the >-1 and <101 exceptions can't be honored; + deliberate use of those should be uncommon enough for + that to be palatable; unfortunately, it also lets 0 with + later < and 100 with later > through too but those won't + break anything, just end up as no-ops */ + if ((val < 0 && (val != -1 || lt_gt_eq != GT_VALUE)) + || (val == 0 && lt_gt_eq == LT_VALUE) + || (val == 100 && lt_gt_eq == GT_VALUE) + || (val > 100 && (val != 101 || lt_gt_eq != LT_VALUE))) { + pline("'%s%d%%' is not a valid percent value.", + (lt_gt_eq == LT_VALUE) ? "<" + : (lt_gt_eq == LE_VALUE) ? "<=" + : (lt_gt_eq == GT_VALUE) ? ">" + : (lt_gt_eq == GE_VALUE) ? ">=" + : (lt_gt_eq == EQ_VALUE) ? "=" + /* didn't specify lt_gt_eq with number */ + : "", + val); goto choose_value; } + /* restore suffix for use in color and attribute prompts */ + if (!index(numstart, '%')) + Strcat(numstart, "%"); } if (!skipltgt) { lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf); - if (lt_gt_eq == -2) + if (lt_gt_eq == NO_LTEQGT) goto choose_value; } - Sprintf(colorqry, "Choose a color for when %s is %s%s:", + Sprintf(colorqry, "Choose a color for when %s is %s%s%s:", initblstats[fld].fldname, + (lt_gt_eq == LT_VALUE) ? "less than " + : (lt_gt_eq == GT_VALUE) ? "more than " + : "", numstart, - (lt_gt_eq == EQ_VALUE) ? "" - : (lt_gt_eq == LT_VALUE) ? " or less" - : " or more"); - - Sprintf(attrqry, "Choose attribute for when %s is %s%s:", + (lt_gt_eq == LE_VALUE) ? " or less" + : (lt_gt_eq == GE_VALUE) ? " or more" + : ""); + Sprintf(attrqry, "Choose attribute for when %s is %s%s%s:", initblstats[fld].fldname, - inbuf, - (lt_gt_eq == EQ_VALUE) ? "" - : (lt_gt_eq == LT_VALUE) ? " or less" - : " or more"); + (lt_gt_eq == LT_VALUE) ? "less than " + : (lt_gt_eq == GT_VALUE) ? "more than " + : "", + numstart, + (lt_gt_eq == LE_VALUE) ? " or less" + : (lt_gt_eq == GE_VALUE) ? " or more" + : ""); hilite.rel = lt_gt_eq; - hilite.value.a_int = val; + hilite.value = aval; } else if (behavior == BL_TH_UPDOWN) { lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0); - if (lt_gt_eq == -2) + if (lt_gt_eq == NO_LTEQGT) goto choose_behavior; Sprintf(colorqry, "Choose a color for when %s %s:", initblstats[fld].fldname, (lt_gt_eq == EQ_VALUE) ? "changes" - : (lt_gt_eq == LT_VALUE) ? "decreases" - : "increases"); + : (lt_gt_eq == LT_VALUE) ? "decreases" + : "increases"); Sprintf(attrqry, "Choose attribute for when %s %s:", initblstats[fld].fldname, (lt_gt_eq == EQ_VALUE) ? "changes" - : (lt_gt_eq == LT_VALUE) ? "decreases" - : "increases"); + : (lt_gt_eq == LT_VALUE) ? "decreases" + : "increases"); hilite.rel = lt_gt_eq; } else if (behavior == BL_TH_CONDITION) { cond = query_conditions(); if (!cond) { if (origfld == BL_FLUSH) goto choose_field; - else - return FALSE; + return FALSE; } Sprintf(colorqry, "Choose a color for conditions %s:", conditionbitmask2str(cond)); @@ -2875,6 +2975,7 @@ choose_value: conditionbitmask2str(cond)); } else if (behavior == BL_TH_TEXTMATCH) { char qry_buf[BUFSZ]; + Sprintf(qry_buf, "%s %s text value to match:", (fld == BL_CAP || fld == BL_ALIGN @@ -2884,27 +2985,31 @@ choose_value: if (fld == BL_CAP) { int rv = query_arrayvalue(qry_buf, enc_stat, - SLT_ENCUMBER, OVERLOADED+1); + SLT_ENCUMBER, OVERLOADED + 1); + if (rv < SLT_ENCUMBER) goto choose_behavior; hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, enc_stat[rv]); } else if (fld == BL_ALIGN) { - const char *aligntxt[] = {"chaotic", "neutral", "lawful"}; + static const char *aligntxt[] = { "chaotic", "neutral", "lawful" }; int rv = query_arrayvalue(qry_buf, - aligntxt, 0, 3); + aligntxt, 0, 2 + 1); + if (rv < 0) goto choose_behavior; hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, aligntxt[rv]); } else if (fld == BL_HUNGER) { - const char *hutxt[] = {"Satiated", (char *)0, "Hungry", "Weak", - "Fainting", "Fainted", "Starved"}; + static const char *hutxt[] = { "Satiated", (char *) 0, "Hungry", + "Weak", "Fainting", "Fainted", + "Starved" }; int rv = query_arrayvalue(qry_buf, hutxt, - SATIATED, STARVED+1); + SATIATED, STARVED + 1); + if (rv < SATIATED) goto choose_behavior; @@ -2925,7 +3030,7 @@ choose_value: hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, rolelist[rv]); } else { - char inbuf[BUFSZ] = DUMMY; + char inbuf[BUFSZ]; inbuf[0] = '\0'; getlin(qry_buf, inbuf); @@ -2933,7 +3038,7 @@ choose_value: goto choose_behavior; hilite.rel = TXT_VALUE; - if (strlen(inbuf) < QBUFSZ-1) + if (strlen(inbuf) < QBUFSZ - 1) Strcpy(hilite.textmatch, inbuf); else return FALSE; @@ -2950,7 +3055,6 @@ choose_value: } choose_color: - clr = query_color(colorqry); if (clr == -1) { if (behavior != BL_TH_ALWAYS_HILITE) @@ -2984,6 +3088,7 @@ choose_color: char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; char *tmpattr; + if (atr == HL_DIM) cond_hilites[HL_ATTCLR_DIM] |= cond; else if (atr == HL_BLINK) From 6311985d57b0e6eff59fd73cee2e6f1bb749292d Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 21 May 2018 09:35:38 -0400 Subject: [PATCH 14/23] don't highlight the leading space before gold field on the status line --- doc/fixes36.2 | 2 ++ win/tty/wintty.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index f22af782e..cfe7190f8 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -23,6 +23,8 @@ targetting with a polearm could give away location of hidden monster Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository ------------------------------------------------------------------ fix access violation when --debug:xxxx has no other args after it +Setting the inverse attribute for gold had the space before "$:" + getting highlighted along with the gold field Platform- and/or Interface-Specific Fixes diff --git a/win/tty/wintty.c b/win/tty/wintty.c index fc40dcc75..42dad00c2 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1526429383 2018/05/16 00:09:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.166 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1526909614 2018/05/21 13:33:34 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.167 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -4175,6 +4175,10 @@ render_status(VOID_ARGS) * +-----------+ */ if (iflags.hilite_delta) { + if (*text == ' ') { + tty_putstatusfield(nullfield, " ", x++, y); + text++; + } /* multiple attributes can be in effect concurrently */ Begin_Attr(attridx); if (coloridx != NO_COLOR && coloridx != CLR_MAX) From 8f73f85ac4a1fab9fe384959443fa8935a2575a4 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 21 May 2018 10:14:39 -0400 Subject: [PATCH 15/23] static prototype could be left orphaned depending on #defines --- src/rip.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rip.c b/src/rip.c index ee2a51642..6e48dde45 100644 --- a/src/rip.c +++ b/src/rip.c @@ -5,8 +5,6 @@ #include "hack.h" -STATIC_DCL void FDECL(center, (int, char *)); - #if defined(TTY_GRAPHICS) || defined(X11_GRAPHICS) || defined(GEM_GRAPHICS) \ || defined(MSWIN_GRAPHICS) || defined(DUMPLOG) #define TEXT_TOMBSTONE @@ -18,6 +16,7 @@ STATIC_DCL void FDECL(center, (int, char *)); #endif #ifdef TEXT_TOMBSTONE +STATIC_DCL void FDECL(center, (int, char *)); #ifndef NH320_DEDICATION /* A normal tombstone for end of game display. */ From fcc26f2c445582278e9c0e13e961853770f6b4bf Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 21 May 2018 10:20:17 -0400 Subject: [PATCH 16/23] fixes update for previous static prototype move in rip.c --- doc/fixes36.2 | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 59b558e98..3c15cf873 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -18,6 +18,7 @@ permanent inventory window was updated too soon when a scroll of charging was used to [re]charge an item, not reflecting the item's change(s) for starting inventory, don't give an orc hero lembas wafers or cram rations targetting with a polearm could give away location of hidden monster +static prototype could be left orphaned depending on #defines in rip.c Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository From d95dae3cf485188ad1ba2cab3bed5a490760750c Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 21 May 2018 16:39:16 -0400 Subject: [PATCH 17/23] fix early crash during config file error processing --- doc/fixes36.2 | 2 ++ src/windows.c | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 3c15cf873..2cdd77fdc 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -19,6 +19,8 @@ permanent inventory window was updated too soon when a scroll of charging for starting inventory, don't give an orc hero lembas wafers or cram rations targetting with a polearm could give away location of hidden monster static prototype could be left orphaned depending on #defines in rip.c +config file error handling routines were calling xx_wait_synch early + even before the window system was initialized; add a default routine Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/windows.c b/src/windows.c index 9a82325d9..96d9a218d 100644 --- a/src/windows.c +++ b/src/windows.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 windows.c $NHDT-Date: 1495232365 2017/05/19 22:19:25 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.41 $ */ +/* NetHack 3.6 windows.c $NHDT-Date: 1526933747 2018/05/21 20:15:47 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.48 $ */ /* Copyright (c) D. Cohrs, 1993. */ /* NetHack may be freely redistributed. See license for details. */ @@ -56,6 +56,7 @@ extern void *FDECL(trace_procs_chain, (int, int, void *, void *, void *)); #endif STATIC_DCL void FDECL(def_raw_print, (const char *s)); +STATIC_DCL void NDECL(def_wait_synch); #ifdef DUMPLOG STATIC_DCL winid FDECL(dump_create_nhwindow, (int)); @@ -191,6 +192,22 @@ const char *s; puts(s); } +STATIC_OVL +void +def_wait_synch(VOID_ARGS) +{ + /* Config file error handling routines + * call wait_sync() without checking to + * see if it actually has a value, + * leading to spectacular violations + * when you try to execute address zero. + * The existence of this allows early + * processing to have something to execute + * even though it essentially does nothing + */ + return; +} + #ifdef WINCHAIN static struct win_choices * win_choices_find(s) @@ -232,6 +249,9 @@ const char *s; if (!windowprocs.win_raw_print) windowprocs.win_raw_print = def_raw_print; + if (!windowprocs.win_wait_synch) + /* early config file error processing routines call this */ + windowprocs.win_wait_synch = def_wait_synch; if (!winchoices[0].procs) { raw_printf("No window types?"); From 81e422189cb13a906b98781c9964615aacb994b0 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 21 May 2018 16:44:19 -0400 Subject: [PATCH 18/23] Windows wouldn't compile without TTY_GRAPHICS defined --- src/cmd.c | 4 ++- src/options.c | 4 ++- sys/share/pcmain.c | 13 ++++---- sys/share/pcsys.c | 9 ++++++ sys/share/pcunix.c | 9 +++--- sys/winnt/nttty.c | 80 +++++++++++++++++++++++++--------------------- sys/winnt/stubs.c | 4 +++ sys/winnt/winnt.c | 12 +++++-- 8 files changed, 84 insertions(+), 51 deletions(-) diff --git a/src/cmd.c b/src/cmd.c index 9c4739a42..d7303ec18 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -5354,8 +5354,10 @@ dotravel(VOID_ARGS) } #ifdef PORT_DEBUG +#if defined(WIN32) && defined(TTY_GRAPHICS) extern void NDECL(win32con_debug_keystrokes); extern void NDECL(win32con_handler_info); +#endif int wiz_port_debug() @@ -5369,7 +5371,7 @@ wiz_port_debug() char *menutext; void NDECL((*fn)); } menu_selections[] = { -#ifdef WIN32 +#if defined(WIN32) && defined(TTY_GRAPHICS) { "test win32 keystrokes (tty only)", win32con_debug_keystrokes }, { "show keystroke handler information (tty only)", win32con_handler_info }, diff --git a/src/options.c b/src/options.c index e61eaa906..fbe568fb2 100644 --- a/src/options.c +++ b/src/options.c @@ -2651,7 +2651,7 @@ boolean tinitial, tfrom_file; bad_negation(fullname, FALSE); return FALSE; } else if ((op = string_for_opt(opts, negated)) != 0) { -#if defined(WIN32CON) +#if defined(WIN32) && defined(TTY_GRAPHICS) set_altkeyhandler(op); #endif } else @@ -3304,7 +3304,9 @@ boolean tinitial, tfrom_file; op = string_for_opt(opts, 0); if (!op) return FALSE; +#ifdef TTY_GRAPHICS map_subkeyvalue(op); +#endif #endif } return retval; diff --git a/sys/share/pcmain.c b/sys/share/pcmain.c index 1b38ab951..cfb7dbf8a 100644 --- a/sys/share/pcmain.c +++ b/sys/share/pcmain.c @@ -310,7 +310,9 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ #endif #ifdef WIN32 save_getreturn_status = getreturn_enabled; +#ifdef TTY_GRAPHICS raw_clear_screen(); +#endif getreturn_enabled = TRUE; check_recordfile((char *) 0); #endif @@ -484,6 +486,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ NHWinMainInit(); else */ +#ifdef TTY_GRAPHICS if (!strncmpi(windowprocs.name, "tty", 3)) { iflags.use_background_glyph = FALSE; nttty_open(1); @@ -492,6 +495,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ } #endif #endif +#endif #if defined(MSDOS) || defined(WIN32) /* Player didn't specify any symbol set so use IBM defaults */ @@ -510,7 +514,7 @@ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);*/ process_options(argc, argv); #endif -#ifdef WIN32 +#if defined(WIN32) && defined(TTY_GRAPHICS) toggle_mouse_support(); /* must come after process_options */ #endif @@ -791,14 +795,11 @@ char *argv[]; #endif #ifdef WIN32 case 'w': /* windowtype */ +#ifdef TTY_GRAPHICS if (strncmpi(&argv[0][2], "tty", 3)) { nttty_open(1); } - /* - else { - NHWinMainInit(); - } - */ +#endif config_error_init(FALSE, "command line", FALSE); choose_windows(&argv[0][2]); config_error_done(); diff --git a/sys/share/pcsys.c b/sys/share/pcsys.c index 71b62c6e0..cb5650af6 100644 --- a/sys/share/pcsys.c +++ b/sys/share/pcsys.c @@ -501,6 +501,15 @@ int code; exit(code); } +#ifdef WIN32 +#ifndef TTY_GRAPHICS +void +synch_cursor() +{ +} +#endif +#endif + /* Chdir back to original directory */ #ifdef TOS diff --git a/sys/share/pcunix.c b/sys/share/pcunix.c index 72daafe10..22bc4ae4e 100644 --- a/sys/share/pcunix.c +++ b/sys/share/pcunix.c @@ -15,9 +15,10 @@ #if defined(WIN32) || defined(MSDOS) extern char orgdir[]; -#ifdef WIN32 -extern void NDECL(backsp); #endif + +#if defined(WIN32) && defined(TTY_GRAPHICS) +extern void NDECL(backsp); extern void NDECL(clear_screen); #endif @@ -196,7 +197,7 @@ getlock() if (c == 'y' || c == 'Y') #ifndef SELF_RECOVER if (eraseoldlocks()) { -#if defined(WIN32) +#if defined(WIN32) && defined(TTY_GRAPHICS) if (!strncmpi(windowprocs.name, "tty", 3)) clear_screen(); /* display gets fouled up otherwise */ #endif @@ -210,7 +211,7 @@ getlock() } #else /*SELF_RECOVER*/ if (recover_savefile()) { -#if defined(WIN32) +#if defined(WIN32) && defined(TTY_GRAPHICS) if (!strncmpi(windowprocs.name, "tty", 3)) clear_screen(); /* display gets fouled up otherwise */ #endif diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index f7417adbe..e983d36fb 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -13,6 +13,7 @@ * */ + #ifdef WIN32 #define NEED_VARARGS /* Uses ... */ #include "hack.h" @@ -21,6 +22,10 @@ #include #include "win32api.h" +extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ +extern int redirect_stdout; + +#ifdef TTY_GRAPHICS /* * Console Buffer Flipping Support * @@ -84,9 +89,6 @@ INPUT_RECORD ir; /* Support for changing console font if existing glyph widths are too wide */ -extern boolean getreturn_enabled; /* from sys/share/pcsys.c */ -extern int redirect_stdout; - /* Flag for whether NetHack was launched via the GUI, not the command line. * The reason we care at all, is so that we can get * a final RETURN at the end of the game when launched from the GUI @@ -1078,40 +1080,6 @@ void set_altkeyhandler(const char * inName) return; } -/* this is used as a printf() replacement when the window - * system isn't initialized yet - */ -void msmsg -VA_DECL(const char *, fmt) -{ - char buf[ROWNO * COLNO]; /* worst case scenario */ - VA_START(fmt); - VA_INIT(fmt, const char *); - Vsprintf(buf, fmt, VA_ARGS); - if (redirect_stdout) - fprintf(stdout, "%s", buf); - else { - if(!init_ttycolor_completed) - init_ttycolor(); - - /* if we have generated too many messages ... ask the user to - * confirm and then clear. - */ - if (console.cursor.Y > console.height - 4) { - xputs("Hit to continue."); - while (pgetchar() != '\n') - ; - raw_clear_screen(); - set_console_cursor(1, 0); - } - - xputs(buf); - if (ttyDisplay) - curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y); - } - VA_END(); - return; -} /* fatal error */ /*VARARGS1*/ @@ -1912,5 +1880,43 @@ void nethack_enter_nttty() error("Unable to load nhraykey.dll"); } } +#endif TTY_GRAPHICS + +/* this is used as a printf() replacement when the window + * system isn't initialized yet + */ +void msmsg +VA_DECL(const char *, fmt) +{ + char buf[ROWNO * COLNO]; /* worst case scenario */ + VA_START(fmt); + VA_INIT(fmt, const char *); + Vsprintf(buf, fmt, VA_ARGS); + if (redirect_stdout) + fprintf(stdout, "%s", buf); + else { +#ifdef TTY_GRAPHICS + if(!init_ttycolor_completed) + init_ttycolor(); + /* if we have generated too many messages ... ask the user to + * confirm and then clear. + */ + if (console.cursor.Y > console.height - 4) { + xputs("Hit to continue."); + while (pgetchar() != '\n') + ; + raw_clear_screen(); + set_console_cursor(1, 0); + } + xputs(buf); + if (ttyDisplay) + curs(BASE_WINDOW, console.cursor.X + 1, console.cursor.Y); +#else + fprintf(stdout, "%s", buf); +#endif + } + VA_END(); + return; +} #endif /* WIN32 */ diff --git a/sys/winnt/stubs.c b/sys/winnt/stubs.c index ef161ae9b..2a0d10e79 100644 --- a/sys/winnt/stubs.c +++ b/sys/winnt/stubs.c @@ -96,11 +96,13 @@ clear_screen() return; } +#ifdef TTY_GRAPHICS void backsp() { return; } +#endif int has_color(int color) @@ -158,11 +160,13 @@ VA_DECL(const char *, s) return; } +#ifdef TTY_GRAPHICS void synch_cursor() { return; } +#endif void more() diff --git a/sys/winnt/winnt.c b/sys/winnt/winnt.c index 067ba5abd..451aae2f0 100644 --- a/sys/winnt/winnt.c +++ b/sys/winnt/winnt.c @@ -17,7 +17,9 @@ #endif #include #include "win32api.h" +#ifdef TTY_GRAPHICS #include "wintty.h" +#endif #ifdef WIN32 /* @@ -227,7 +229,14 @@ Delay(int ms) (void) Sleep(ms); } +#ifdef TTY_GRAPHICS extern void NDECL(backsp); +#else +void +backsp() +{ +} +#endif void win32_abort() @@ -481,11 +490,10 @@ void ntassert_failed(const char * exp, const char * file, int line) /* nethack_enter_winnt() is the first thing called from main */ void nethack_enter_winnt() { -#ifdef WIN32CON +#ifdef TTY_GRAPHICS nethack_enter_nttty(); #endif } - #endif /* WIN32 */ /*winnt.c*/ From f614b9e27e9b953fe715825ef88bce93b36e56c7 Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 21 May 2018 18:31:45 -0400 Subject: [PATCH 19/23] fix access violation in status_finish() if window port not initialized --- doc/fixes36.2 | 3 +++ src/botl.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 2cdd77fdc..705de5ca3 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -21,6 +21,9 @@ targetting with a polearm could give away location of hidden monster static prototype could be left orphaned depending on #defines in rip.c config file error handling routines were calling xx_wait_synch early even before the window system was initialized; add a default routine +status_finish() in botl.c would unconditionally invoke the window port's + win_status_finish() routine which was problematic if the windowport + wasn't initialized yet Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/botl.c b/src/botl.c index c219d3db3..8576d39e2 100644 --- a/src/botl.c +++ b/src/botl.c @@ -876,7 +876,8 @@ status_finish() int i; /* call the window port cleanup routine first */ - (*windowprocs.win_status_finish)(); + if (windowprocs.win_status_finish) + (*windowprocs.win_status_finish)(); /* free memory that we alloc'd now */ for (i = 0; i < MAXBLSTATS; ++i) { From 2b8160a70ddc51f0b4e64a9d38a301de0e4f5fc0 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 21 May 2018 19:11:18 -0700 Subject: [PATCH 20/23] status_hilite: no more ANY_UINT The fact that the index to the array of hunger strings is an unsigned field in 'struct you' is unimportant as far as its usage for status highlighting. Since it is the only ANY_UINT field, change BL_HUNGER to plain 'int' so that there'll be no need for ANY_UINT handling. And some more validation when setting up highlight rules. For 'O', in the menu to choose a relationship after supplying a number N, don't include "less than N" and "N or less" for percentage or absolute--other than AC--unless N is greater than 0, and don't include "N or more" and "more than N" for percentage unless N < 100. Also, when 'O' prompted for a number, if you entered =', &c) when reprompting for a valid number. If the 'X' portion is invalid, discard the relationship operator before asking for another number. --- src/botl.c | 183 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 105 insertions(+), 78 deletions(-) diff --git a/src/botl.c b/src/botl.c index 8576d39e2..02fdefec8 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526907473 2018/05/21 12:57:53 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.96 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1526955073 2018/05/22 02:11:13 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.98 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -462,8 +462,8 @@ STATIC_DCL void NDECL(status_hilite_linestr_gather); STATIC_DCL char *FDECL(status_hilite2str, (struct hilite_s *)); STATIC_DCL int NDECL(status_hilite_menu_choose_field); STATIC_DCL int FDECL(status_hilite_menu_choose_behavior, (int)); -STATIC_DCL int FDECL(status_hilite_menu_choose_updownboth, (int, - const char *)); +STATIC_DCL int FDECL(status_hilite_menu_choose_updownboth, (int, const char *, + BOOLEAN_P, BOOLEAN_P)); STATIC_DCL boolean FDECL(status_hilite_menu_add, (int)); #define has_hilite(i) (blstats[0][(i)].thresholds) #endif @@ -494,7 +494,8 @@ STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = { INIT_BLSTAT("armor-class", " AC:%s", ANY_INT, 10, BL_AC), INIT_BLSTAT("HD", " HD:%s", ANY_INT, 10, BL_HD), INIT_BLSTAT("time", " T:%s", ANY_LONG, 20, BL_TIME), - INIT_BLSTAT("hunger", " %s", ANY_UINT, 40, BL_HUNGER), + /* hunger used to be 'ANY_UINT'; see note below in bot_via_windowport() */ + INIT_BLSTAT("hunger", " %s", ANY_INT, 40, BL_HUNGER), INIT_BLSTATP("hitpoints", " HP:%s", ANY_INT, 10, BL_HPMAX, BL_HP), INIT_BLSTAT("hitpoints-max", "(%s)", ANY_INT, 10, BL_HPMAX), INIT_BLSTAT("dungeon-level", "%s", ANY_STR, 80, BL_LEVELDESC), @@ -639,7 +640,11 @@ bot_via_windowport() blstats[idx][BL_TIME].a.a_long = moves; /* Hunger */ - blstats[idx][BL_HUNGER].a.a_uint = u.uhs; + /* note: u.uhs is unsigned, and 3.6.1's STATUS_HILITE defined + BL_HUNGER to be ANY_UINT, but that was the only non-int/non-long + numeric field so it's far simpler to treat it as plain int and + not need ANY_UINT handling at all */ + blstats[idx][BL_HUNGER].a.a_int = (int) u.uhs; Strcpy(blstats[idx][BL_HUNGER].val, (u.uhs != NOT_HUNGRY) ? hu_stat[u.uhs] : ""); valset[BL_HUNGER] = TRUE; @@ -1220,6 +1225,10 @@ static struct fieldid_t { {0, BL_FLUSH} }; +/* format arguments */ +static const char threshold_value[] = "hilite_status threshold ", + is_out_of_range[] = " is out of range"; + /* field name to bottom line index */ STATIC_OVL enum statusfields fldname_to_bl_indx(name) @@ -1724,7 +1733,7 @@ boolean from_configfile; int coloridx = -1, successes = 0; int disp_attrib = 0; boolean percent, changed, numeric, down, up, - gt, lt, ge, le, eq, txtval, always; + gt, lt, ge, le, eq, txtval, always; const char *txt; enum statusfields fld = BL_FLUSH; struct hilite_s hilite; @@ -1814,6 +1823,8 @@ boolean from_configfile; } else if (!strcmpi(s[sidx], "changed")) { changed = TRUE; } else if (is_ltgt_percentnumber(s[sidx])) { + const char *op; + tmp = s[sidx]; /* is_ltgt_() guarantees [<>]?=?[-+]?[0-9]+%? */ if (strchr(tmp, '%')) percent = TRUE; @@ -1835,33 +1846,26 @@ boolean from_configfile; numeric = TRUE; dt = percent ? ANY_INT : initblstats[fld].anytype; (void) s_to_anything(&hilite.value, tmp, dt); + + op = gt ? ">" : ge ? ">=" : lt ? "<" : le ? "<=" : "="; if (dt == ANY_INT /* AC is the only field where negative values make sense but - accept >-1 for other fields */ - && (hilite.value.a_int < (fld == BL_AC ? -128 : gt ? -1 : 0) + accept >-1 for other fields; reject <0 for non-AC */ + && (hilite.value.a_int + < ((fld == BL_AC) ? -128 : gt ? -1 : lt ? 1 : 0) /* percentages have another more comprehensive check below */ || hilite.value.a_int > (percent ? (lt ? 101 : 100) : LARGEST_INT))) { - config_error_add( - "hilite_status threshold '%s%d%s' is out of range", - gt ? ">" : ge ? ">=" - : lt ? "<" : le ? "<=" : "=", - hilite.value.a_int, - percent ? "%" : ""); - return FALSE; + config_error_add("%s'%s%d%s'%s", threshold_value, + op, hilite.value.a_int, percent ? "%" : "", + is_out_of_range); + return FALSE; + } else if (dt == ANY_LONG + && (hilite.value.a_long < (gt ? -1L : lt ? 1L : 0L))) { + config_error_add("%s'%s%ld'%s", threshold_value, + op, hilite.value.a_long, is_out_of_range); + return FALSE; } - /* - * Note: the only check for ANY_LONG would be - * if (hilite.value.a_long < (gt ? -1L : 0L)) { } - * so we might as well skip it instead of replicating - * the above config_error_add() for hilite.value.a_long. - * The only non-int/non-long numeric is BL_HUNGER - * (unsigned: ANY_UINT) and it should be changed to int - * instead of trying to support one oddball field, - * particularly since users are encouraged to use the - * "satiated"/"hungry"/"weak"/&c strings instead of - * their internal numeric values. - */ } else if (initblstats[fld].anytype == ANY_STR) { txt = s[sidx]; txtval = TRUE; @@ -2658,7 +2662,7 @@ int fld; } if (fld != BL_CAP && fld != BL_HUNGER - && (at == ANY_INT || at == ANY_LONG || at == ANY_UINT)) { + && (at == ANY_INT || at == ANY_LONG)) { any = zeroany; any.a_int = onlybeh = BL_TH_VAL_ABSOLUTE; add_menu(tmpwin, NO_GLYPH, &any, 'n', 0, ATR_NONE, @@ -2704,9 +2708,10 @@ int fld; } STATIC_OVL int -status_hilite_menu_choose_updownboth(fld, str) +status_hilite_menu_choose_updownboth(fld, str, ltok, gtok) int fld; const char *str; +boolean ltok, gtok; { int res, ret = NO_LTEQGT; winid tmpwin; @@ -2717,23 +2722,25 @@ const char *str; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); - if (str) - Sprintf(buf, "%s than %s", - (fld == BL_AC) ? "Better (lower)" : "Less", str); - else - Sprintf(buf, "Value goes down"); - any = zeroany; - any.a_int = 10 + LT_VALUE; - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - buf, MENU_UNSELECTED); - - if (str) { - Sprintf(buf, "%s or %s", - str, (fld == BL_AC) ? "better (lower)" : "less"); + if (ltok) { + if (str) + Sprintf(buf, "%s than %s", + (fld == BL_AC) ? "Better (lower)" : "Less", str); + else + Sprintf(buf, "Value goes down"); any = zeroany; - any.a_int = 10 + LE_VALUE; + any.a_int = 10 + LT_VALUE; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); + + if (str) { + Sprintf(buf, "%s or %s", + str, (fld == BL_AC) ? "better (lower)" : "less"); + any = zeroany; + any.a_int = 10 + LE_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } } if (str) @@ -2745,25 +2752,26 @@ const char *str; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); - if (str) { - Sprintf(buf, "%s or %s", - str, (fld == BL_AC) ? "worse (higher)" : "more"); + if (gtok) { + if (str) { + Sprintf(buf, "%s or %s", + str, (fld == BL_AC) ? "worse (higher)" : "more"); + any = zeroany; + any.a_int = 10 + GE_VALUE; + add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, + buf, MENU_UNSELECTED); + } + + if (str) + Sprintf(buf, "%s than %s", + (fld == BL_AC) ? "Worse (higher)" : "More", str); + else + Sprintf(buf, "Value goes up"); any = zeroany; - any.a_int = 10 + GE_VALUE; + any.a_int = 10 + GT_VALUE; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, - buf, MENU_UNSELECTED); - } - - if (str) - Sprintf(buf, "%s than %s", - (fld == BL_AC) ? "Worse (higher)" : "More", str); - else - Sprintf(buf, "Value goes up"); - any = zeroany; - any.a_int = 10 + GT_VALUE; - add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); - + } Sprintf(buf, "Select field %s value:", initblstats[fld].fldname); end_menu(tmpwin, buf); @@ -2783,7 +2791,7 @@ int origfld; { int fld; int behavior; - int lt_gt_eq = NO_LTEQGT; /* not set up yet */ + int lt_gt_eq; int clr = NO_COLOR, atr = HL_UNDEF; struct hilite_s hilite; unsigned long cond = 0UL; @@ -2827,10 +2835,11 @@ choose_value: char inbuf[BUFSZ], buf[BUFSZ]; anything aval; int val, dt; - boolean skipltgt = FALSE, gotnum = FALSE, - percent = (behavior == BL_TH_VAL_PERCENTAGE); + boolean gotnum = FALSE, percent = (behavior == BL_TH_VAL_PERCENTAGE); char *inp, *numstart; + const char *op; + lt_gt_eq = NO_LTEQGT; /* not set up yet */ inbuf[0] = '\0'; Sprintf(buf, "Enter %svalue for %s threshold:", percent ? "percentage " : "", @@ -2849,7 +2858,6 @@ choose_value: lt_gt_eq = (*inp == '>') ? ((inp[1] == '=') ? GE_VALUE : GT_VALUE) : (*inp == '<') ? ((inp[1] == '=') ? LE_VALUE : LT_VALUE) : EQ_VALUE; - skipltgt = TRUE; *inp++ = ' '; numstart++; if (lt_gt_eq == GE_VALUE || lt_gt_eq == LE_VALUE) { @@ -2882,6 +2890,12 @@ choose_value: pline("Is that an invisible number?"); goto choose_value; } + op = (lt_gt_eq == LT_VALUE) ? "<" + : (lt_gt_eq == LE_VALUE) ? "<=" + : (lt_gt_eq == GT_VALUE) ? ">" + : (lt_gt_eq == GE_VALUE) ? ">=" + : (lt_gt_eq == EQ_VALUE) ? "=" + : ""; /* didn't specify lt_gt_eq with number */ aval = zeroany; dt = percent ? ANY_INT : initblstats[fld].anytype; @@ -2898,31 +2912,43 @@ choose_value: /* if player only specified a number then lt_gt_eq isn't set up yet and the >-1 and <101 exceptions can't be honored; deliberate use of those should be uncommon enough for - that to be palatable; unfortunately, it also lets 0 with - later < and 100 with later > through too but those won't - break anything, just end up as no-ops */ + that to be palatable; for 0 and 100, choose_updown_both() + will prevent useless operations */ if ((val < 0 && (val != -1 || lt_gt_eq != GT_VALUE)) || (val == 0 && lt_gt_eq == LT_VALUE) || (val == 100 && lt_gt_eq == GT_VALUE) || (val > 100 && (val != 101 || lt_gt_eq != LT_VALUE))) { - pline("'%s%d%%' is not a valid percent value.", - (lt_gt_eq == LT_VALUE) ? "<" - : (lt_gt_eq == LE_VALUE) ? "<=" - : (lt_gt_eq == GT_VALUE) ? ">" - : (lt_gt_eq == GE_VALUE) ? ">=" - : (lt_gt_eq == EQ_VALUE) ? "=" - /* didn't specify lt_gt_eq with number */ - : "", - val); + pline("'%s%d%%' is not a valid percent value.", op, val); goto choose_value; } /* restore suffix for use in color and attribute prompts */ if (!index(numstart, '%')) Strcat(numstart, "%"); + + /* reject negative values except for AC and >-1; reject 0 for < */ + } else if (dt == ANY_INT + && (aval.a_int < ((fld == BL_AC) ? -128 + : (lt_gt_eq == GT_VALUE) ? -1 + : (lt_gt_eq == LT_VALUE) ? 1 : 0))) { + pline("%s'%s%d'%s", threshold_value, + op, aval.a_int, is_out_of_range); + goto choose_value; + } else if (dt == ANY_LONG + && (aval.a_long < (lt_gt_eq == GT_VALUE) ? -1L + : (lt_gt_eq == LT_VALUE) ? 1L : 0L)) { + pline("%s'%s%ld'%s", threshold_value, + op, aval.a_long, is_out_of_range); + goto choose_value; } - if (!skipltgt) { - lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf); + if (lt_gt_eq == NO_LTEQGT) { + boolean ltok = ((dt == ANY_INT) + ? (aval.a_int > 0 || fld == BL_AC) + : (aval.a_long > 0L)), + gtok = (!percent || aval.a_long < 100); + + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, inbuf, + ltok, gtok); if (lt_gt_eq == NO_LTEQGT) goto choose_value; } @@ -2949,7 +2975,8 @@ choose_value: hilite.rel = lt_gt_eq; hilite.value = aval; } else if (behavior == BL_TH_UPDOWN) { - lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0); + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0, + TRUE, TRUE); if (lt_gt_eq == NO_LTEQGT) goto choose_behavior; Sprintf(colorqry, "Choose a color for when %s %s:", From 1e0f54674035d6a489740a51c3b3243181b3a928 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 22 May 2018 02:42:08 -0700 Subject: [PATCH 21/23] hilite_status string comparison Simplify the string comparison done when checking 'textmatch' rules to decide whether to highlight something. Fix the menu titles when setting up a textmatch via 'O': the title for color referred to attribute and the one for attribute used the default. The two tiles are set up in advance; the one for color was set correctly but then the one for attribute was written into the wrong buffer. When using 'O' to manipulate hilite_status rules, if there are any when you're done and the 'statushilites' option (iflags.hilite_delta) is 0, give a message reminding that it needs to be non-zero for highlighting to be activated. --- doc/fixes36.2 | 6 +++++ src/botl.c | 70 +++++++++++++++++++++++++-------------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 705de5ca3..802ce2507 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -24,6 +24,12 @@ config file error handling routines were calling xx_wait_synch early status_finish() in botl.c would unconditionally invoke the window port's win_status_finish() routine which was problematic if the windowport wasn't initialized yet +using 'O' to set up a hilite_status rule for string comparison, the menu for + color was titled "choose attribute for when is 'bar'" and the + one prompting for attribute used the default "pick an attribute" +when finishing using 'O' to examine or set hilite_status rules, if the + 'statushilites' option is 0 and there is at least one rule, give a + reminder about setting it to non-zero to activate highlighting Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/botl.c b/src/botl.c index 02fdefec8..fdc25ae01 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526955073 2018/05/22 02:11:13 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1526982122 2018/05/22 09:42:02 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.99 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1363,7 +1363,7 @@ int *colorptr; int bestcolor = NO_COLOR; struct hilite_s *hl; anything *value = (anything *) vp; - char *txtstr, *cmpstr; + char *txtstr; if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS) return; @@ -1457,19 +1457,13 @@ int *colorptr; } break; case BL_TH_TEXTMATCH: - txtstr = dupstr(blstats[idx][fldidx].val); - cmpstr = txtstr; - if (fldidx == BL_TITLE) { - int len = (int) (strlen(plname) + sizeof (" the")); - - cmpstr += len; - } - (void) trimspaces(cmpstr); - if (hl->rel == TXT_VALUE && hl->textmatch[0] && - !strcmpi(hl->textmatch, cmpstr)) { + txtstr = blstats[idx][fldidx].val; + if (fldidx == BL_TITLE) + txtstr += (strlen(plname) + sizeof " the " - sizeof ""); + if (hl->rel == TXT_VALUE && hl->textmatch[0] + && fuzzymatch(hl->textmatch, txtstr, " -_", TRUE)) { merge_bestcolor(&bestcolor, hl->coloridx); } - free(txtstr); break; case BL_TH_ALWAYS_HILITE: merge_bestcolor(&bestcolor, hl->coloridx); @@ -2337,8 +2331,7 @@ struct _status_hilite_line_str { struct _status_hilite_line_str *next; }; -struct _status_hilite_line_str *status_hilite_str = - (struct _status_hilite_line_str *) 0; +static struct _status_hilite_line_str *status_hilite_str = 0; static int status_hilite_str_id = 0; STATIC_OVL void @@ -2348,22 +2341,20 @@ struct hilite_s *hl; unsigned long mask; const char *str; { - struct _status_hilite_line_str *tmp = (struct _status_hilite_line_str *) - alloc(sizeof(struct _status_hilite_line_str)); - struct _status_hilite_line_str *nxt = status_hilite_str; + struct _status_hilite_line_str *tmp, *nxt; - (void) memset(tmp, 0, sizeof(struct _status_hilite_line_str)); + tmp = (struct _status_hilite_line_str *) alloc(sizeof *tmp); + (void) memset(tmp, 0, sizeof *tmp); ++status_hilite_str_id; tmp->fld = fld; tmp->hl = hl; tmp->mask = mask; (void) stripchars(tmp->str, " ", str); - tmp->id = status_hilite_str_id; - if (nxt) { - while (nxt && nxt->next) + if ((nxt = status_hilite_str) != 0) { + while (nxt->next) nxt = nxt->next; nxt->next = tmp; } else { @@ -2375,8 +2366,7 @@ const char *str; STATIC_OVL void status_hilite_linestr_done() { - struct _status_hilite_line_str *tmp = status_hilite_str; - struct _status_hilite_line_str *nxt; + struct _status_hilite_line_str *nxt, *tmp = status_hilite_str; while (tmp) { nxt = tmp->next; @@ -2406,6 +2396,7 @@ int count_status_hilites(VOID_ARGS) { int count; + status_hilite_linestr_gather(); count = status_hilite_linestr_countfield(BL_FLUSH); status_hilite_linestr_done(); @@ -2421,13 +2412,14 @@ status_hilite_linestr_gather_conditions() unsigned long clratr; } cond_maps[SIZE(valid_conditions)]; - (void)memset(cond_maps, 0, - sizeof(struct _cond_map) * SIZE(valid_conditions)); + (void) memset(cond_maps, 0, + SIZE(valid_conditions) * sizeof (struct _cond_map)); for (i = 0; i < SIZE(valid_conditions); i++) { int clr = NO_COLOR; int atr = HL_NONE; int j; + for (j = 0; j < CLR_MAX; j++) if (cond_hilites[j] & valid_conditions[i].bitmask) clr = j; @@ -2445,6 +2437,7 @@ status_hilite_linestr_gather_conditions() if (clr != NO_COLOR || atr != HL_NONE) { unsigned long ca = clr | (atr << 8); boolean added_condmap = FALSE; + for (j = 0; j < SIZE(valid_conditions); j++) if (cond_maps[j].clratr == ca) { cond_maps[j].bm |= valid_conditions[i].bitmask; @@ -2465,12 +2458,14 @@ status_hilite_linestr_gather_conditions() for (i = 0; i < SIZE(valid_conditions); i++) if (cond_maps[i].bm) { int clr = NO_COLOR, atr = HL_NONE; + split_clridx(cond_maps[i].clratr, &clr, &atr); if (clr != NO_COLOR || atr != HL_NONE) { char clrbuf[BUFSZ]; char attrbuf[BUFSZ]; char condbuf[BUFSZ]; char *tmpattr; + (void) stripchars(clrbuf, " ", clr2colorname(clr)); tmpattr = hlattr2attrname(atr, attrbuf, BUFSZ); if (tmpattr) @@ -3073,7 +3068,7 @@ choose_value: } Sprintf(colorqry, "Choose a color for when %s is '%s':", initblstats[fld].fldname, hilite.textmatch); - Sprintf(colorqry, "Choose attribute for when %s is '%s':", + Sprintf(attrqry, "Choose attribute for when %s is '%s':", initblstats[fld].fldname, hilite.textmatch); } else if (behavior == BL_TH_ALWAYS_HILITE) { Sprintf(colorqry, "Choose a color to always hilite %s:", @@ -3346,9 +3341,7 @@ shlmenu_redo: start_menu(tmpwin); status_hilite_linestr_gather(); - countall = status_hilite_linestr_countfield(BL_FLUSH); - if (countall) { any = zeroany; any.a_int = -1; @@ -3364,12 +3357,10 @@ shlmenu_redo: char buf[BUFSZ]; any = zeroany; - any.a_int = (i+1); + any.a_int = i + 1; + Sprintf(buf, "%-18s", initblstats[i].fldname); if (count) - Sprintf(buf, "%-18s (%i defined)", - initblstats[i].fldname, count); - else - Sprintf(buf, "%-18s", initblstats[i].fldname); + Sprintf(eos(buf), " (%d defined)", count); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); } @@ -3382,17 +3373,24 @@ shlmenu_redo: status_hilites_viewall(); else (void) status_hilite_menu_fld(i); - free((genericptr_t) picks); + free((genericptr_t) picks), picks = (menu_item *) 0; redo = TRUE; } - picks = (menu_item *) 0; destroy_nhwindow(tmpwin); + countall = status_hilite_linestr_countfield(BL_FLUSH); status_hilite_linestr_done(); if (redo) goto shlmenu_redo; + /* hilite_delta=='statushilites' does double duty: it is the + number of turns for temporary highlights to remain visible + and also when non-zero it is the flag to enable highlighting */ + if (countall > 0 && !iflags.hilite_delta) + pline( + "To have highlights become active, set 'statushilites' option to non-zero."); + return TRUE; } From cb02e7758943e90c3ec1b1710d9bc752ce2f876c Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 22 May 2018 10:40:55 -0700 Subject: [PATCH 22/23] hilite_status, what else? While deciding which highlights to apply, give 'percentage' and/or 'absolute' rules that match precedence over 'always' rules regardless of order within the config settings. When using 'O' to add 'up/down/changed' rule, don't include 'down' as a choice for field 'time'. When using 'O' to add rules, don't squeeze out spaces if adding a 'textmatch' rule for title (to support "field worker", "high priest", "student of stones", and so forth). While deciding which highlights to apply, ignore double quotes when testing whether a 'textmatch' rule matches the current text of a field. This allows rules to specify string values as '"value"' instead of just 'value'. It not does validate them to ensure quotes are paired at beginning and end, it just ignores them. New rules created via 'O' for rank title include them when displaying what the new rule would look like as a config file option. Other text fields haven't been changed to show quotes but ignoring such applies to all 'textmatch' comparisons. Expand the menu for adding 'textmatch' rules for title. When a rank has separate male and female titles, list three entries instead of just one "male rank" "female rank" "male rank" or "female rank" (the order of the first two entries and of the two titles in the third entry is reversed if the current character is female). If the user picks the third entry, two rules are added instead of just one, identical to each other except for the text to match. Further expand that menu with "none of the above (polymorphed)" at the end. When deciding which highlights to apply, "none of the above" and "(polymorphed)" and the full string are treated as equivalent (with spaces, quotes, and parentheses ignored). Rather than comparing anything against the title text, it matches if the hero is polymorphed (where title will be " the " instead of " the "). Note that the user can have config file 'textmatch' rules for title to match specific "" values but the 'O' menu doesn't offer any opportunity for that. (I've just realized that rules for specific monster types should be given precedence over "none of the above" but at present that isn't done; the order of the rules will determine which wins out.) --- src/botl.c | 145 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 33 deletions(-) diff --git a/src/botl.c b/src/botl.c index fdc25ae01..a7a22ec60 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1526982122 2018/05/22 09:42:02 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.99 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1527010852 2018/05/22 17:40:52 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.100 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -446,15 +446,16 @@ STATIC_DCL boolean FDECL(is_ltgt_percentnumber, (const char *)); STATIC_DCL boolean FDECL(has_ltgt_percentnumber, (const char *)); STATIC_DCL boolean FDECL(parse_status_hl2, (char (*)[QBUFSZ],BOOLEAN_P)); STATIC_DCL boolean FDECL(parse_condition, (char (*)[QBUFSZ], int)); +STATIC_DCL boolean FDECL(noneoftheabove, (const char *)); STATIC_DCL void FDECL(merge_bestcolor, (int *, int)); STATIC_DCL void FDECL(get_hilite_color, (int, int, genericptr_t, int, - int, int *)); + int, int *)); STATIC_DCL unsigned long FDECL(match_str2conditionbitmask, (const char *)); STATIC_DCL unsigned long FDECL(str2conditionbitmask, (char *)); STATIC_DCL void FDECL(split_clridx, (int, int *, int *)); STATIC_DCL char *FDECL(hlattr2attrname, (int, char *, int)); STATIC_DCL void FDECL(status_hilite_linestr_add, (int, struct hilite_s *, - unsigned long, const char *)); + unsigned long, const char *)); STATIC_DCL void NDECL(status_hilite_linestr_done); STATIC_DCL int FDECL(status_hilite_linestr_countfield, (int)); STATIC_DCL void NDECL(status_hilite_linestr_gather_conditions); @@ -1308,6 +1309,20 @@ reset_status_hilites() context.botlx = TRUE; } +/* test whether the text from a title rule matches the string for + title-while-polymorphed in the 'textmatch' menu */ +STATIC_OVL boolean +noneoftheabove(hl_text) +const char *hl_text; +{ + if (fuzzymatch(hl_text, "none of the above", "\" -_", TRUE) + || fuzzymatch(hl_text, "(polymorphed)", "()", TRUE) + || fuzzymatch(hl_text, "none of the above (polymorphed)", + "\" -_()", TRUE)) + return TRUE; + return FALSE; +} + STATIC_OVL void merge_bestcolor(bestcolor, newcolor) int *bestcolor; @@ -1372,7 +1387,8 @@ int *colorptr; /* there are hilites set here */ int max_pc = -1, min_pc = 101; int max_val = -LARGEST_INT, min_val = LARGEST_INT; - boolean exactmatch = FALSE, updown = FALSE, changed = FALSE; + boolean exactmatch = FALSE, updown = FALSE, changed = FALSE, + perc_or_abs = FALSE; /* min_/max_ are used to track best fit */ for (hl = blstats[idx][fldidx].thresholds; hl; hl = hl->next) { @@ -1381,33 +1397,43 @@ int *colorptr; updown rules to get the last one which qualifies */ if ((updown || changed) && hl->behavior != BL_TH_UPDOWN) continue; + /* among persistent highlights, if a 'percentage' or 'absolute' + rule has been matched, it takes precedence over 'always' */ + if (perc_or_abs && hl->behavior == BL_TH_ALWAYS_HILITE) + continue; switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: if (hl->rel == EQ_VALUE && pc == hl->value.a_int) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = max_pc = hl->value.a_int; - exactmatch = TRUE; - } else if (hl->rel == LT_VALUE && !exactmatch + exactmatch = perc_or_abs = TRUE; + } else if (exactmatch) { + ; /* already found best fit, skip lt,ge,&c */ + } else if (hl->rel == LT_VALUE && (pc < hl->value.a_int) && (hl->value.a_int <= min_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = hl->value.a_int; - } else if (hl->rel == LE_VALUE && !exactmatch + perc_or_abs = TRUE; + } else if (hl->rel == LE_VALUE && (pc <= hl->value.a_int) && (hl->value.a_int <= min_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); min_pc = hl->value.a_int; - } else if (hl->rel == GT_VALUE && !exactmatch + perc_or_abs = TRUE; + } else if (hl->rel == GT_VALUE && (pc > hl->value.a_int) && (hl->value.a_int >= max_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); max_pc = hl->value.a_int; - } else if (hl->rel == GE_VALUE && !exactmatch + perc_or_abs = TRUE; + } else if (hl->rel == GE_VALUE && (pc >= hl->value.a_int) && (hl->value.a_int >= max_pc)) { merge_bestcolor(&bestcolor, hl->coloridx); max_pc = hl->value.a_int; + perc_or_abs = TRUE; } break; case BL_TH_UPDOWN: @@ -1433,27 +1459,33 @@ int *colorptr; if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = max_val = hl->value.a_int; - exactmatch = TRUE; - } else if (hl->rel == LT_VALUE && !exactmatch + exactmatch = perc_or_abs = TRUE; + } else if (exactmatch) { + ; /* already found best fit, skip lt,ge,&c */ + } else if (hl->rel == LT_VALUE && (value->a_int < hl->value.a_int) && (hl->value.a_int <= min_val)) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = hl->value.a_int; - } else if (hl->rel == LE_VALUE && !exactmatch + perc_or_abs = TRUE; + } else if (hl->rel == LE_VALUE && (value->a_int <= hl->value.a_int) && (hl->value.a_int <= min_val)) { merge_bestcolor(&bestcolor, hl->coloridx); min_val = hl->value.a_int; - } else if (hl->rel == GT_VALUE && !exactmatch + perc_or_abs = TRUE; + } else if (hl->rel == GT_VALUE && (value->a_int > hl->value.a_int) && (hl->value.a_int >= max_val)) { merge_bestcolor(&bestcolor, hl->coloridx); max_val = hl->value.a_int; - } else if (hl->rel == GE_VALUE && !exactmatch + perc_or_abs = TRUE; + } else if (hl->rel == GE_VALUE && (value->a_int >= hl->value.a_int) && (hl->value.a_int >= max_val)) { merge_bestcolor(&bestcolor, hl->coloridx); max_val = hl->value.a_int; + perc_or_abs = TRUE; } break; case BL_TH_TEXTMATCH: @@ -1461,7 +1493,8 @@ int *colorptr; if (fldidx == BL_TITLE) txtstr += (strlen(plname) + sizeof " the " - sizeof ""); if (hl->rel == TXT_VALUE && hl->textmatch[0] - && fuzzymatch(hl->textmatch, txtstr, " -_", TRUE)) { + && (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE) + || (Upolyd && noneoftheabove(hl->textmatch)))) { merge_bestcolor(&bestcolor, hl->coloridx); } break; @@ -1524,12 +1557,11 @@ boolean from_configfile; hsbuf[fldnum][ccount] = '\0'; op++; continue; - } else { - rslt = parse_status_hl2(hsbuf, from_configfile); - if (!rslt) { - badopt = TRUE; - break; - } + } + rslt = parse_status_hl2(hsbuf, from_configfile); + if (!rslt) { + badopt = TRUE; + break; } } for (i = 0; i < MAX_THRESH; ++i) { @@ -2350,7 +2382,10 @@ const char *str; tmp->fld = fld; tmp->hl = hl; tmp->mask = mask; - (void) stripchars(tmp->str, " ", str); + if (fld == BL_TITLE) + Strcpy(tmp->str, str); + else + (void) stripchars(tmp->str, " ", str); tmp->id = status_hilite_str_id; if ((nxt = status_hilite_str) != 0) { @@ -2970,8 +3005,10 @@ choose_value: hilite.rel = lt_gt_eq; hilite.value = aval; } else if (behavior == BL_TH_UPDOWN) { + boolean ltok = (fld != BL_TIME), gtok = TRUE; + lt_gt_eq = status_hilite_menu_choose_updownboth(fld, (char *)0, - TRUE, TRUE); + ltok, gtok); if (lt_gt_eq == NO_LTEQGT) goto choose_behavior; Sprintf(colorqry, "Choose a color for when %s %s:", @@ -3039,19 +3076,45 @@ choose_value: hilite.rel = TXT_VALUE; Strcpy(hilite.textmatch, hutxt[rv]); } else if (fld == BL_TITLE) { - const char *rolelist[9]; - int i, rv; + const char *rolelist[3 * 9 + 1]; + char mbuf[QBUFSZ], fbuf[QBUFSZ], obuf[QBUFSZ]; + int i, j, rv; - for (i = 0; i < 9; i++) - rolelist[i] = (flags.female && urole.rank[i].f) - ? urole.rank[i].f : urole.rank[i].m; + for (i = j = 0; i < 9; i++) { + Sprintf(mbuf, "\"%s\"", urole.rank[i].m); + if (urole.rank[i].f) { + Sprintf(fbuf, "\"%s\"", urole.rank[i].f); + Sprintf(obuf, "%s or %s", + flags.female ? fbuf : mbuf, + flags.female ? mbuf : fbuf); + } else { + fbuf[0] = obuf[0] = '\0'; + } + if (flags.female) { + if (*fbuf) + rolelist[j++] = dupstr(fbuf); + rolelist[j++] = dupstr(mbuf); + if (*obuf) + rolelist[j++] = dupstr(obuf); + } else { + rolelist[j++] = dupstr(mbuf); + if (*fbuf) + rolelist[j++] = dupstr(fbuf); + if (*obuf) + rolelist[j++] = dupstr(obuf); + } + } + rolelist[j++] = dupstr("\"none of the above (polymorphed)\""); - rv = query_arrayvalue(qry_buf, rolelist, 0, 9); + rv = query_arrayvalue(qry_buf, rolelist, 0, j); + if (rv >= 0) { + hilite.rel = TXT_VALUE; + Strcpy(hilite.textmatch, rolelist[rv]); + } + for (i = 0; i < j; i++) + free((genericptr_t) rolelist[i]), rolelist[i] = 0; if (rv < 0) goto choose_behavior; - - hilite.rel = TXT_VALUE; - Strcpy(hilite.textmatch, rolelist[rv]); } else { char inbuf[BUFSZ]; @@ -3137,9 +3200,25 @@ choose_color: pline("Added hilite condition/%s/%s", conditionbitmask2str(cond), clrbuf); } else { + char *p, *q; + hilite.coloridx = clr | (atr << 8); hilite.anytype = initblstats[fld].anytype; + if (fld == BL_TITLE && (p = strstri(hilite.textmatch, " or ")) != 0) { + /* split menu choice "male-rank or female-rank" into two distinct + but otherwise identical rules, "male-rank" and "female-rank" */ + *p = '\0'; /* chop off " or female-rank" */ + /* new rule for male-rank */ + status_hilite_add_threshold(fld, &hilite); + pline("Added hilite %s", status_hilite2str(&hilite)); + /* transfer female-rank to start of hilite.textmatch buffer */ + p += sizeof " or " - sizeof ""; + q = hilite.textmatch; + while ((*q++ = *p++) != '\0') + continue; + /* proceed with normal addition of new rule */ + } status_hilite_add_threshold(fld, &hilite); pline("Added hilite %s", status_hilite2str(&hilite)); } @@ -3314,7 +3393,7 @@ status_hilites_viewall() while (hlstr) { Sprintf(buf, "OPTIONS=hilite_status: %.*s", - (int)(BUFSZ - sizeof "OPTIONS=hilite_status: " - 1), + (int) (BUFSZ - sizeof "OPTIONS=hilite_status: " - 1), hlstr->str); putstr(datawin, 0, buf); hlstr = hlstr->next; From 645e1ac5b3b05f5407b75080e20d5a0cd34dfb96 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 22 May 2018 19:23:03 -0700 Subject: [PATCH 23/23] hilite_status title when polymorphed Make a 'textmatch' rule for title that matches a specific monster type while polymorphed take precedence over a "none of the above" one. --- src/botl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/botl.c b/src/botl.c index a7a22ec60..1548aef70 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1527010852 2018/05/22 17:40:52 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.100 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1527042178 2018/05/23 02:22:58 $ $NHDT-Branch: NetHack-3.6.2 $:$NHDT-Revision: 1.101 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1316,7 +1316,7 @@ noneoftheabove(hl_text) const char *hl_text; { if (fuzzymatch(hl_text, "none of the above", "\" -_", TRUE) - || fuzzymatch(hl_text, "(polymorphed)", "()", TRUE) + || fuzzymatch(hl_text, "(polymorphed)", "\"()", TRUE) || fuzzymatch(hl_text, "none of the above (polymorphed)", "\" -_()", TRUE)) return TRUE; @@ -1492,10 +1492,16 @@ int *colorptr; txtstr = blstats[idx][fldidx].val; if (fldidx == BL_TITLE) txtstr += (strlen(plname) + sizeof " the " - sizeof ""); - if (hl->rel == TXT_VALUE && hl->textmatch[0] - && (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE) - || (Upolyd && noneoftheabove(hl->textmatch)))) { - merge_bestcolor(&bestcolor, hl->coloridx); + if (hl->rel == TXT_VALUE && hl->textmatch[0]) { + if (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE)) { + merge_bestcolor(&bestcolor, hl->coloridx); + exactmatch = TRUE; + } else if (exactmatch) { + ; /* already found best fit, skip "noneoftheabove" */ + } else if (fldidx == BL_TITLE + && Upolyd && noneoftheabove(hl->textmatch)) { + merge_bestcolor(&bestcolor, hl->coloridx); + } } break; case BL_TH_ALWAYS_HILITE: