From 81d755a9e049270568e97f835dc77500ffad3426 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 28 Mar 2019 08:40:40 -0700 Subject: [PATCH 01/17] fix githib issue #179 - random drawbridge state Fixes #179 The Valkyrie goal level has two drawbridges and one of them was set to have 50:50 chance to be closed (raised). But 'random' for drawbridge open/closed (or lowered/raised) state was always choosing open (lowered). This fixes that and also changes that level to have the old settings (southern open, northern 50:50) for 75% of the time and new settings (both 50:50) for 25% of the time. So there's now a 12.5% chance that both will be closed instead of both always being open (due to the bug with handling random). --- dat/Valkyrie.des | 10 +++++++--- doc/fixes36.2 | 5 ++++- src/sp_lev.c | 7 +++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dat/Valkyrie.des b/dat/Valkyrie.des index 21541ab0a..ebae19526 100644 --- a/dat/Valkyrie.des +++ b/dat/Valkyrie.des @@ -1,4 +1,4 @@ -# NetHack 3.6 Valkyrie.des $NHDT-Date: 1432512783 2015/05/25 00:13:03 $ $NHDT-Branch: master $:$NHDT-Revision: 1.13 $ +# NetHack 3.6 Valkyrie.des $NHDT-Date: 1553787633 2019/03/28 15:40:33 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.14 $ # Copyright (c) 1989 by Jean-Christophe Collet # Copyright (c) 1991-2 by M. Stephenson # NetHack may be freely redistributed. See license for details. @@ -216,9 +216,13 @@ REGION:(00,00,34,16),lit,"ordinary" STAIR:(45,10),up # Non diggable walls NON_DIGGABLE:(00,00,34,16) -# Drawbridges +# Drawbridges; northern one opens to the south, southern one to the north DRAWBRIDGE:(17,02),south,random -DRAWBRIDGE:(17,14),north,open +IF [75%] { + DRAWBRIDGE:(17,14),north,open +} ELSE { + DRAWBRIDGE:(17,14),north,random +} # Objects OBJECT:('(',"crystal ball"),(17,08),blessed,5,name:"The Orb of Fate" OBJECT:random,random diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 9439ab4d3..66de6f54d 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.285 $ $NHDT-Date: 1553653612 2019/03/27 02:26:52 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.286 $ $NHDT-Date: 1553787633 2019/03/28 15:40:33 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -398,6 +398,9 @@ avoid spurious status refresh when hero gains experience while 'showexp' and 'showscore' options are off using Cleaver to attack a worm tail segment but kill adjacent head first would result in an impossible warning from cutworm +Valkyrie quest was supposed to have a 50:50 chance that northern drawbridge + would be raised, but both were always lowered; chances now are: both + lowered: 3/8, S down+N up: 3/8, N down+S up: 1/8, both raised: 1/8 Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/sp_lev.c b/src/sp_lev.c index ded755317..cd952108c 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 sp_lev.c $NHDT-Date: 1550524566 2019/02/18 21:16:06 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.110 $ */ +/* NetHack 3.6 sp_lev.c $NHDT-Date: 1553787633 2019/03/28 15:40:33 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.111 $ */ /* Copyright (c) 1989 by Jean-Christophe Collet */ /* NetHack may be freely redistributed. See license for details. */ @@ -4711,13 +4711,16 @@ struct sp_coder *coder; { static const char nhFunc[] = "spo_drawbridge"; xchar x, y; + int dopen; struct opvar *dir, *db_open, *dcoord; if (!OV_pop_i(dir) || !OV_pop_i(db_open) || !OV_pop_c(dcoord)) return; get_location_coord(&x, &y, DRY | WET | HOT, coder->croom, OV_i(dcoord)); - if (!create_drawbridge(x, y, OV_i(dir), OV_i(db_open))) + if ((dopen = OV_i(db_open)) == -1) + dopen = !rn2(2); + if (!create_drawbridge(x, y, OV_i(dir), dopen ? TRUE : FALSE)) impossible("Cannot create drawbridge."); SpLev_Map[x][y] = 1; From 0597eeebe992a6156f0ab3bf0b66c3a842ebccb7 Mon Sep 17 00:00:00 2001 From: PatR Date: Thu, 28 Mar 2019 14:06:46 -0700 Subject: [PATCH 02/17] Valk quest comment --- dat/Valkyrie.des | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dat/Valkyrie.des b/dat/Valkyrie.des index ebae19526..252c6ce82 100644 --- a/dat/Valkyrie.des +++ b/dat/Valkyrie.des @@ -1,4 +1,4 @@ -# NetHack 3.6 Valkyrie.des $NHDT-Date: 1553787633 2019/03/28 15:40:33 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.14 $ +# NetHack 3.6 Valkyrie.des $NHDT-Date: 1553807172 2019/03/28 21:06:12 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.15 $ # Copyright (c) 1989 by Jean-Christophe Collet # Copyright (c) 1991-2 by M. Stephenson # NetHack may be freely redistributed. See license for details. @@ -216,7 +216,8 @@ REGION:(00,00,34,16),lit,"ordinary" STAIR:(45,10),up # Non diggable walls NON_DIGGABLE:(00,00,34,16) -# Drawbridges; northern one opens to the south, southern one to the north +# Drawbridges; northern one opens from the south (portcullis) to further +# north (lowered span), southern one from the north to further south DRAWBRIDGE:(17,02),south,random IF [75%] { DRAWBRIDGE:(17,14),north,open From d1dade164ef220a011bf89fa445a8b0012bf38df Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Mar 2019 04:21:18 -0700 Subject: [PATCH 03/17] tty statuslines:3 Implement the 'statuslines' option for tty. 2 and 3 line status are similar to curses. Tty's version doesn't include insertion of extra spaces for enhanced readability, or ignoring 'showexp' when space is needed for other fields, or right justifying 'score' and suppressing it when there isn't room for the entire number. It continues to have abbreviated condition and encumbrance descriptions that curses lacks which get used when the normal ones take up too much space. 'statuslines' can be set with 'O' so it is feasible to switch back and forth between 2 and 3 lines on the fly. But only if the display is at least 25 lines (actually ROWNO+4) or else CLIPPING is enabled at build time. This fixes the bug where after resorting to abbreviated condition values it sometimes (always?) wouldn't switch back after more room became available. Abbreviated encumbrance values had problems too (lack of leading space and not changing value if encumbrance changed to anything other than unencumbered) and this fixes that as well. --- doc/Guidebook.mn | 4 +- doc/Guidebook.tex | 2 +- doc/fixes36.2 | 4 +- include/wintty.h | 6 +- src/options.c | 24 +- win/tty/termcap.c | 37 ++- win/tty/wintty.c | 664 +++++++++++++++++++++++++--------------------- 7 files changed, 412 insertions(+), 329 deletions(-) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 051f61299..956ba2aa1 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.301 $ $NHDT-Date: 1553480404 2019/03/25 02:20:04 $ +.\" $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.302 $ $NHDT-Date: 1553858473 2019/03/29 11:21:13 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\" NetHack's Guidebook.mn currently does *not* adhere to these guidelines. @@ -3657,7 +3657,7 @@ up (default yes). .lp statuslines Number of lines for traditional below-the-map status display. Acceptable values are 2 and 3 (default is 2). -Curses interface only. +Curses and tty interfaces only. .lp "term_cols\ \ \fIand\fP" .lp term_rows Curses interface only. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index fa1a9872a..151bed238 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -4057,7 +4057,7 @@ it starts up (default yes). \item[\ib{statuslines}] Number of lines for traditional below-the-map status display. Acceptable values are 2 and 3 (default is 2). -Curses interface only. +Curses and tty interfaces only. %.lp \item[\ib{term\verb+_+cols}\ \ {\it and}]" %.lp diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 66de6f54d..72cb9856f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.286 $ $NHDT-Date: 1553787633 2019/03/28 15:40:33 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.287 $ $NHDT-Date: 1553858472 2019/03/29 11:21:12 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -653,6 +653,8 @@ curses: status display substantially revamped for both horizontal (via 'align_status:bottom' or 'top') and vertical (via 'align_status:left' or 'right'); 3-line horizontal layout (via 'statuslines:3') added curses: support msg_window:full; default is still msg_window:reversed +tty: support statuslines:3 and dynamically switching between 2 and 3 with 'O' + (requires a display with at least 25 lines or that CLIPPING be enabled) NetHack Community Patches (or Variation) Included diff --git a/include/wintty.h b/include/wintty.h index f7e5b0611..1a609de2a 100644 --- a/include/wintty.h +++ b/include/wintty.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.h $NHDT-Date: 1549327485 2019/02/05 00:44:45 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.32 $ */ +/* NetHack 3.6 wintty.h $NHDT-Date: 1553858470 2019/03/29 11:21:10 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.33 $ */ /* Copyright (c) David Cohrs, 1991,1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -79,7 +79,7 @@ struct tty_status_fields { boolean valid; boolean dirty; boolean redraw; - boolean last_on_row; + boolean _not_used; /* was 'last_in_row' */ }; #endif @@ -151,6 +151,7 @@ E void NDECL(cl_eos); * a color or whatever. wintty.c should concern itself with WHERE to put * stuff in a window. */ +E int FDECL(term_attr_fixup, (int)); E void FDECL(term_start_attr, (int attr)); E void FDECL(term_end_attr, (int attr)); E void NDECL(term_start_raw_bold); @@ -182,6 +183,7 @@ E void FDECL(win_tty_init, (int)); /* external declarations */ E void FDECL(tty_init_nhwindows, (int *, char **)); +E void FDECL(tty_preference_update, (const char *)); E void NDECL(tty_player_selection); E void NDECL(tty_askname); E void NDECL(tty_get_nh_event); diff --git a/src/options.c b/src/options.c index f90cef3bd..c4471cf61 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1553480404 2019/03/25 02:20:04 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.360 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1553858470 2019/03/29 11:21:10 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.361 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -385,20 +385,23 @@ static struct Comp_Opt { #ifdef MSDOS { "soundcard", "type of sound card to use", 20, SET_IN_FILE }, #endif -#ifdef STATUS_HILITES { "statushilites", +#ifdef STATUS_HILITES "0=no status highlighting, N=show highlights for N turns", - 20, SET_IN_GAME }, + 20, SET_IN_GAME #else - { "statushilites", "highlight control", 20, SET_IN_FILE }, + "highlight control", 20, SET_IN_FILE #endif -#ifdef CURSES_GRAPHICS + }, { "statuslines", +#ifdef CURSES_GRAPHICS "2 or 3 lines for horizonal (bottom or top) status display", - 20, SET_IN_GAME }, /*WC2*/ + 20, SET_IN_GAME #else - { "statuslines", "2 or 3 lines for status display", 20, SET_IN_FILE }, + "2 or 3 lines for status display", + 20, SET_IN_FILE #endif + }, /*WC2*/ { "symset", "load a set of display symbols from the symbols file", 70, SET_IN_GAME }, { "roguesymset", @@ -3703,6 +3706,7 @@ boolean tinitial, tfrom_file; } return retval; } +#endif /* CURSES_GRAPHICS */ /* WINCAP2 * statuslines:n */ @@ -3728,7 +3732,6 @@ boolean tinitial, tfrom_file; } return retval; } -#endif /* CURSES_GRAPHICS */ /* menustyle:traditional or combination or full or partial */ fullname = "menustyle"; @@ -5739,8 +5742,9 @@ char *buf; iflags.hilite_delta, iflags.hilite_delta); #endif } else if (!strcmp(optname,"statuslines")) { - Strcpy(buf, (WINDOWPORT("curses") - && iflags.wc2_statuslines < 3) ? "2" : "3"); + if (wc2_supported(optname)) + Strcpy(buf, (iflags.wc2_statuslines < 3) ? "2" : "3"); + /* else default to "unknown" */ } else if (!strcmp(optname, "suppress_alert")) { if (flags.suppress_alert == 0L) Strcpy(buf, none); diff --git a/win/tty/termcap.c b/win/tty/termcap.c index c373df04c..ead39d228 100644 --- a/win/tty/termcap.c +++ b/win/tty/termcap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 termcap.c $NHDT-Date: 1456907853 2016/03/02 08:37:33 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.24 $ */ +/* NetHack 3.6 termcap.c $NHDT-Date: 1553858473 2019/03/29 11:21:13 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.29 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Pasi Kallinen, 2018. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1206,12 +1206,37 @@ int n; return nulstr; } +/* suppress nonfunctional highlights so render_status() might be able to + optimize more; keep this in sync with s_atr2str() */ +int +term_attr_fixup(msk) +int msk; +{ + /* underline is converted to bold if its start sequence isn't available */ + if ((msk & (1 << ATR_ULINE)) && !nh_US) { + msk |= (1 << ATR_BOLD); + msk &= ~(1 << ATR_ULINE); + } + /* blink is converted to bold unconditionally [why?] */ + if (msk & (1 << ATR_BLINK)) { + msk |= (1 << ATR_BOLD); + msk &= ~(1 << ATR_BLINK); + } + /* dim is ignored */ + if (msk & (1 << ATR_DIM)) + msk &= ~(1 << ATR_DIM); + return msk; +} + void term_start_attr(attr) int attr; { if (attr) { - xputs(s_atr2str(attr)); + const char *astr = s_atr2str(attr); + + if (*astr) + xputs(s_atr2str(attr)); } } @@ -1220,7 +1245,10 @@ term_end_attr(attr) int attr; { if (attr) { - xputs(e_atr2str(attr)); + const char *astr = e_atr2str(attr); + + if (*astr) + xputs(e_atr2str(attr)); } } @@ -1248,7 +1276,8 @@ void term_start_color(color) int color; { - xputs(hilites[color]); + if (color < CLR_MAX) + xputs(hilites[color]); } /* not to be confused with has_colors() in unixtty.c */ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index b7ad2bf5b..fe06b8180 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1553716531 2019/03/27 19:55:31 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.198 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1553858474 2019/03/29 11:21:14 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.199 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -96,7 +96,7 @@ struct window_procs tty_procs = { | WC2_HILITE_STATUS | WC2_HITPOINTBAR | WC2_FLUSH_STATUS | WC2_RESET_STATUS #endif - | WC2_DARKGRAY | WC2_SUPPRESS_HIST), + | WC2_DARKGRAY | WC2_SUPPRESS_HIST | WC2_STATUSLINES), tty_init_nhwindows, tty_player_selection, tty_askname, tty_get_nh_event, tty_exit_nhwindows, tty_suspend_nhwindows, tty_resume_nhwindows, tty_create_nhwindow, tty_clear_nhwindow, tty_display_nhwindow, @@ -123,11 +123,7 @@ struct window_procs tty_procs = { /* other defs that really should go away (they're tty specific) */ tty_start_screen, tty_end_screen, genl_outrip, -#if defined(WIN32) - nttty_preference_update, -#else - genl_preference_update, -#endif + tty_preference_update, tty_getmsghistory, tty_putmsghistory, tty_status_init, genl_status_finish, tty_status_enablefield, @@ -179,8 +175,10 @@ static const char to_continue[] = "to continue"; #else STATIC_DCL void NDECL(getret); #endif -STATIC_DCL void FDECL(erase_menu_or_text, - (winid, struct WinDesc *, BOOLEAN_P)); +STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */ +STATIC_DCL void NDECL(new_status_window); +STATIC_DCL void FDECL(erase_menu_or_text, (winid, struct WinDesc *, + BOOLEAN_P)); STATIC_DCL void FDECL(free_window_info, (struct WinDesc *, BOOLEAN_P)); STATIC_DCL void FDECL(dmore, (struct WinDesc *, const char *)); STATIC_DCL void FDECL(set_item_state, (winid, int, tty_menu_item *)); @@ -198,23 +196,20 @@ STATIC_DCL void FDECL(process_text_window, (winid, struct WinDesc *)); STATIC_DCL tty_menu_item *FDECL(reverse, (tty_menu_item *)); STATIC_DCL const char *FDECL(compress_str, (const char *)); STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P)); -STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */ STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int)); STATIC_DCL boolean NDECL(reset_role_filtering); #ifdef STATUS_HILITES -STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *, int *)); +STATIC_DCL boolean FDECL(check_fields, (BOOLEAN_P, int *)); STATIC_DCL void NDECL(render_status); -STATIC_DCL void FDECL(tty_putstatusfield, (struct tty_status_fields *, - const char *, int, int)); +STATIC_DCL void FDECL(tty_putstatusfield, (const char *, int, int)); STATIC_DCL boolean NDECL(check_windowdata); STATIC_DCL int NDECL(condition_size); STATIC_DCL int FDECL(make_things_fit, (BOOLEAN_P)); STATIC_DCL void FDECL(shrink_enc, (int)); STATIC_DCL void FDECL(shrink_dlvl, (int)); -STATIC_DCL void NDECL(do_setlast); #endif /* @@ -322,19 +317,8 @@ int sig_unused UNUSED; cw = wins[WIN_MESSAGE]; cw->curx = cw->cury = 0; - tty_destroy_nhwindow(WIN_STATUS); - WIN_STATUS = tty_create_nhwindow(NHW_STATUS); - + new_status_window(); if (u.ux) { -#ifdef CLIPPING - if (CO < COLNO || LI < ROWNO + 3) { - setclipped(); - tty_cliparound(u.ux, u.uy); - } else { - clipping = FALSE; - clipx = clipy = 0; - } -#endif i = ttyDisplay->toplin; ttyDisplay->toplin = 0; docrt(); @@ -359,6 +343,41 @@ int sig_unused UNUSED; } #endif +/* destroy and recreate status window; extracted from winch_handler() + and augmented for use by tty_preference_update() */ +STATIC_OVL void +new_status_window() +{ + if (WIN_STATUS != WIN_ERR) { + /* if it's shrinking, clear it before destroying so that + dropped portion won't show anything that's now becoming stale */ + if (wins[WIN_STATUS]->maxrow > iflags.wc2_statuslines) + tty_clear_nhwindow(WIN_STATUS); + + tty_destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR; + } + /* frees some status tracking data */ + genl_status_finish(); + /* creates status window and allocates tracking data */ + tty_status_init(); + tty_clear_nhwindow(WIN_STATUS); /* does some init, sets context.botlx */ +#ifdef STATUS_HILITES + status_initialize(REASSESS_ONLY); +#endif + +#ifdef CLIPPING + if (u.ux) { + if (LI < 1 + ROWNO + iflags.wc2_statuslines) { + setclipped(); + tty_cliparound(u.ux, u.uy); + } else { + clipping = FALSE; + clipx = clipy = 0; + } + } +#endif +} + /*ARGSUSED*/ void tty_init_nhwindows(argcp, argv) @@ -367,14 +386,17 @@ char **argv UNUSED; { int wid, hgt, i; -/* - * Remember tty modes, to be restored on exit. - * - * gettty() must be called before tty_startup() - * due to ordering of LI/CO settings - * tty_startup() must be called before initoptions() - * due to ordering of graphics settings - */ + /* options aren't processed yet so wc2_statuslines might be 0; + make sure that it has a reasonable value during tty setup */ + iflags.wc2_statuslines = (iflags.wc2_statuslines < 3) ? 2 : 3; + /* + * Remember tty modes, to be restored on exit. + * + * gettty() must be called before tty_startup() + * due to ordering of LI/CO settings + * tty_startup() must be called before initoptions() + * due to ordering of graphics settings + */ #if defined(UNIX) || defined(VMS) setbuf(stdout, obuf); #endif @@ -414,6 +436,31 @@ char **argv UNUSED; tty_putstr(BASE_WINDOW, 0, copyright_banner_line(i)); tty_putstr(BASE_WINDOW, 0, ""); tty_display_nhwindow(BASE_WINDOW, FALSE); + + /* 'statuslines' defaults to SET_IN_FILE, allowed but invisible; + make it dynamically settable if feasible, otherwise visible */ + if (tty_procs.wincap2 & WC2_STATUSLINES) + set_wc2_option_mod_status(WC2_STATUSLINES, +#ifndef CLIPPING + (LI < 1 + ROWNO + 2) ? DISP_IN_GAME : +#endif + SET_IN_GAME); +} + +void +tty_preference_update(pref) +const char *pref; +{ + if (!strcmp(pref, "statuslines") && iflags.window_inited) { + new_status_window(); + } + +#if defined(WIN32) + nttty_preference_update(pref); +#else + genl_preference_update(pref); +#endif + return; } /* try to reduce clutter in the code below... */ @@ -1367,7 +1414,7 @@ tty_create_nhwindow(type) int type; { struct WinDesc *newwin; - int i; + int i, rowoffset; int newid; if (maxwin == MAXWIN) @@ -1402,15 +1449,22 @@ int type; newwin->maxcol = newwin->cols = 0; break; case NHW_STATUS: - /* status window, 2 lines long, full width, bottom of screen */ + /* status window, 2 or 3 lines long, full width, bottom of screen */ + if (iflags.wc2_statuslines < 2 +#ifndef CLIPPING + || (LI < 1 + ROWNO + 3) +#endif + || iflags.wc2_statuslines > 3) + iflags.wc2_statuslines = 2; newwin->offx = 0; + rowoffset = ttyDisplay->rows - iflags.wc2_statuslines; #if defined(USE_TILES) && defined(MSDOS) if (iflags.grmode) { - newwin->offy = ttyDisplay->rows - 2; + newwin->offy = rowoffset; } else #endif - newwin->offy = min((int) ttyDisplay->rows - 2, ROWNO + 1); - newwin->rows = newwin->maxrow = 2; + newwin->offy = min(rowoffset, ROWNO + 1); + newwin->rows = newwin->maxrow = iflags.wc2_statuslines; newwin->cols = newwin->maxcol = ttyDisplay->cols; break; case NHW_MAP: @@ -1424,9 +1478,8 @@ int type; break; case NHW_MENU: case NHW_TEXT: - /* inventory/menu window, variable length, full width, top of screen - */ - /* help window, the same, different semantics for display, etc */ + /* inventory/menu window, variable length, full width, top of screen; + help window, the same, different semantics for display, etc */ newwin->offx = newwin->offy = 0; newwin->rows = 0; newwin->cols = ttyDisplay->cols; @@ -1449,18 +1502,15 @@ int type; } if (newwin->maxrow) { - newwin->data = - (char **) alloc(sizeof(char *) * (unsigned) newwin->maxrow); - newwin->datlen = - (short *) alloc(sizeof(short) * (unsigned) newwin->maxrow); - if (newwin->maxcol) { - /* WIN_STATUS */ - for (i = 0; i < newwin->maxrow; i++) { + newwin->data = (char **) alloc( + (unsigned) (newwin->maxrow * sizeof (char *))); + newwin->datlen = (short *) alloc( + (unsigned) (newwin->maxrow * sizeof (short))); + for (i = 0; i < newwin->maxrow; i++) { + if (newwin->maxcol) { /* WIN_STATUS */ newwin->data[i] = (char *) alloc((unsigned) newwin->maxcol); newwin->datlen[i] = (short) newwin->maxcol; - } - } else { - for (i = 0; i < newwin->maxrow; i++) { + } else { newwin->data[i] = (char *) 0; newwin->datlen[i] = 0; } @@ -1547,6 +1597,7 @@ void tty_clear_nhwindow(window) winid window; { + int i, j, m, n; register struct WinDesc *cw = 0; HUPSKIP(); @@ -1567,10 +1618,18 @@ winid window; } break; case NHW_STATUS: - tty_curs(window, 1, 0); - cl_end(); - tty_curs(window, 1, 1); - cl_end(); + m = cw->maxrow; + n = cw->cols; + for (i = 0; i < m; ++i) { + tty_curs(window, 1, i); + cl_end(); + + for (j = 0; j < n - 1; ++j) + cw->data[i][j] = ' '; + cw->data[i][n - 1] = '\0'; + /*finalx[i][NOW] = finalx[i][BEFORE] = 0;*/ + } + context.botlx = 1; break; case NHW_MAP: /* cheap -- clear the whole thing and tell nethack to redraw botl */ @@ -1578,6 +1637,8 @@ winid window; /*FALLTHRU*/ case NHW_BASE: clear_screen(); + /*for (i = 0; i < cw->maxrow; ++i) */ + /* finalx[i][NOW] = finalx[i][BEFORE] = 0;*/ break; case NHW_MENU: case NHW_TEXT: @@ -2612,7 +2673,7 @@ const char *str; case NHW_STATUS: ob = &cw->data[cw->cury][j = cw->curx]; if (context.botlx) - *ob = 0; + *ob = '\0'; if (!cw->cury && (int) strlen(str) >= CO) { /* the characters before "St:" are unnecessary */ nb = index(str, ':'); @@ -2637,7 +2698,7 @@ const char *str; (void) strncpy(&cw->data[cw->cury][j], str, cw->cols - j - 1); cw->data[cw->cury][cw->cols - 1] = '\0'; /* null terminate */ - cw->cury = (cw->cury + 1) % 2; + cw->cury = (cw->cury + 1) % cw->maxrow; cw->curx = 0; break; #endif /* STATUS_HILITES */ @@ -2950,7 +3011,7 @@ const char *prompt; /* prompt to for menu */ free((genericptr_t) cw->plist); cw->plist_size = cw->npages + 1; cw->plist = (tty_menu_item **) alloc(cw->plist_size - * sizeof(tty_menu_item *)); + * sizeof (tty_menu_item *)); } cw->cols = 0; /* cols is set when the win is initialized... (why?) */ @@ -3247,7 +3308,7 @@ setclipped() clipping = TRUE; clipx = clipy = 0; clipxmax = CO; - clipymax = LI - 3; + clipymax = LI - 1 - iflags.wc2_statuslines; } void @@ -3269,10 +3330,10 @@ int x, y; } if (y < clipy + 2) { clipy = max(0, y - (clipymax - clipy) / 2); - clipymax = clipy + (LI - 3); + clipymax = clipy + (LI - 1 - iflags.wc2_statuslines); } else if (y > clipymax - 2) { clipymax = min(ROWNO, clipymax + (clipymax - clipy) / 2); - clipy = clipymax - (LI - 3); + clipy = clipymax - (LI - 1 - iflags.wc2_statuslines); } if (clipx != oldx || clipy != oldy) { if (on_level(&u.uz0, &u.uz) && !restoring) @@ -3582,14 +3643,6 @@ extern char *status_vals[MAXBLSTATS]; extern boolean status_activefields[MAXBLSTATS]; extern winid WIN_STATUS; -const char *fieldnames[] = { - "title", "strength", "dexterity", "constitution", "intelligence", - "wisdom", "charisma", "alignment", "score", "carrying-capacity", - "gold", "power", "power-max", "experience-level", "armor-class", - "HD", "time", "hunger", "hitpoints", "hitpoints-max", - "dungeon-level", "experience", "condition", -}; - #ifdef STATUS_HILITES #ifdef TEXTCOLOR STATIC_DCL int FDECL(condcolor, (long, unsigned long *)); @@ -3597,10 +3650,7 @@ STATIC_DCL int FDECL(condcolor, (long, unsigned long *)); STATIC_DCL int FDECL(condattr, (long, unsigned long *)); static unsigned long *tty_colormasks; static long tty_condition_bits; -int cond_disp_width[2]; /* 2: current and previous */ -static struct tty_status_fields - tty_status[2][MAXBLSTATS]; /* 2: first index is for current - and previous */ +static struct tty_status_fields tty_status[2][MAXBLSTATS]; /* 2: NOW,BEFORE */ static int hpbar_percent, hpbar_color; static struct condition_t { long mask; @@ -3626,21 +3676,35 @@ static const char *encvals[3][6] = { { "", "Burden", "Stress", "Strain", "Overtax", "Overload" }, { "", "Brd", "Strs", "Strn", "Ovtx", "Ovld" } }; +#define blPAD BL_FLUSH #define MAX_PER_ROW 15 -static enum statusfields fieldorder[2][MAX_PER_ROW] = { /* 2: 2 status lines */ +/* 2 or 3 status lines */ +static const enum statusfields + twolineorder[3][MAX_PER_ROW] = { { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, BL_ALIGN, - BL_SCORE, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, BL_FLUSH, - BL_FLUSH }, + BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD }, { BL_LEVELDESC, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, BL_AC, BL_XP, BL_EXP, BL_HD, BL_TIME, BL_HUNGER, - BL_CAP, BL_CONDITION, BL_FLUSH } + BL_CAP, BL_CONDITION, BL_FLUSH }, + /* third row of array isn't used for twolineorder */ + { BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } +}, + /* Align moved from 1 to 2, Leveldesc+Time+Condition moved from 2 to 3 */ + threelineorder[3][MAX_PER_ROW] = { + { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN, BL_WI, BL_CH, + BL_SCORE, BL_FLUSH, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD }, + { BL_ALIGN, BL_GOLD, BL_HP, BL_HPMAX, BL_ENE, BL_ENEMAX, + BL_AC, BL_XP, BL_EXP, BL_HD, BL_HUNGER, + BL_CAP, BL_FLUSH, blPAD, blPAD }, + { BL_LEVELDESC, BL_TIME, BL_CONDITION, BL_FLUSH, blPAD, blPAD, + blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD, blPAD } }; +static const enum statusfields (*fieldorder)[3][MAX_PER_ROW]; -static int last_on_row[2]; /* [rows] */ -static int finalx[2][2]; /* [rows][NOW or BEFORE] */ -static boolean setlast = FALSE; +static int finalx[3][2]; /* [rows][NOW or BEFORE] */ static boolean windowdata_init = FALSE; -static int cond_shrinklvl = 0, cond_width_at_shrink = 0; +static int cond_shrinklvl = 0; static int enclev = 0, enc_shrinklvl = 0; /* static int dl_shrinklvl = 0; */ static boolean truncation_expected = FALSE; @@ -3673,18 +3737,19 @@ void tty_status_init() { #ifdef STATUS_HILITES - int i; + int i, num_rows; + + num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3; + fieldorder = (num_rows != 3) ? &twolineorder : &threelineorder; for (i = 0; i < MAXBLSTATS; ++i) { tty_status[NOW][i].idx = BL_FLUSH; tty_status[NOW][i].color = NO_COLOR; /* no color */ tty_status[NOW][i].attr = ATR_NONE; - tty_status[NOW][i].x = 0; - tty_status[NOW][i].y = 0; + tty_status[NOW][i].x = tty_status[NOW][i].y = 0; tty_status[NOW][i].valid = FALSE; tty_status[NOW][i].dirty = FALSE; tty_status[NOW][i].redraw = FALSE; - tty_status[NOW][i].last_on_row = FALSE; tty_status[BEFORE][i] = tty_status[NOW][i]; } tty_condition_bits = 0L; @@ -3703,10 +3768,6 @@ const char *fmt; boolean enable; { genl_status_enablefield(fieldidx, nm, fmt, enable); -#ifdef STATUS_HILITES - /* force re-evaluation of last field on the row */ - setlast = FALSE; -#endif } #ifdef STATUS_HILITES @@ -3774,10 +3835,11 @@ int fldidx, chg UNUSED, percent, color; genericptr_t ptr; unsigned long *colormasks; { - int i; + int i, attrmask; long *condptr = (long *) ptr; char *text = (char *) ptr; - char *fval, *lastchar, *p; + char goldbuf[40], *fval, *lastchar, *p; + const char *fmt; boolean reset_state = NO_RESET; if ((fldidx < BL_RESET) || (fldidx >= MAXBLSTATS)) @@ -3786,13 +3848,6 @@ unsigned long *colormasks; if ((fldidx >= 0 && fldidx < MAXBLSTATS) && !status_activefields[fldidx]) return; - if (!setlast) - do_setlast(); - -#ifndef TEXTCOLOR - color = (color & ~0x00FF) | NO_COLOR; -#endif - switch (fldidx) { case BL_RESET: reset_state = FORCE_RESET; @@ -3809,13 +3864,28 @@ unsigned long *colormasks; tty_status[NOW][fldidx].dirty = TRUE; truncation_expected = FALSE; break; + case BL_GOLD: + text = decode_mixed(goldbuf, text); + /*FALLTHRU*/ default: + attrmask = (color >> 8) & 0x00FF; +#ifndef TEXTCOLOR + color = NO_COLOR; +#endif + fmt = status_fieldfmt[fldidx]; + if (!fmt) + fmt = "%s"; + /* should be checking for first enabled field here rather than + just first field, but 'fieldorder' doesn't start any rows + with fields which can be disabled so [any_row][0] suffices */ + if (*fmt == ' ' && (fldidx == (*fieldorder)[0][0] + || fldidx == (*fieldorder)[1][0] + || fldidx == (*fieldorder)[2][0])) + ++fmt; /* skip leading space for first field on line */ + Sprintf(status_vals[fldidx], fmt, text); tty_status[NOW][fldidx].idx = fldidx; - Sprintf(status_vals[fldidx], - status_fieldfmt[fldidx] ? status_fieldfmt[fldidx] : "%s", - text); tty_status[NOW][fldidx].color = (color & 0x00FF); - tty_status[NOW][fldidx].attr = ((color >> 8) & 0x00FF); + tty_status[NOW][fldidx].attr = term_attr_fixup(attrmask); tty_status[NOW][fldidx].lth = strlen(status_vals[fldidx]); tty_status[NOW][fldidx].valid = TRUE; tty_status[NOW][fldidx].dirty = TRUE; @@ -3867,11 +3937,14 @@ unsigned long *colormasks; break; case BL_CAP: fval = status_vals[fldidx]; - if (fval) { + enclev = 0; + if (*fval) { + /* TODO: replace this with a callback to the core to + retrieve the cap index directly */ if (*fval == ' ') fval++; - for (i = 0; i < SIZE(encvals); ++i) { - if (!strcmp(encvals[enc_shrinklvl][i], fval)) { + for (i = 0; i < SIZE(encvals[0]); ++i) { + if (!strcmp(encvals[0][i], fval)) { enclev = i; break; /* for */ } @@ -3883,40 +3956,26 @@ unsigned long *colormasks; return; } -void -do_setlast() -{ - int i, row, fld; - - setlast = TRUE; - for (row = 0; row < 2; ++row) - for (i = MAX_PER_ROW - 1; i > 0; --i) { - fld = fieldorder[row][i]; - - if (fld == BL_FLUSH || !status_activefields[fld]) - continue; - - last_on_row[row] = fld; - break; - } -} - STATIC_OVL int make_things_fit(force_update) boolean force_update; { - int trycnt, fitting = 0, condsz = 0, requirement = 0; - int rowsz[2], otheroptions = 0; - boolean check = FALSE; + int trycnt, fitting = 0, condsz, requirement; + int rowsz[3], num_rows, condrow, otheroptions = 0; + num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3; + condrow = num_rows - 1; + cond_shrinklvl = 0; condsz = condition_size(); for (trycnt = 0; trycnt < 6 && !fitting; ++trycnt) { - check = check_fields(force_update, &rowsz[0], &rowsz[1]); - if (!check) + /* FIXME: this remeasures each line every time even though it + is only attempting to shrink one of them and the other one + (or two) remains the same */ + if (!check_fields(force_update, rowsz)) return 0; - requirement = rowsz[1]; - if (requirement < wins[WIN_STATUS]->cols - 1) { + requirement = rowsz[condrow] - 1; + if (requirement <= wins[WIN_STATUS]->cols - 1) { fitting = requirement; break; /* we're good */ } @@ -3924,7 +3983,6 @@ boolean force_update; if (cond_shrinklvl < trycnt + 1) { cond_shrinklvl = trycnt + 1; condsz = condition_size(); - cond_width_at_shrink = cond_disp_width[NOW]; } continue; } @@ -3932,9 +3990,11 @@ boolean force_update; /* We've exhausted the condition identifiers shrinkage, * so let's try something other things... */ - if (otheroptions == 0 || otheroptions == 1) { - /* try shrinking the encumbrance word */ - shrink_enc(otheroptions + 1); + if (otheroptions < 2) { + /* try shrinking the encumbrance word, but + only when it's on the same line as conditions */ + if (num_rows == 2) + shrink_enc(otheroptions + 1); otheroptions++; } else if (otheroptions == 2) { shrink_dlvl(1); @@ -3956,23 +4016,23 @@ boolean force_update; * This is now done at an individual field case-by-case level. */ boolean -check_fields(forcefields, topsz, bottomsz) +check_fields(forcefields, sz) boolean forcefields; -int *topsz, *bottomsz; +int sz[3]; { - int c, i, row, col, trackx, idx; - boolean valid = TRUE, matchprev = FALSE, update_right, disregard = FALSE; + int c, i, row, col, num_rows, idx; + boolean valid = TRUE, matchprev, update_right; if (!windowdata_init && !check_windowdata()) return FALSE; - for (row = 0; row < 2; ++row) { + num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3; + + for (row = 0; row < num_rows; ++row) { + sz[row] = 0; col = 1; - trackx = 1; update_right = FALSE; - idx = -1; - for (i = 0; fieldorder[row][i] != BL_FLUSH; ++i) { - idx = fieldorder[row][i]; + for (i = 0; (idx = (*fieldorder)[row][i]) != BL_FLUSH; ++i) { if (!status_activefields[idx]) continue; if (!tty_status[NOW][idx].valid) @@ -3983,109 +4043,98 @@ int *topsz, *bottomsz; /* On a change to the field length, everything further to the right must be updated as well */ - if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth) + if (tty_status[NOW][idx].lth != tty_status[BEFORE][idx].lth + || (idx == BL_CONDITION + && tty_status[NOW][idx].x != tty_status[BEFORE][idx].x)) update_right = TRUE; - if (idx == last_on_row[row]) - tty_status[NOW][idx].last_on_row = TRUE; - - if (!update_right && !forcefields) { + matchprev = FALSE; /* assume failure */ + if (valid && !update_right && !forcefields) { /* - * Check values against those already on the dislay. + * Check values against those already on the display. * - Is the additional processing time for this worth it? */ - if (do_field_opt) { - matchprev = FALSE; - disregard = (tty_status[NOW][idx].lth == 0); - if (do_field_opt && !disregard - && tty_status[NOW][idx].dirty) { + if (do_field_opt + && (tty_status[NOW][idx].color + == tty_status[BEFORE][idx].color) + && (tty_status[NOW][idx].attr + == tty_status[BEFORE][idx].attr)) { + matchprev = TRUE; /* assume success */ + if (tty_status[NOW][idx].dirty) { /* compare values */ - const char *ob, *nb; /* old byte, new byte */ + const char *ob, *nb; /* old byte, new byte */ + struct WinDesc *cw = wins[WIN_STATUS]; c = col - 1; - ob = &wins[WIN_STATUS]->data[row][c]; + ob = &cw->data[row][c]; nb = status_vals[idx]; - while (*nb && c < wins[WIN_STATUS]->cols) { + while (*nb && c < cw->cols) { if (*nb != *ob) break; nb++; ob++; c++; } - if (!*nb && c > col - 1) - matchprev = TRUE; + /* if we're not at the end of new string, no match; + we don't need to worry about whether there might + be leftover old string; that could only happen + if they have different lengths, in which case + 'update_right' will be set and we won't get here */ + if (*nb) + matchprev = FALSE; +#if 0 + if (*nb || (*ob && *ob != ' ' + && (*ob != '/' || idx != BL_XP) + && (*ob != '(' || (idx != BL_HP + && idx != BL_ENE)))) + matchprev = FALSE; +#endif } - } else { - matchprev = FALSE; } } - /* - * With STATUS_HILITES, it is possible that the color - * needs to change even if the text is the same, so - * we flag that here by setting .redraw. - * Then, render_status() will see that flag setting - * and ensure that the tty cell content is updated. - * After the field has been updated, render_status() - * will also clear .redraw and .dirty. - */ if (forcefields || update_right - || (!matchprev && tty_status[NOW][idx].dirty && !disregard) - || tty_status[NOW][idx].color != tty_status[BEFORE][idx].color - || tty_status[NOW][idx].attr != tty_status[BEFORE][idx].attr) + || (tty_status[NOW][idx].dirty && !matchprev)) tty_status[NOW][idx].redraw = TRUE; + col += tty_status[NOW][idx].lth; } - if (idx != -1) { - if (row && bottomsz) - *bottomsz = col + tty_status[NOW][idx].lth; - else if (topsz) - *topsz = col + tty_status[NOW][idx].lth; - } + sz[row] = col; } return valid; } /* * This is what places a field on the tty display. - * If val isn't null, it will be used rather than - * fld (it takes precedence). */ STATIC_OVL void -tty_putstatusfield(fld, val, x, y) -struct tty_status_fields *fld; -const char *val; +tty_putstatusfield(text, x, y) +const char *text; int x, y; { - int i, n, ncols, lth = 0; + int i, n, ncols, nrows, lth = 0; struct WinDesc *cw = 0; - const char *text = (char *)0; - if ((cw = wins[NHW_STATUS]) == (struct WinDesc *) 0) - panic("Invalid WinDesc\n"); + if (WIN_STATUS == WIN_ERR + || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) + panic("tty_putstatusfield: Invalid WinDesc\n"); ncols = cw->cols; - if (val) { - text = val; - lth = strlen(text); - } else if (fld) { - text = status_vals[fld->idx]; - lth = fld->lth; - } - if (!text) - return; + nrows = cw->maxrow; + lth = (int) strlen(text); print_vt_code2(AVTC_SELECT_WINDOW, NHW_STATUS); - if (x < ncols && y < 2) { - tty_curs(NHW_STATUS, x, y); + if (x < ncols && y < nrows) { + if (x != cw->curx || y != cw->cury) + tty_curs(NHW_STATUS, x, y); for (i = 0; i < lth; ++i) { n = i + x; if (n < ncols && *text) { (void) putchar(*text); ttyDisplay->curx++; cw->curx++; - cw->data[y][n-1] = *text; + cw->data[y][n - 1] = *text; text++; } } @@ -4096,23 +4145,23 @@ int x, y; } } +/* caller must set cond_shrinklvl (0..2) before calling us */ STATIC_OVL int condition_size() { - long mask = 0L; - int c, x; + long mask; + int c, lth; - x = 0; - for (c = 0; c < SIZE(conditions); ++c) { - mask = conditions[c].mask; - if ((tty_condition_bits & mask) == mask) { - x++; /* for spacer */ - x += (int) strlen(conditions[c].text[cond_shrinklvl]); + lth = 0; + if (tty_condition_bits) { + for (c = 0; c < SIZE(conditions); ++c) { + mask = conditions[c].mask; + if ((tty_condition_bits & mask) == mask) + lth += 1 + (int) strlen(conditions[c].text[cond_shrinklvl]); } } - tty_status[NOW][BL_CONDITION].lth = x; - cond_disp_width[NOW] = x; - return x; + tty_status[NOW][BL_CONDITION].lth = lth; + return lth; } STATIC_OVL void @@ -4120,9 +4169,9 @@ shrink_enc(lvl) int lvl; { /* shrink or restore the encumbrance word */ - if (lvl == 0 || lvl <= 2) { + if (lvl <= 2) { enc_shrinklvl = lvl; - Strcpy(status_vals[BL_CAP], encvals[lvl][enclev]); + Sprintf(status_vals[BL_CAP], " %s", encvals[lvl][enclev]); } tty_status[NOW][BL_CAP].lth = strlen(status_vals[BL_CAP]); } @@ -4154,14 +4203,7 @@ check_windowdata(VOID_ARGS) paniclog("check_windowdata", " null status window."); return FALSE; } else if (!windowdata_init) { - int i, n = wins[WIN_STATUS]->cols; - - for (i = 0; i < wins[WIN_STATUS]->cols; ++i) { - wins[WIN_STATUS]->data[0][i] = ' '; - wins[WIN_STATUS]->data[1][i] = ' '; - } - wins[WIN_STATUS]->data[0][n - 1] = '\0'; /* null terminate */ - wins[WIN_STATUS]->data[1][n - 1] = '\0'; /* null terminate */ + tty_clear_nhwindow(WIN_STATUS); /* also sets cw->data[] to spaces */ windowdata_init = TRUE; } return TRUE; @@ -4263,10 +4305,10 @@ unsigned long *bmarray; STATIC_OVL void render_status(VOID_ARGS) { - long mask = 0L; - int i, c, row, attrmask = 0; + long mask, bits; + int i, x, y, idx, c, row, tlth, num_rows, coloridx = 0, attrmask = 0; + char *text; struct WinDesc *cw = 0; - struct tty_status_fields *nullfield = (struct tty_status_fields *) 0; if (WIN_STATUS == WIN_ERR || (cw = wins[WIN_STATUS]) == (struct WinDesc *) 0) { @@ -4274,22 +4316,19 @@ render_status(VOID_ARGS) return; } - for (row = 0; row < 2; ++row) { + num_rows = (iflags.wc2_statuslines < 3) ? 2 : 3; + for (row = 0; row < num_rows; ++row) { HUPSKIP(); - curs(WIN_STATUS, 1, row); - for (i = 0; fieldorder[row][i] != BL_FLUSH; ++i) { - int idx = fieldorder[row][i]; - + y = row; + tty_curs(WIN_STATUS, 1, y); + for (i = 0; (idx = (*fieldorder)[row][i]) != BL_FLUSH; ++i) { if (!status_activefields[idx]) continue; + x = tty_status[NOW][idx].x; + text = status_vals[idx]; + tlth = (int) tty_status[NOW][idx].lth; - if ((tty_status[NOW][idx].lth || idx == BL_CONDITION) - && (tty_status[NOW][idx].redraw || !do_field_opt)) { - int coloridx = tty_status[NOW][idx].color; - int attridx = tty_status[NOW][idx].attr; - int x = tty_status[NOW][idx].x; - int y = row; - char *text = status_vals[idx]; + if (tty_status[NOW][idx].redraw || !do_field_opt) { boolean hitpointbar = (idx == BL_TITLE && iflags.wc2_hitpointbar); @@ -4299,68 +4338,77 @@ render_status(VOID_ARGS) * | Condition Codes | * +-----------------+ */ + if (!tty_condition_bits) + continue; + if (num_rows == 3) { + int k; + char *dat = &cw->data[y][0]; + + /* line up with hunger (or where it would have + been when currently omitted); if there isn't + enough room for that, right justify; or place + as-is if not even enough room for /that/; we + expect hunger to be on preceding row, in which + case its current data has been moved to [BEFORE] */ + if (tty_status[BEFORE][BL_HUNGER].y < row + && x < tty_status[BEFORE][BL_HUNGER].x + && (tty_status[BEFORE][BL_HUNGER].x + tlth + < cw->cols - 1)) + k = tty_status[BEFORE][BL_HUNGER].x; + else if (x + tlth < cw->cols - 1) + k = cw->cols - tlth; + else + k = x; + while (x < k) { + if (dat[x - 1] != ' ') + tty_putstatusfield(" ", x, y); + ++x; + } + tty_status[NOW][BL_CONDITION].x = x; + tty_curs(WIN_STATUS, x, y); + } + bits = tty_condition_bits; for (c = 0; c < SIZE(conditions); ++c) { mask = conditions[c].mask; - if ((tty_condition_bits & mask) == mask) { + if (bits & mask) { const char *condtext; - tty_putstatusfield(nullfield, " ", x++, y); + tty_putstatusfield(" ", x++, y); if (iflags.hilite_delta) { attrmask = condattr(mask, tty_colormasks); Begin_Attr(attrmask); coloridx = condcolor(mask, tty_colormasks); - if (coloridx != NO_COLOR - && coloridx != CLR_MAX) + if (coloridx != NO_COLOR) term_start_color(coloridx); } - if (x >= cw->cols && !truncation_expected) + condtext = conditions[c].text[cond_shrinklvl]; + if (x >= cw->cols && !truncation_expected) { impossible( "Unexpected condition placement overflow"); - condtext = conditions[c].text[cond_shrinklvl]; - tty_putstatusfield(nullfield, condtext, x, y); + condtext = ""; + } + tty_putstatusfield(condtext, x, y); x += (int) strlen(condtext); if (iflags.hilite_delta) { - if (coloridx != NO_COLOR - && coloridx != CLR_MAX) + if (coloridx != NO_COLOR) term_end_color(); End_Attr(attrmask); } + if (!(bits &= ~mask)) + break; } } - if (x >= cw->cols) { + /* 'x' is 1-based and 'cols' and 'data' are 0-based, + so x==cols means we just stored in data[N-2] and + are now positioned at data[N-1], the terminator; + that's ok as long as we don't write there */ + if (x > cw->cols) { static unsigned once_only = 0; if (!truncation_expected && !once_only++) - paniclog( - "render_status()", " unexpected truncation."); - x = cw->cols - 1; - } - tty_curs(WIN_STATUS, x, y); - cl_end(); - } else if (idx == BL_GOLD) { - char buf[BUFSZ]; - /* - * +-----------+ - * | Gold | - * +-----------+ - */ - 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) - term_start_color(coloridx); - } - /* decode_mixed() due to GOLD glyph */ - tty_putstatusfield(nullfield, - decode_mixed(buf, text), x, y); - if (iflags.hilite_delta) { - if (coloridx != NO_COLOR && coloridx != CLR_MAX) - term_end_color(); - End_Attr(attridx); + paniclog("render_status()", + " unexpected truncation."); + x = cw->cols; } } else if (hitpointbar) { /* @@ -4381,6 +4429,7 @@ render_status(VOID_ARGS) } else Strcpy(bar, text); bar_len = (int) strlen(bar); /* always 30 */ + tlth = bar_len + 2; /* when at full HP, the whole title will be highlighted; when injured or dead, there will be a second portion which is not highlighted */ @@ -4395,12 +4444,12 @@ render_status(VOID_ARGS) savedch = *bar2; *bar2 = '\0'; } - tty_putstatusfield(nullfield, "[", x++, y); + tty_putstatusfield("[", x++, y); if (*bar) { /* always True, unless twoparts+dead (0 HP) */ term_start_attr(ATR_INVERSE); if (iflags.hilite_delta && hpbar_color != NO_COLOR) term_start_color(hpbar_color); - tty_putstatusfield(nullfield, bar, x, y); + tty_putstatusfield(bar, x, y); x += (int) strlen(bar); if (iflags.hilite_delta && hpbar_color != NO_COLOR) term_end_color(); @@ -4408,11 +4457,10 @@ render_status(VOID_ARGS) } if (twoparts) { /* no highlighting for second part */ *bar2 = savedch; - tty_putstatusfield(nullfield, bar2, x, y); + tty_putstatusfield(bar2, x, y); x += (int) strlen(bar2); - /*tty_curs(WIN_STATUS, x, y);*/ } - tty_putstatusfield(nullfield, "]", x++, y); + tty_putstatusfield("]", x++, y); } else { /* * +-----------------------------+ @@ -4421,43 +4469,47 @@ render_status(VOID_ARGS) * +-----------------------------+ */ if (iflags.hilite_delta) { - if (*text == ' ') { - tty_putstatusfield(nullfield, " ", x++, y); + while (*text == ' ') { + tty_putstatusfield(" ", x++, y); text++; } - /* multiple attributes can be in effect concurrently */ - Begin_Attr(attridx); - if (coloridx != NO_COLOR && coloridx != CLR_MAX) + if (*text == '/' && idx == BL_EXP) { + tty_putstatusfield("/", x++, y); + text++; + } + attrmask = tty_status[NOW][idx].attr; + Begin_Attr(attrmask); + coloridx = tty_status[NOW][idx].color; + if (coloridx != NO_COLOR) term_start_color(coloridx); } - tty_putstatusfield(&tty_status[NOW][idx], text, x, y); + tty_putstatusfield(text, x, y); + x += (int) strlen(text); if (iflags.hilite_delta) { - if (coloridx != NO_COLOR && coloridx != CLR_MAX) + if (coloridx != NO_COLOR) term_end_color(); - End_Attr(attridx); + End_Attr(attrmask); } } - if (tty_status[NOW][idx].last_on_row) { - int padright = 0; - - x = tty_status[NOW][idx].x + tty_status[NOW][idx].lth; - finalx[row][NOW] = x - 1; - if (finalx[row][NOW] < finalx[row][BEFORE]) - padright = finalx[row][BEFORE] - finalx[row][NOW]; - while (padright-- > 0) - tty_putstatusfield(nullfield, " ", x++, y); - } + } else { + /* not rendered => same text as before */ + x += tlth; } + finalx[row][NOW] = x - 1; /* reset .redraw, .dirty, .padright now that they're rendered */ tty_status[NOW][idx].dirty = FALSE; tty_status[NOW][idx].redraw = FALSE; - tty_status[NOW][idx].last_on_row = FALSE; - /* * For comparison of current and previous: * - Copy the entire tty_status struct. */ - tty_status[BEFORE][idx] = tty_status[NOW][idx]; + tty_status[BEFORE][idx] = tty_status[NOW][idx]; + } + x = finalx[row][NOW]; + if ((x < finalx[row][BEFORE] || !finalx[row][BEFORE]) + && x + 1 < cw->cols) { + tty_curs(WIN_STATUS, x + 1, y); + cl_end(); } /* * For comparison of current and previous: @@ -4465,12 +4517,6 @@ render_status(VOID_ARGS) */ finalx[row][BEFORE] = finalx[row][NOW]; } - if (cond_disp_width[NOW] < cond_width_at_shrink) { - cond_shrinklvl = 0; /* reset */ - cond_width_at_shrink = condition_size(); - shrink_enc(0); - shrink_dlvl(0); - } return; } From be40ff310485857780b8fa4c5590413ff686fe19 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Fri, 29 Mar 2019 16:33:51 +0200 Subject: [PATCH 04/17] Cursor targeting help improvement Based on feedback from users, explicitly show that m/M keys cycle to next/previous monster, and so on. --- src/do_name.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/do_name.c b/src/do_name.c index c4605b479..b0d113fd8 100644 --- a/src/do_name.c +++ b/src/do_name.c @@ -51,9 +51,9 @@ boolean FDECL((*gp_getvalidf), (int, int)); } static const char *const gloc_descr[NUM_GLOCS][4] = { - { "any monsters", "monster", "next monster", "monsters" }, - { "any items", "item", "next object", "objects" }, - { "any doors", "door", "next door or doorway", "doors or doorways" }, + { "any monsters", "monster", "next/previous monster", "monsters" }, + { "any items", "item", "next/previous object", "objects" }, + { "any doors", "door", "next/previous door or doorway", "doors or doorways" }, { "any unexplored areas", "unexplored area", "unexplored location", "unexplored locations" }, { "anything interesting", "interesting thing", "anything interesting", @@ -77,7 +77,7 @@ int gloc; { char sbuf[BUFSZ]; - Sprintf(sbuf, "Use '%s' or '%s' to %s%s%s.", + Sprintf(sbuf, "Use '%s'/'%s' to %s%s%s.", k1, k2, iflags.getloc_usemenu ? "get a menu of " : "move the cursor to ", From 25a456bb2a64d38d11657dcb20ecfc5bfbce21e8 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Mar 2019 11:50:56 -0700 Subject: [PATCH 05/17] getpos when teleporting Using repeated ^T to become hungry was very tedious due to the extra response required every time. --- doc/fixes36.2 | 9 ++++++++- src/teleport.c | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 72cb9856f..c05935508 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.287 $ $NHDT-Date: 1553858472 2019/03/29 11:21:12 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.288 $ $NHDT-Date: 1553885439 2019/03/29 18:50:39 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -401,6 +401,8 @@ using Cleaver to attack a worm tail segment but kill adjacent head first would Valkyrie quest was supposed to have a 50:50 chance that northern drawbridge would be raised, but both were always lowered; chances now are: both lowered: 3/8, S down+N up: 3/8, N down+S up: 1/8, both raised: 1/8 +shorten the getpos prompt for teleport destination so that it won't yield a + --More-- prompt for the help hint (--More-- still possible when riding) Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository @@ -481,6 +483,11 @@ tty: fix leftover display artifact when the last field on the row got placed to the left of where it was previously due to it, or one of the fields to its left, getting shorter tty: support hitpointbar even when statushilites is set to 0 +tty: when status condition names were abbreviated due to lack of room, they + were inconsistent about re-expanding when more room became available +tty: when encumbrance state was abbreviated due to lack of room, there was no + separation from preceding status field and the value would not update + when the state changed (except for removal on change to unencumbered) X11: its use of genl_status_update exposed a negative index use that could lead to a segfault X11: rollback disabling of keystroke input for PICK_NONE menus (for scrolling) diff --git a/src/teleport.c b/src/teleport.c index 4ae361796..76d36f35c 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 teleport.c $NHDT-Date: 1550524567 2019/02/18 21:16:07 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.85 $ */ +/* NetHack 3.6 teleport.c $NHDT-Date: 1553885439 2019/03/29 18:50:39 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.86 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2011. */ /* NetHack may be freely redistributed. See license for details. */ @@ -478,7 +478,7 @@ struct obj *scroll; Strcpy(whobuf, "you"); if (u.usteed) Sprintf(eos(whobuf), " and %s", mon_nam(u.usteed)); - pline("To what position do %s want to be teleported?", whobuf); + pline("Where do %s want to be teleported?", whobuf); cc.x = u.ux; cc.y = u.uy; if (getpos(&cc, TRUE, "the desired position") < 0) From 14d8ed199ec95f6a5c130183fd01e568d63e2bb4 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Mar 2019 14:35:36 -0700 Subject: [PATCH 06/17] tty: panning while clipped Noticed while testing statuslines on a small terminal window. Using the cursor to pick locations that panned the map to view a new subset would end up showing a new view of the regular map rather than a different section of what was currently displayed. For farlook that caused monsters to take on new hallucinatory forms which was fairly inconsequential, but for #terrain and various forms of detection it reverted to the ordinary map instead of showing the map features that the player requested or the temporarily revealed monsters and such. Most interfaces keep track of the whole map and just show their view of the new subset when panning, similar to redisplay after being covered up and then re-exposed, but tty isn't doing that. I made same change to Amiga as to tty since the code it was using was very similar. I haven't touched any of the other interfaces and assume that they don't need this. I've verified that curses and X11 don't. --- doc/fixes36.2 | 7 +++++- include/extern.h | 3 ++- src/display.c | 58 +++++++++++++++++++++++++++++++++----------- sys/amiga/winfuncs.c | 10 +++----- win/tty/wintty.c | 6 ++--- 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index c05935508..ce18f2e31 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.288 $ $NHDT-Date: 1553885439 2019/03/29 18:50:39 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.289 $ $NHDT-Date: 1553895318 2019/03/29 21:35:18 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -488,6 +488,11 @@ tty: when status condition names were abbreviated due to lack of room, they tty: when encumbrance state was abbreviated due to lack of room, there was no separation from preceding status field and the value would not update when the state changed (except for removal on change to unencumbered) +tty: when clipping was used to show a subset of the map on a small display, + panning into a new subset while using the cursor to pick a location + would ask the core to generate a new view of the map rather than use + whatever was currently shown, bringing back suppressed monsters/ + objects/traps for #terrain and new hallucinatory monsters for farlook X11: its use of genl_status_update exposed a negative index use that could lead to a segfault X11: rollback disabling of keystroke input for PICK_NONE menus (for scrolling) diff --git a/include/extern.h b/include/extern.h index f35623f9a..9fa6dfc0b 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1552945074 2019/03/18 21:37:54 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.695 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1553895318 2019/03/29 21:35:18 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.696 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -351,6 +351,7 @@ E void NDECL(see_traps); E void NDECL(curs_on_u); E int NDECL(doredraw); E void NDECL(docrt); +E void NDECL(redraw_map); E void FDECL(show_glyph, (int, int, int)); E void NDECL(clear_glyph_buffer); E void FDECL(row_refresh, (int, int, int)); diff --git a/src/display.c b/src/display.c index 281b5f57e..7666aa928 100644 --- a/src/display.c +++ b/src/display.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 display.c $NHDT-Date: 1551138503 2019/02/25 23:48:23 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.97 $ */ +/* NetHack 3.6 display.c $NHDT-Date: 1553895319 2019/03/29 21:35:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.98 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1213,8 +1213,7 @@ int mode; } } -/* ========================================================================= - */ +/* ======================================================================== */ /* * Loop through all of the monsters and update them. Called when: @@ -1380,10 +1379,39 @@ docrt() context.botlx = 1; /* force a redraw of the bottom line */ } -/* ========================================================================= - */ -/* Glyph Buffering (3rd screen) ============================================ - */ +/* for panning beyond a clipped region; resend the current map data to + the interface rather than use docrt()'s regeneration of that data */ +void +redraw_map() +{ + int x, y, glyph; + + /* + * Not sure whether this is actually necessary; save and restore did + * used to get much too involved with each dungeon level as it was + * read and written. + * + * !u.ux: display isn't ready yet; (restoring || !on_level()): was part + * of cliparound() but interface shouldn't access this much internals + */ + if (!u.ux || restoring || !on_level(&u.uz0, &u.uz)) + return; + + /* + * This yields sensible clipping when #terrain+getpos is in + * progress and the screen displays something other than what + * the map would currently be showing. + */ + for (y = 0; y < ROWNO; ++y) + for (x = 1; x < COLNO; ++x) { + glyph = glyph_at(x, y); /* not levl[x][y].glyph */ + print_glyph(WIN_MAP, x, y, glyph, get_bk_glyph(x, y)); + } + flush_screen(1); +} + +/* ======================================================================== */ +/* Glyph Buffering (3rd screen) =========================================== */ typedef struct { xchar new; /* perhaps move this bit into the rm structure. */ @@ -1536,7 +1564,7 @@ int start, stop, y; for (x = start; x <= stop; x++) if (gbuf[y][x].glyph != cmap_to_glyph(S_stone)) - print_glyph(WIN_MAP, x, y, gbuf[y][x].glyph, get_bk_glyph(x,y)); + print_glyph(WIN_MAP, x, y, gbuf[y][x].glyph, get_bk_glyph(x, y)); } void @@ -1599,8 +1627,7 @@ int cursor_on_u; bot(); } -/* ========================================================================= - */ +/* ======================================================================== */ /* * back_to_glyph() @@ -1804,16 +1831,19 @@ xchar x, y; /* * This will be used to get the glyph for the background so that - * it can potentially be merged into graphical window ports - * to improve the appearance of stuff on dark room - * squares and the plane of air etc. + * it can potentially be merged into graphical window ports to + * improve the appearance of stuff on dark room squares and the + * plane of air etc. * * Until that is working correctly in the branch, however, for now * we just return NO_GLYPH as an indicator to ignore it. + * + * [This should be using background as recorded for #overview rather + * than current data from the map.] */ STATIC_OVL int -get_bk_glyph(x,y) +get_bk_glyph(x, y) xchar x, y; { int idx, bkglyph = NO_GLYPH; diff --git a/sys/amiga/winfuncs.c b/sys/amiga/winfuncs.c index d565b1af4..84838ed22 100644 --- a/sys/amiga/winfuncs.c +++ b/sys/amiga/winfuncs.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 winfuncs.c $NHDT-Date: 1433806596 2015/06/08 23:36:36 $ $NHDT-Branch: master $:$NHDT-Revision: 1.15 $ */ +/* NetHack 3.6 winfuncs.c $NHDT-Date: 1553895320 2019/03/29 21:35:20 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.16 $ */ /* Copyright (c) Gregg Wonderly, Naperville, Illinois, 1991,1992,1993,1996. */ /* NetHack may be freely redistributed. See license for details. */ @@ -2131,13 +2131,13 @@ void amii_cliparound(x, y) register int x, y; { - extern boolean restoring; #ifdef CLIPPING int oldx = clipx, oldy = clipy; int oldxmax = clipxmax, oldymax = clipymax; int COx, LIx; #define SCROLLCNT 1 /* Get there in 3 moves... */ int scrollcnt = SCROLLCNT; /* ...or 1 if we changed level */ + if (!clipping) /* And 1 in anycase, cleaner, simpler, quicker */ return; @@ -2306,8 +2306,7 @@ register int x, y; clipymax += incy; /* Draw the exposed portion */ - if (on_level(&u.uz0, &u.uz) && !restoring) - (void) doredraw(); + redraw_map(); flush_glyph_buffer(amii_wins[WIN_MAP]->win); } } @@ -2317,8 +2316,7 @@ register int x, y; clipymax = saveymax; clipxmax = savexmax; #endif - if (on_level(&u.uz0, &u.uz) && !restoring && moves > 1) - (void) doredraw(); + redraw_map(); flush_glyph_buffer(amii_wins[WIN_MAP]->win); } reclip = 0; diff --git a/win/tty/wintty.c b/win/tty/wintty.c index fe06b8180..7520b14ff 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1553858474 2019/03/29 11:21:14 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.199 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1553895321 2019/03/29 21:35:21 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.200 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -3315,7 +3315,6 @@ void tty_cliparound(x, y) int x, y; { - extern boolean restoring; int oldx = clipx, oldy = clipy; HUPSKIP(); @@ -3336,8 +3335,7 @@ int x, y; clipy = clipymax - (LI - 1 - iflags.wc2_statuslines); } if (clipx != oldx || clipy != oldy) { - if (on_level(&u.uz0, &u.uz) && !restoring) - (void) doredraw(); + redraw_map(); /* ask the core to resend the map window's data */ } } #endif /* CLIPPING */ From 68542da636b911b12a9721a6ee2d6b901dcc3d80 Mon Sep 17 00:00:00 2001 From: PatR Date: Fri, 29 Mar 2019 17:03:03 -0700 Subject: [PATCH 07/17] curses: save/restore message history Have the curses interface save and restore message history for use by ^P. It doesn't spit the saved messages out into the visible message window after restore; that's too distracting. --- doc/fixes36.2 | 3 +- include/wincurs.h | 2 + win/curses/cursmain.c | 4 +- win/curses/cursmesg.c | 95 +++++++++++++++++++++++++++++++++++++++++++ win/curses/cursmesg.h | 2 + 5 files changed, 103 insertions(+), 3 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index ce18f2e31..edbd2c4ca 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.289 $ $NHDT-Date: 1553895318 2019/03/29 21:35:18 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.290 $ $NHDT-Date: 1553904170 2019/03/30 00:02:50 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -523,6 +523,7 @@ curses: the memory deallocation was releasing previous messages when curses curses: popup window to show ^P output was removed from screen but never deleted; further ^P's repeated that cycle; likewise for help which displays an external text file +curses: preserve ^P message history across save/restore vms: add compile of isaac64.c to Makefile.src and vmsbuild.com vms+curses: add compile support but it is known to fail to build diff --git a/include/wincurs.h b/include/wincurs.h index 79893ddcd..165facaba 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -222,6 +222,8 @@ extern void curses_init_mesg_history(void); extern void curses_teardown_messages(void); extern void curses_prev_mesg(void); extern void curses_count_window(const char *count_text); +char *curses_getmsghistory(BOOLEAN_P); +void curses_putmsghistory(const char *, BOOLEAN_P); #endif /* WINCURS_H */ diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 100719370..8dba85a93 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -72,8 +72,8 @@ struct window_procs curses_procs = { curses_end_screen, genl_outrip, curses_preference_update, - genl_getmsghistory, - genl_putmsghistory, + curses_getmsghistory, + curses_putmsghistory, curses_status_init, curses_status_finish, genl_status_enablefield, diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index ccbc6f1e0..57cc5636b 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -693,4 +693,99 @@ get_msg_line(boolean reverse, int mindex) return current_mesg; } +/* save/restore code retrieves one ^P message at a time during save and + puts it into save file; if any new messages are added to the list while + that is taking place, the results are likely to be scrambled */ +char * +curses_getmsghistory(init) +boolean init; +{ + static int nxtidx; + nhprev_mesg *mesg; + + if (init) + nxtidx = 0; + else + ++nxtidx; + + if (nxtidx < num_messages) { + /* we could encode mesg->turn with the text of the message, + but then that text might need to be truncated, and more + significantly, restoring the save file with another + interface wouldn't know how find and decode or remove it; + likewise, restoring another interface's save file with + curses wouldn't find the expected turn info; + so, we live without that */ + mesg = get_msg_line(FALSE, nxtidx); + } else + mesg = (nhprev_mesg *) 0; + + return mesg ? mesg->str : (char *) 0; +} + +/* + * This is called by the core savefile restore routines. + * Each time we are called, we stuff the string into our message + * history recall buffer. The core will send the oldest message + * first (actually it sends them in the order they exist in the + * save file, but that is supposed to be the oldest first). + * These messages get pushed behind any which have been issued + * during this session since they come from a previous session + * and logically precede anything (like "Restoring save file...") + * that's happened now. + * + * Called with a null pointer to finish up restoration. + * + * It's also called by the quest pager code when a block message + * has a one-line summary specified. We put that line directly + * into message history for ^P recall without having displayed it. + */ +void +curses_putmsghistory(msg, restoring_msghist) +const char *msg; +boolean restoring_msghist; +{ + static boolean initd = FALSE; + static int stash_count; + static nhprev_mesg *stash_head = 0; + + if (restoring_msghist && !initd) { + /* hide any messages we've gathered since starting current session + so that the ^P data will start out empty as we add ones being + restored from save file; we'll put these back after that's done */ + stash_count = num_messages, num_messages = 0; + stash_head = first_mesg, first_mesg = (nhprev_mesg *) 0; + last_mesg = (nhprev_mesg *) 0; /* no need to remember the tail */ + initd = TRUE; + } + + if (msg) { + mesg_add_line(msg); + /* treat all saved and restored messages as turn #1 */ + last_mesg->turn = 1L; + } else if (stash_count) { + nhprev_mesg *mesg; + long mesg_turn; + + /* put any messages generated during the beginning of the current + session back; they logically follow any from the previous + session's save file */ + while (stash_count > 0) { + /* we could manipulate the linked list directly but treating + stashed messages as newly occurring ones is much simpler; + we ignore the backlinks because the list is destroyed as it + gets processed hence there can't be any other traversals */ + mesg = stash_head; + stash_head = mesg->next_mesg; + --stash_count; + mesg_turn = mesg->turn; + mesg_add_line(mesg->str); + last_mesg->turn = mesg_turn; + free((genericptr_t) mesg->str); + free((genericptr_t) mesg); + } + initd = FALSE; /* reset */ + } +} + /*cursmesg.c*/ diff --git a/win/curses/cursmesg.h b/win/curses/cursmesg.h index f6002356d..803b0fb5b 100644 --- a/win/curses/cursmesg.h +++ b/win/curses/cursmesg.h @@ -18,5 +18,7 @@ void curses_last_messages(void); void curses_init_mesg_history(void); void curses_prev_mesg(void); void curses_count_window(const char *count_text); +char *curses_getmsghistory(BOOLEAN_P); +void curses_putmsghistory(const char *, BOOLEAN_P); #endif /* CURSMESG_H */ From 4ca8f6428bff16c6b53c93f4b7fbf52d06781590 Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 30 Mar 2019 17:46:16 -0700 Subject: [PATCH 08/17] status line title field Status formatting used to truncate the Name portion of "Name the Rank" or "Name the Monster-type" at 10 characters even if the rank or monster portion left room for more. Change that to keep as much of the name as will fit. The truncation might vary over time as new experience levels produce new rank titles of differing lengths, but I don't think that's a problem. For truncated names, it still keeps at least 10 characters even if that leaves the field longer than the target length for title (which used to be 29 but now is 30). --- src/botl.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/botl.c b/src/botl.c index 92a2335e7..441581292 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1553387148 2019/03/24 00:25:48 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.137 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1553993169 2019/03/31 00:46:09 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.138 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -524,9 +524,11 @@ unsigned long cond_hilites[BL_ATTCLR_MAX]; void bot_via_windowport() { + static int idx = 0; char buf[BUFSZ]; + const char *titl; register char *nb; - static int i, idx = 0, idx_p, cap; + int i, idx_p, cap; long money; if (!blinit) @@ -549,15 +551,23 @@ bot_via_windowport() */ Strcpy(nb = buf, plname); nb[0] = highc(nb[0]); - nb[10] = '\0'; + titl = !Upolyd ? rank() : mons[u.umonnum].mname; + i = (int) (strlen(buf) + sizeof " the " + strlen(titl) - sizeof ""); + /* if "Name the Rank/monster" is too long, we truncate the name + but always keep at least 10 characters of it; when hitpintbar is + enabled, anything beyond 30 (long monster name) will be truncated */ + if (i > 30) { + i = 30 - (int) (sizeof " the " + strlen(titl) - sizeof ""); + nb[max(i, 10)] = '\0'; + } Strcpy(nb = eos(nb), " the "); - if (Upolyd) { - for (i = 0, nb = strcpy(eos(nb), mons[u.umonnum].mname); nb[i]; i++) + Strcpy(nb = eos(nb), titl); + if (Upolyd) { /* when poly'd, capitalize monster name */ + for (i = 0; nb[i]; i++) if (i == 0 || nb[i - 1] == ' ') nb[i] = highc(nb[i]); - } else - Strcpy(nb = eos(nb), rank()); - Sprintf(blstats[idx][BL_TITLE].val, "%-29s", buf); + } + Sprintf(blstats[idx][BL_TITLE].val, "%-30s", buf); valset[BL_TITLE] = TRUE; /* indicate val already set */ /* Strength */ From c63d3fbfbbbf225c864b49454792f69ada042aac Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Mar 2019 00:33:33 -0700 Subject: [PATCH 09/17] bogus status updates I finally figured out why status gets updated periodically even if none of the fields have changed. Once a temporary highlight times out, it starts a cycle of timeouts every 'statushilites' turns. When I worked on this before, it was convoluted but not this convoluted. In moveloop, if 'context.botl' call bot; in bot call evaluate_and_notify_windowport; for each field, call evaluate_and_notify_windowport_field: call hilite_reset_needed and set 'reset' to the result; if 'reset' is True then do status_update and set 'curr->chg' and 'prev->chg' to True. Then in moveloop call status_eval_next_unhilite: for each field if 'curr->chg' set 'curr->time' to moves+hilite_delta; on the call after hilite_delta ('statushilites') moves, call hilite_reset_needed which returns True if there is any rule for temporary highlight and set 'context.botl'. Go back to start. If multiple fields had temporary timeouts and they were activated on different turns so expired on different turns you could conceivably end up with context.botl being set every turn. My first writeup trying to explain all this was wrong. I won't testify about the accuracy of this one in court.... This extends the highlighting data structure to track the current rule that's in use. And for that to make sense, it eliminates the merging of settings from multiple matching rules. So anybody with hit-points/up/inverse hit-points/up/green hit-points/up/bold will need to manually merge their rules like hit-points/up/green&inverse+bold or else whichever rule matches last will be the only one in effect. There are a lot of miscellaneous changes made as I flailed about. The three most significant ones are that there is no guesswork over what kind of highlight rule is in effect, status_eval_next_unhilite will only set a timeout value if the current rule is for a temporary highlight, and hilite_reset_needed will only return True if a timeout is for a temporary highlight (probably moot after the _next_unhilite change). --- doc/fixes36.2 | 5 +- src/botl.c | 327 +++++++++++++++++++++++++------------------------- 2 files changed, 165 insertions(+), 167 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index edbd2c4ca..864175d99 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.290 $ $NHDT-Date: 1553904170 2019/03/30 00:02:50 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.291 $ $NHDT-Date: 1554017610 2019/03/31 07:33:30 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -403,6 +403,9 @@ Valkyrie quest was supposed to have a 50:50 chance that northern drawbridge lowered: 3/8, S down+N up: 3/8, N down+S up: 1/8, both raised: 1/8 shorten the getpos prompt for teleport destination so that it won't yield a --More-- prompt for the help hint (--More-- still possible when riding) +once a status highlight for a temporary rule ('up', 'down', 'changed') timed + out, further spurious status updates (evaluating all fields) would + occur every 'statushilites' turns even if no fields had changed Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/src/botl.c b/src/botl.c index 441581292..545e5cd6a 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1553993169 2019/03/31 00:46:09 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.138 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1554017610 2019/03/31 07:33:30 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.139 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -399,6 +399,8 @@ char *buf; /* structure that tracks the status details in the core */ +#define MAXVALWIDTH 80 /* actually less, but was using 80 to allocate title + * and leveldesc then using QBUFSZ everywhere else */ #ifdef STATUS_HILITES struct hilite_s { enum statusfields fld; @@ -406,7 +408,7 @@ struct hilite_s { unsigned anytype; anything value; int behavior; - char textmatch[QBUFSZ]; + char textmatch[MAXVALWIDTH]; enum relationships rel; int coloridx; struct hilite_s *next; @@ -425,30 +427,29 @@ struct istat_s { enum statusfields idxmax; enum statusfields fld; #ifdef STATUS_HILITES + struct hilite_s *hilite_rule; /* the entry, if any, in 'thresholds' + * list that currently applies */ struct hilite_s *thresholds; #endif }; - STATIC_DCL void NDECL(init_blstats); STATIC_DCL char *FDECL(anything_to_s, (char *, anything *, int)); STATIC_OVL int FDECL(percentage, (struct istat_s *, struct istat_s *)); STATIC_OVL int FDECL(compare_blstats, (struct istat_s *, struct istat_s *)); -STATIC_DCL boolean FDECL(evaluate_and_notify_windowport_field, - (int, boolean *, int, int)); -STATIC_DCL void FDECL(evaluate_and_notify_windowport, (boolean *, int, int)); +STATIC_DCL boolean FDECL(eval_notify_windowport_field, (int, boolean *, int)); +STATIC_DCL void FDECL(evaluate_and_notify_windowport, (boolean *, int)); #ifdef STATUS_HILITES STATIC_DCL boolean FDECL(hilite_reset_needed, (struct istat_s *, long)); STATIC_DCL void FDECL(s_to_anything, (anything *, char *, int)); 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_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 *)); +STATIC_DCL struct hilite_s *FDECL(get_hilite, (int, int, genericptr_t, + 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 *)); @@ -466,18 +467,25 @@ 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) +/* TH_UPDOWN encompasses specific 'up' and 'down' also general 'changed' */ +#define Is_Temp_Hilite(rule) ((rule) && (rule)->behavior == BL_TH_UPDOWN) + +/* pointers to current hilite rule and list of this field's defined rules */ +#define INIT_THRESH , (struct hilite_s *) 0, (struct hilite_s *) 0 +#else /* !STATUS_HILITES */ +#define INIT_THRESH /*empty*/ #endif #define INIT_BLSTAT(name, fmtstr, anytyp, wid, fld) \ { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \ - wid, -1, fld } + wid, -1, fld INIT_THRESH } #define INIT_BLSTATP(name, fmtstr, anytyp, wid, maxfld, fld) \ { name, fmtstr, 0L, FALSE, anytyp, { (genericptr_t) 0 }, (char *) 0, \ - wid, maxfld, fld } + wid, maxfld, fld INIT_THRESH } /* If entries are added to this, botl.h will require updating too */ STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = { - INIT_BLSTAT("title", "%s", ANY_STR, 80, BL_TITLE), + INIT_BLSTAT("title", "%s", ANY_STR, MAXVALWIDTH, BL_TITLE), INIT_BLSTAT("strength", " St:%s", ANY_INT, 10, BL_STR), INIT_BLSTAT("dexterity", " Dx:%s", ANY_INT, 10, BL_DX), INIT_BLSTAT("constitution", " Co:%s", ANY_INT, 10, BL_CO), @@ -498,18 +506,18 @@ STATIC_DCL struct istat_s initblstats[MAXBLSTATS] = { 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), + INIT_BLSTAT("dungeon-level", "%s", ANY_STR, MAXVALWIDTH, BL_LEVELDESC), INIT_BLSTAT("experience", "/%s", ANY_LONG, 20, BL_EXP), INIT_BLSTAT("condition", "%s", ANY_MASK32, 0, BL_CONDITION) }; #undef INIT_BLSTATP #undef INIT_BLSTAT +#undef INIT_THRESH struct istat_s blstats[2][MAXBLSTATS]; static boolean blinit = FALSE, update_all = FALSE; static boolean valset[MAXBLSTATS]; -unsigned long blcolormasks[CLR_MAX]; static long bl_hilite_moves = 0L; /* we don't put this next declaration in #ifdef STATUS_HILITES. @@ -519,7 +527,7 @@ static long bl_hilite_moves = 0L; * the final argument of status_update, with or * without STATUS_HILITES. */ -unsigned long cond_hilites[BL_ATTCLR_MAX]; +static unsigned long cond_hilites[BL_ATTCLR_MAX]; void bot_via_windowport() @@ -528,13 +536,13 @@ bot_via_windowport() char buf[BUFSZ]; const char *titl; register char *nb; - int i, idx_p, cap; + int i, cap; long money; if (!blinit) panic("bot before init."); - idx_p = idx; + /* toggle from previous iteration */ idx = 1 - idx; /* 0 -> 1, 1 -> 0 */ /* clear the "value set" indicators */ @@ -699,12 +707,12 @@ bot_via_windowport() blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY; if (u.usteed) blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE; - evaluate_and_notify_windowport(valset, idx, idx_p); + evaluate_and_notify_windowport(valset, idx); } STATIC_OVL boolean -evaluate_and_notify_windowport_field(fld, valsetlist, idx, idx_p) -int fld, idx, idx_p; +eval_notify_windowport_field(fld, valsetlist, idx) +int fld, idx; boolean *valsetlist; { static int oldrndencode = 0; @@ -720,7 +728,7 @@ boolean *valsetlist; */ anytype = blstats[idx][fld].anytype; curr = &blstats[idx][fld]; - prev = &blstats[idx_p][fld]; + prev = &blstats[1 - idx][fld]; color = NO_COLOR; chg = update_all ? 0 : compare_blstats(prev, curr); @@ -753,7 +761,7 @@ boolean *valsetlist; reset = FALSE; #ifdef STATUS_HILITES - if (!update_all && !chg) { + if (!update_all && !chg && curr->time) { reset = hilite_reset_needed(prev, bl_hilite_moves); if (reset) curr->time = prev->time = 0L; @@ -769,9 +777,11 @@ boolean *valsetlist; if (anytype != ANY_MASK32) { #ifdef STATUS_HILITES - if ((chg || *curr->val)) { - get_hilite_color(idx, fld, (genericptr_t) &curr->a, - chg, pc, &color); + if (chg || *curr->val) { + curr->hilite_rule = get_hilite(idx, fld, + (genericptr_t) &curr->a, + chg, pc, &color); + prev->hilite_rule = curr->hilite_rule; if (chg == 2) { color = NO_COLOR; chg = 0; @@ -779,11 +789,11 @@ boolean *valsetlist; } #endif /* STATUS_HILITES */ status_update(fld, (genericptr_t) curr->val, - chg, pc, color, &cond_hilites[0]); + chg, pc, color, (unsigned long *) 0); } else { /* Color for conditions is done through cond_hilites[] */ - status_update(fld, (genericptr_t) &curr->a.a_ulong, chg, pc, - color, &cond_hilites[0]); + status_update(fld, (genericptr_t) &curr->a.a_ulong, + chg, pc, color, cond_hilites); } curr->chg = prev->chg = TRUE; updated = TRUE; @@ -792,8 +802,8 @@ boolean *valsetlist; } static void -evaluate_and_notify_windowport(valsetlist, idx, idx_p) -int idx, idx_p; +evaluate_and_notify_windowport(valsetlist, idx) +int idx; boolean *valsetlist; { int i, updated = 0, notpresent = 0; @@ -810,7 +820,7 @@ boolean *valsetlist; notpresent++; continue; } - if (evaluate_and_notify_windowport_field(i, valsetlist, idx, idx_p)) + if (eval_notify_windowport_field(i, valsetlist, idx)) updated++; } /* @@ -838,13 +848,13 @@ boolean *valsetlist; */ if (context.botlx && (windowprocs.wincap2 & WC2_RESET_STATUS) != 0L) status_update(BL_RESET, (genericptr_t) 0, 0, 0, - NO_COLOR, &cond_hilites[0]); + NO_COLOR, (unsigned long *) 0); else if ((updated || context.botlx) && (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, - NO_COLOR, &cond_hilites[0]); + NO_COLOR, (unsigned long *) 0); - context.botl = context.botlx = 0; + context.botl = context.botlx = FALSE; update_all = FALSE; } @@ -852,11 +862,12 @@ void status_eval_next_unhilite() { int i; - struct istat_s *curr = NULL; + struct istat_s *curr; long next_unhilite, this_unhilite; - bl_hilite_moves = moves; - /* figure out when the next unhilight needs to be performed */ + bl_hilite_moves = moves; /* simpllfied; used to try to encode fractional + * amounts for multiple moves within same turn */ + /* figure out whether an unhilight needs to be performed now */ next_unhilite = 0L; for (i = 0; i < MAXBLSTATS; ++i) { curr = &blstats[0][i]; /* blstats[0][*].time == blstats[1][*].time */ @@ -865,10 +876,17 @@ status_eval_next_unhilite() struct istat_s *prev = &blstats[1][i]; #ifdef STATUS_HILITES - curr->time = prev->time = (bl_hilite_moves + iflags.hilite_delta); + if (Is_Temp_Hilite(curr->hilite_rule)) + curr->time = prev->time = (bl_hilite_moves + + iflags.hilite_delta); + else + curr->time = prev->time = 0L; #endif curr->chg = prev->chg = FALSE; + context.botl = TRUE; } + if (context.botl) + continue; /* just process other blstats[][].time and .chg */ this_unhilite = curr->time; if (this_unhilite > 0L @@ -876,21 +894,22 @@ status_eval_next_unhilite() #ifdef STATUS_HILITES && hilite_reset_needed(curr, this_unhilite + 1L) #endif - ) + ) { next_unhilite = this_unhilite; + if (next_unhilite < bl_hilite_moves) + context.botl = TRUE; + } } - if (next_unhilite > 0L && next_unhilite < bl_hilite_moves) - context.botl = TRUE; } void status_initialize(reassessment) -boolean - reassessment; /* TRUE = just reassess fields w/o other initialization*/ +boolean reassessment; /* TRUE: just recheck fields w/o other initialization */ { + enum statusfields fld; + boolean fldenabl; int i; - const char *fieldfmt = (const char *) 0; - const char *fieldname = (const char *) 0; + const char *fieldfmt, *fieldname; if (!reassessment) { if (blinit) @@ -898,22 +917,22 @@ boolean init_blstats(); (*windowprocs.win_status_init)(); blinit = TRUE; + } else if (!blinit) { + panic("status 'reassess' before init"); } for (i = 0; i < MAXBLSTATS; ++i) { - enum statusfields fld = initblstats[i].fld; - boolean fldenabled = (fld == BL_SCORE) ? flags.showscore - : (fld == BL_XP) ? (boolean) !Upolyd - : (fld == BL_HD) ? (boolean) Upolyd - : (fld == BL_TIME) ? flags.time - : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd) - : TRUE; + fld = initblstats[i].fld; + fldenabl = (fld == BL_SCORE) ? flags.showscore + : (fld == BL_TIME) ? flags.time + : (fld == BL_EXP) ? (boolean) (flags.showexp && !Upolyd) + : (fld == BL_XP) ? (boolean) !Upolyd + : (fld == BL_HD) ? (boolean) Upolyd + : TRUE; fieldname = initblstats[i].fldname; - if (fld == BL_TITLE && iflags.wc2_hitpointbar) - fieldfmt = "%-30s"; - else - fieldfmt = initblstats[i].fldfmt; - status_enablefield(fld, fieldname, fieldfmt, fldenabled); + fieldfmt = (fld == BL_TITLE && iflags.wc2_hitpointbar) ? "%-30.30s" + : initblstats[i].fldfmt; + status_enablefield(fld, fieldname, fieldfmt, fldenabl); } update_all = TRUE; } @@ -934,14 +953,17 @@ status_finish() if (blstats[1][i].val) free((genericptr_t) blstats[1][i].val), blstats[1][i].val = 0; #ifdef STATUS_HILITES + /* pointer to an entry in thresholds list; Null it out since + that list is about to go away */ + blstats[0][i].hilite_rule = blstats[1][i].hilite_rule = 0; if (blstats[0][i].thresholds) { - struct hilite_s *temp, *next = 0; + struct hilite_s *temp, *next; for (temp = blstats[0][i].thresholds; temp; temp = next) { next = temp->next; free((genericptr_t) temp); - blstats[0][i].thresholds = blstats[1][i].thresholds = 0; } + blstats[0][i].thresholds = blstats[1][i].thresholds = 0; } #endif /* STATUS_HILITES */ } @@ -957,9 +979,7 @@ init_blstats() impossible("init_blstats called more than once."); return; } - - initalready = TRUE; - for (i = BEFORE; i <= NOW; ++i) { + for (i = 0; i <= 1; ++i) { for (j = 0; j < MAXBLSTATS; ++j) { #ifdef STATUS_HILITES struct hilite_s *keep_hilite_chain = blstats[i][j].thresholds; @@ -977,6 +997,7 @@ init_blstats() #endif } } + initalready = TRUE; } /* @@ -1327,26 +1348,19 @@ hilite_reset_needed(bl_p, augmented_time) struct istat_s *bl_p; long augmented_time; { - struct hilite_s *tl = bl_p->thresholds; - /* * This 'multi' handling may need some tuning... */ if (multi) return FALSE; + if (!Is_Temp_Hilite(bl_p->hilite_rule)) + return FALSE; + if (bl_p->time == 0 || bl_p->time >= augmented_time) return FALSE; - while (tl) { - /* only this style times out (includes general 'changed' - as well as specific 'up' and 'down') */ - if (tl->behavior == BL_TH_UPDOWN) - return TRUE; - tl = tl->next; - } - - return FALSE; + return TRUE; } /* called by options handling when 'statushilites' boolean is toggled */ @@ -1377,33 +1391,13 @@ const char *hl_text; return FALSE; } -STATIC_OVL void -merge_bestcolor(bestcolor, newcolor) -int *bestcolor; -int newcolor; -{ - int natr = HL_UNDEF, nclr = NO_COLOR; - - split_clridx(newcolor, &nclr, &natr); - - if (nclr != NO_COLOR) - *bestcolor = (*bestcolor & 0xff00) | nclr; - - if (natr != HL_UNDEF) { - if (natr == HL_NONE) - *bestcolor = *bestcolor & 0x00ff; /* reset all attributes */ - else - *bestcolor |= (natr << 8); /* merge attributes */ - } -} - /* - * get_hilite_color + * get_hilite * - * Figures out, based on the value and the direction it is moving, - * the color that the field should be displayed in. + * Returns, based on the value and the direction it is moving, + * the highlight rule that applies to the specified field. * - * Provide get_hilite_color() with the following to work with: + * Provide get_hilite() with the following to work with: * actual value vp * useful for BL_TH_VAL_ABSOLUTE * indicator of down, up, or the same (-1, 1, 0) chg @@ -1412,28 +1406,22 @@ int newcolor; * useful for BL_TH_VAL_PERCENTAGE * * Get back: - * color based on user thresholds set in config file. - * The rightmost 8 bits contain a color index. - * The 8 bits to the left of that contain - * the attribute bits. - * color = 0x00FF - * attrib= 0xFF00 + * pointer to rule that applies; Null if no rule does. */ -STATIC_OVL void -get_hilite_color(idx, fldidx, vp, chg, pc, colorptr) +STATIC_OVL struct hilite_s * +get_hilite(idx, fldidx, vp, chg, pc, colorptr) int idx, fldidx, chg, pc; genericptr_t vp; int *colorptr; { - int bestcolor = NO_COLOR; - struct hilite_s *hl; + struct hilite_s *hl, *rule = 0; anything *value = (anything *) vp; char *txtstr; - if (!colorptr || fldidx < 0 || fldidx >= MAXBLSTATS) - return; + if (fldidx < 0 || fldidx >= MAXBLSTATS) + return (struct hilite_s *) 0; - if (blstats[idx][fldidx].thresholds) { + if (has_hilite(fldidx)) { int dt; /* there are hilites set here */ int max_pc = -1, min_pc = 101; @@ -1447,7 +1435,7 @@ int *colorptr; perc_or_abs = FALSE; /* min_/max_ are used to track best fit */ - for (hl = blstats[idx][fldidx].thresholds; hl; hl = hl->next) { + for (hl = blstats[0][fldidx].thresholds; hl; hl = hl->next) { dt = initblstats[fldidx].anytype; /* only needed for 'absolute' */ /* if we've already matched a temporary highlight, it takes precedence over all persistent ones; we still process @@ -1462,7 +1450,7 @@ int *colorptr; switch (hl->behavior) { case BL_TH_VAL_PERCENTAGE: /* percent values are always ANY_INT */ if (hl->rel == EQ_VALUE && pc == hl->value.a_int) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_pc = max_pc = hl->value.a_int; exactmatch = perc_or_abs = TRUE; } else if (exactmatch) { @@ -1470,25 +1458,25 @@ int *colorptr; } else if (hl->rel == LT_VALUE && (pc < hl->value.a_int) && (hl->value.a_int <= min_pc)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_pc = hl->value.a_int; 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); + rule = hl; min_pc = hl->value.a_int; 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); + rule = hl; max_pc = hl->value.a_int; 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); + rule = hl; max_pc = hl->value.a_int; perc_or_abs = TRUE; } @@ -1497,13 +1485,13 @@ int *colorptr; /* 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); + rule = hl; updown = TRUE; } else if (chg > 0 && hl->rel == GT_VALUE) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; updown = TRUE; } else if (chg != 0 && hl->rel == EQ_VALUE && !updown) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; changed = TRUE; } break; @@ -1516,7 +1504,7 @@ int *colorptr; if (dt == ANY_INT) { if (hl->rel == EQ_VALUE && hl->value.a_int == value->a_int) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_ival = max_ival = hl->value.a_int; exactmatch = perc_or_abs = TRUE; } else if (exactmatch) { @@ -1524,32 +1512,32 @@ int *colorptr; } else if (hl->rel == LT_VALUE && (value->a_int < hl->value.a_int) && (hl->value.a_int <= min_ival)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_ival = hl->value.a_int; perc_or_abs = TRUE; } else if (hl->rel == LE_VALUE && (value->a_int <= hl->value.a_int) && (hl->value.a_int <= min_ival)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_ival = hl->value.a_int; perc_or_abs = TRUE; } else if (hl->rel == GT_VALUE && (value->a_int > hl->value.a_int) && (hl->value.a_int >= max_ival)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; max_ival = hl->value.a_int; perc_or_abs = TRUE; } else if (hl->rel == GE_VALUE && (value->a_int >= hl->value.a_int) && (hl->value.a_int >= max_ival)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; max_ival = hl->value.a_int; perc_or_abs = TRUE; } } else { /* ANY_LONG */ if (hl->rel == EQ_VALUE && hl->value.a_long == value->a_long) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_lval = max_lval = hl->value.a_long; exactmatch = perc_or_abs = TRUE; } else if (exactmatch) { @@ -1557,25 +1545,25 @@ int *colorptr; } else if (hl->rel == LT_VALUE && (value->a_long < hl->value.a_long) && (hl->value.a_long <= min_lval)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_lval = hl->value.a_long; perc_or_abs = TRUE; } else if (hl->rel == LE_VALUE && (value->a_long <= hl->value.a_long) && (hl->value.a_long <= min_lval)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; min_lval = hl->value.a_long; perc_or_abs = TRUE; } else if (hl->rel == GT_VALUE && (value->a_long > hl->value.a_long) && (hl->value.a_long >= max_lval)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; max_lval = hl->value.a_long; perc_or_abs = TRUE; } else if (hl->rel == GE_VALUE && (value->a_long >= hl->value.a_long) && (hl->value.a_long >= max_lval)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; max_lval = hl->value.a_long; perc_or_abs = TRUE; } @@ -1588,18 +1576,18 @@ int *colorptr; txtstr += (strlen(plname) + sizeof " the " - sizeof ""); if (hl->rel == TXT_VALUE && hl->textmatch[0]) { if (fuzzymatch(hl->textmatch, txtstr, "\" -_", TRUE)) { - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; 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); + rule = hl; } } break; case BL_TH_ALWAYS_HILITE: - merge_bestcolor(&bestcolor, hl->coloridx); + rule = hl; break; case BL_TH_NONE: break; @@ -1608,8 +1596,8 @@ int *colorptr; } } } - *colorptr = bestcolor; - return; + *colorptr = rule ? rule->coloridx : NO_COLOR; + return rule; } STATIC_OVL void @@ -2119,9 +2107,9 @@ boolean from_configfile; hilite.anytype = dt; - if (hilite.behavior == BL_TH_TEXTMATCH && txt - && strlen(txt) < QBUFSZ - 1) { - Strcpy(hilite.textmatch, txt); + if (hilite.behavior == BL_TH_TEXTMATCH && txt) { + (void) strncpy(hilite.textmatch, txt, sizeof hilite.textmatch); + hilite.textmatch[sizeof hilite.textmatch - 1] = '\0'; (void) trimspaces(hilite.textmatch); } @@ -2307,6 +2295,15 @@ int sidx; /*3.6.1: OPTION=hilite_status: condition/stone+slime+foodPois/red&inverse */ + /* + * TODO? + * It would be simpler to treat each condition (also hunger state + * and encumbrance level) as if it were a separate field. That + * way they could have either or both 'changed' temporary rule and + * 'always' persistent rule and wouldn't need convoluted access to + * the intended color and attributes. + */ + sidx++; while(s[sidx]) { int sf = 0; /* subfield count */ @@ -2410,17 +2407,15 @@ clear_status_hilites() int i; for (i = 0; i < MAXBLSTATS; ++i) { - if (blstats[0][i].thresholds) { - struct hilite_s *temp = blstats[0][i].thresholds, - *next = (struct hilite_s *)0; - while (temp) { - next = temp->next; - free(temp); - blstats[0][i].thresholds = (struct hilite_s *)0; - blstats[1][i].thresholds = blstats[0][i].thresholds; - temp = next; - } + struct hilite_s *temp, *next; + + for (temp = blstats[0][i].thresholds; temp; temp = next) { + next = temp->next; + free(temp); } + blstats[0][i].thresholds = blstats[1][i].thresholds = 0; + /* pointer into thresholds list, now stale */ + blstats[0][i].hilite_rule = blstats[1][i].hilite_rule = 0; } } @@ -3200,7 +3195,7 @@ choose_value: Strcpy(hilite.textmatch, hutxt[rv]); } else if (fld == BL_TITLE) { const char *rolelist[3 * 9 + 1]; - char mbuf[QBUFSZ], fbuf[QBUFSZ], obuf[QBUFSZ]; + char mbuf[MAXVALWIDTH], fbuf[MAXVALWIDTH], obuf[MAXVALWIDTH]; int i, j, rv; for (i = j = 0; i < 9; i++) { @@ -3247,7 +3242,7 @@ choose_value: goto choose_behavior; hilite.rel = TXT_VALUE; - if (strlen(inbuf) < QBUFSZ - 1) + if (strlen(inbuf) < sizeof hilite.textmatch) Strcpy(hilite.textmatch, inbuf); else return FALSE; @@ -3357,25 +3352,25 @@ int id; return TRUE; } else { int fld = hlstr->fld; - struct hilite_s *hl = blstats[0][fld].thresholds; - struct hilite_s *hlprev = (struct hilite_s *) 0; + struct hilite_s *hl, *hlprev = (struct hilite_s *) 0; - if (hl) { - while (hl) { - if (hlstr->hl == hl) { - if (hlprev) { - hlprev->next = hl->next; - } else { - blstats[0][fld].thresholds = hl->next; - blstats[1][fld].thresholds = - blstats[0][fld].thresholds; - } - free((genericptr_t) hl); - return TRUE; + for (hl = blstats[0][fld].thresholds; hl; hl = hl->next) { + if (hlstr->hl == hl) { + if (hlprev) { + hlprev->next = hl->next; + } else { + blstats[0][fld].thresholds = hl->next; + blstats[1][fld].thresholds = blstats[0][fld].thresholds; } - hlprev = hl; - hl = hl->next; + if (blstats[0][fld].hilite_rule == hl) { + blstats[0][fld].hilite_rule + = blstats[1][fld].hilite_rule = (struct hilite_s *) 0; + blstats[0][fld].time = blstats[1][fld].time = 0L; + } + free((genericptr_t) hl); + return TRUE; } + hlprev = hl; } } return FALSE; From c8fdb040cbd7a705ac8d03fe4d78da783bad6863 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Mar 2019 07:04:23 -0700 Subject: [PATCH 10/17] curses status highlighting window.doc states that the colormasks argument to status_update() is only relevant for BL_CONDITION, but curses was relying on it to be passed for BL_FLUSH as well. Yesterday's changes stopped the latter and broke highlighting of status conditions. Other interfaces appear to honor the description in window.doc. --- doc/fixes36.2 | 4 +++- win/curses/cursstat.c | 56 +++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 864175d99..555a0f27e 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.291 $ $NHDT-Date: 1554017610 2019/03/31 07:33:30 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.292 $ $NHDT-Date: 1554041056 2019/03/31 14:04:16 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -527,6 +527,8 @@ curses: popup window to show ^P output was removed from screen but never deleted; further ^P's repeated that cycle; likewise for help which displays an external text file curses: preserve ^P message history across save/restore +curses: highlighting of status conditions was broken by the fix for timing out + of temporary highlights setting off unnecessary status updates vms: add compile of isaac64.c to Makefile.src and vmsbuild.com vms+curses: add compile support but it is known to fail to build diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index ca88f6c5e..ad33482b9 100644 --- a/win/curses/cursstat.c +++ b/win/curses/cursstat.c @@ -25,6 +25,7 @@ extern boolean status_activefields[MAXBLSTATS]; static char *status_vals_long[MAXBLSTATS]; #ifdef STATUS_HILITES +static unsigned long *curses_colormasks; static long curses_condition_bits; static int curses_status_colors[MAXBLSTATS]; static int hpbar_percent, hpbar_color; @@ -36,12 +37,11 @@ static int FDECL(condcolor, (long, unsigned long *)); static int FDECL(condattr, (long, unsigned long *)); static int FDECL(nhattr2curses, (int)); #endif /* STATUS_HILITES */ -static void FDECL(draw_status, (unsigned long *)); -static void FDECL(draw_vertical, (BOOLEAN_P, unsigned long *)); -static void FDECL(draw_horizontal, (BOOLEAN_P, unsigned long *)); +static void NDECL(draw_status); +static void FDECL(draw_vertical, (BOOLEAN_P)); +static void FDECL(draw_horizontal, (BOOLEAN_P)); static void curs_HPbar(char *, int); -static void curs_stat_conds(int, int *, int *, unsigned long *, - char *, boolean *); +static void curs_stat_conds(int, int *, int *, char *, boolean *); static void curs_vert_status_vals(int); /* width of a single line in vertical status orientation (one field per line; @@ -162,6 +162,7 @@ unsigned long *colormasks; return; if (fldidx == BL_CONDITION) { curses_condition_bits = *condptr; + curses_colormasks = colormasks; } else { #ifndef TEXTCOLOR color_and_attr = (color_and_attr & ~0x00FF) | NO_COLOR; @@ -204,19 +205,13 @@ unsigned long *colormasks; } } } else { /* BL_FLUSH */ - if (!changed_fields && !context.botlx) { - ; /* TODO: this isn't impossible but we want to track - * down the circumstances where it happens in order to - * minimize occurrences */ - } - draw_status(colormasks); + draw_status(); changed_fields = 0; } } -void -draw_status(colormasks) -unsigned long *colormasks; +static void +draw_status() { WINDOW *win = curses_get_nhwin(STATUS_WIN); int orient = curses_get_window_orientation(STATUS_WIN); @@ -242,9 +237,9 @@ unsigned long *colormasks; werase(win); if (horiz) - draw_horizontal(border, colormasks); + draw_horizontal(border); else - draw_vertical(border, colormasks); + draw_vertical(border); if (border) box(win, 0, 0); @@ -252,10 +247,9 @@ unsigned long *colormasks; } /* horizontal layout on 2 or 3 lines */ -void -draw_horizontal(border, colormasks) +static void +draw_horizontal(border) boolean border; -unsigned long *colormasks; { #define blPAD BL_FLUSH /* almost all fields already come with a leading space; @@ -343,7 +337,7 @@ unsigned long *colormasks; /* collect active conditions in cbuf[], space separated, suitable for direct output if no highlighting is requested ('asis') but primarily used to measure the length */ - curs_stat_conds(0, &x, &y, colormasks, cbuf, &asis); + curs_stat_conds(0, &x, &y, cbuf, &asis); clen = (int) strlen(cbuf); cap_and_hunger = 0; @@ -640,8 +634,7 @@ unsigned long *colormasks; if (asis) waddstr(win, cbuf); else /* cond by cond if any cond specifies highlighting */ - curs_stat_conds(0, &x, &y, colormasks, - (char *) 0, (boolean *) 0); + curs_stat_conds(0, &x, &y, (char *) 0, (boolean *) 0); } /* curses_condition_bits */ } /* hitpointbar vs regular field vs conditions */ } /* i (fld) */ @@ -651,10 +644,9 @@ unsigned long *colormasks; } /* vertical layout, to left or right of map */ -void -draw_vertical(border, colormasks) +static void +draw_vertical(border) boolean border; -unsigned long *colormasks; { /* for blank lines, the digit prefix is the order in which they get removed if we need to shrink to fit within height limit (very rare) */ @@ -893,8 +885,7 @@ unsigned long *colormasks; if (cond_count) { /* output active conditions, three per line; cursor is already positioned where they should start */ - curs_stat_conds(1, &x, &y, colormasks, - (char *) 0, (boolean *) 0); + curs_stat_conds(1, &x, &y, (char *) 0, (boolean *) 0); } } /* hitpointbar vs regular field vs conditions */ } /* fld loop */ @@ -971,7 +962,6 @@ extern const struct condmap valid_conditions[]; /* botl.c */ static void curs_stat_conds(int vert_cond, /* 0 => horizontal, 1 => vertical */ int *x, int *y, /* real for vertical, ignored otherwise */ - unsigned long *colormasks, /* input */ char *condbuf, /* optional output; collect string of conds */ boolean *nohilite) /* optional output; indicates whether -*/ { /*+ condbuf[] could be used as-is */ @@ -994,8 +984,8 @@ curs_stat_conds(int vert_cond, /* 0 => horizontal, 1 => vertical */ Strcpy(condnam, valid_conditions[i].id); Strcat(strcat(condbuf, " "), upstart(condnam)); if (nohilite && *nohilite - && (condcolor(bitmsk, colormasks) != NO_COLOR - || condattr(bitmsk, colormasks) != 0)) + && (condcolor(bitmsk, curses_colormasks) != NO_COLOR + || condattr(bitmsk, curses_colormasks) != 0)) *nohilite = FALSE; } } @@ -1034,12 +1024,14 @@ curs_stat_conds(int vert_cond, /* 0 => horizontal, 1 => vertical */ if (!do_vert || (vert_cond % 3) != 1) waddch(win, ' '); if (iflags.hilite_delta) { - if ((attrmask = condattr(bitmsk, colormasks)) != 0) { + if ((attrmask = condattr(bitmsk, curses_colormasks)) + != 0) { attrmask = nhattr2curses(attrmask); wattron(win, attrmask); } #ifdef TEXTCOLOR - if ((color = condcolor(bitmsk, colormasks)) != NO_COLOR) + if ((color = condcolor(bitmsk, curses_colormasks)) + != NO_COLOR) curses_toggle_color_attr(win, color, NONE, ON); #endif } From 0a847f46f9755a29ba24fcca1c4f28a9b0a34543 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Mar 2019 08:23:36 -0700 Subject: [PATCH 11/17] streamlined status update for 'time' When the 'time' option is on and context.botl isn't already set, call a simpler status update routine that ignores all other fields. When that flag is already set, full status update takes care of time along with the other fields. Expected to reduce bottom lines processing time but not screen I/O. Only lightly tested. --- doc/fixes36.2 | 3 ++- include/extern.h | 3 ++- include/flag.h | 7 ++++--- src/allmain.c | 10 ++++++++-- src/botl.c | 46 ++++++++++++++++++++++++++++++++++++++++------ src/display.c | 4 +++- src/end.c | 4 ++-- 7 files changed, 61 insertions(+), 16 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 555a0f27e..5ee1657c4 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.292 $ $NHDT-Date: 1554041056 2019/03/31 14:04:16 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.293 $ $NHDT-Date: 1554045807 2019/03/31 15:23:27 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -667,6 +667,7 @@ in wizard mode, ^T can be preceded by 'm' prefix in order to test teleporting include isaac64 for pseudo random number generation core prng and display prng use different contexts when healing magic other than unicorn horn cures blindness, cure deafness too +do less status updating when the 'time' option is on curses: status display substantially revamped for both horizontal (via 'align_status:bottom' or 'top') and vertical (via 'align_status:left' or 'right'); 3-line horizontal layout (via 'statuslines:3') added diff --git a/include/extern.h b/include/extern.h index 9fa6dfc0b..92f320c9c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1553895318 2019/03/29 21:35:18 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.696 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1554045807 2019/03/31 15:23:27 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.697 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -159,6 +159,7 @@ E long NDECL(botl_score); E int FDECL(describe_level, (char *)); E const char *FDECL(rank_of, (int, SHORT_P, BOOLEAN_P)); E void NDECL(bot); +E void NDECL(timebot); E void FDECL(status_initialize, (BOOLEAN_P)); E void NDECL(status_finish); E void FDECL(status_notify_windowport, (BOOLEAN_P)); diff --git a/include/flag.h b/include/flag.h index cf545edbb..fa11a3dfa 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 flag.h $NHDT-Date: 1553204011 2019/03/21 21:33:31 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.148 $ */ +/* NetHack 3.6 flag.h $NHDT-Date: 1554045808 2019/03/31 15:23:28 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.149 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -364,9 +364,10 @@ struct instance_flags { #ifdef TTY_TILES_ESCCODES boolean vt_tiledata; /* output console codes for tile support in TTY */ #endif + boolean clicklook; /* allow right-clicking for look */ + boolean cmdassist; /* provide detailed assistance for some comnds */ + boolean time_botl; /* context.botl for 'time' (moves) only */ boolean wizweight; /* display weight of everything in wizard mode */ - boolean cmdassist; /* provide detailed assistance for some commands */ - boolean clicklook; /* allow right-clicking for look */ /* * Window capability support. */ diff --git a/src/allmain.c b/src/allmain.c index e2aab1886..1237d6b41 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 allmain.c $NHDT-Date: 1553363414 2019/03/23 17:50:14 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.95 $ */ +/* NetHack 3.6 allmain.c $NHDT-Date: 1554045808 2019/03/31 15:23:28 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.96 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -181,7 +181,7 @@ boolean resuming; if (u.ublesscnt) u.ublesscnt--; if (flags.time && !context.run) - context.botl = TRUE; + iflags.time_botl = TRUE; /* One possible result of prayer is healing. Whether or * not you get healed depends on your current hit points. @@ -207,8 +207,10 @@ boolean resuming; : moves % 10)) { if (Upolyd && u.mh > 1) { u.mh--; + context.botl = TRUE; } else if (!Upolyd && u.uhp > 1) { u.uhp--; + context.botl = TRUE; } else { You("pass out from exertion!"); exercise(A_CON, FALSE); @@ -234,6 +236,7 @@ boolean resuming; if (!u.uinvulnerable) { if (Teleportation && !rn2(85)) { xchar old_ux = u.ux, old_uy = u.uy; + tele(); if (u.ux != old_ux || u.uy != old_uy) { if (!next_to_u()) { @@ -356,6 +359,9 @@ boolean resuming; if (context.botl || context.botlx) { bot(); curs_on_u(); + } else if (iflags.time_botl) { + timebot(); + curs_on_u(); } context.move = 1; diff --git a/src/botl.c b/src/botl.c index 545e5cd6a..860835189 100644 --- a/src/botl.c +++ b/src/botl.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 botl.c $NHDT-Date: 1554017610 2019/03/31 07:33:30 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.139 $ */ +/* NetHack 3.6 botl.c $NHDT-Date: 1554045809 2019/03/31 15:23:29 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.140 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -17,6 +17,7 @@ STATIC_OVL NEARDATA int mrank_sz = 0; /* loaded by max_rank_sz (from u_init) */ STATIC_DCL const char *NDECL(rank); #ifdef STATUS_HILITES STATIC_DCL void NDECL(bot_via_windowport); +STATIC_DCL void NDECL(stat_update_time); #endif static char * @@ -244,7 +245,21 @@ bot() putmixed(WIN_STATUS, 0, do_statusline2()); #endif } - context.botl = context.botlx = 0; + context.botl = context.botlx = iflags.time_botl = FALSE; +} + +void +timebot() +{ + if (flags.time) { +#ifdef STATUS_HILITES + stat_update_time(); +#else + /* old status display updates everything */ + bot(); +#endif + } + iflags.time_botl = FALSE; } /* convert experience level (1..30) to rank index (0..8) */ @@ -528,22 +543,23 @@ static long bl_hilite_moves = 0L; * without STATUS_HILITES. */ static unsigned long cond_hilites[BL_ATTCLR_MAX]; +static int now_or_before_idx = 0; /* 0..1 for array[2][] first index */ void bot_via_windowport() { - static int idx = 0; char buf[BUFSZ]; const char *titl; register char *nb; - int i, cap; + int i, idx, cap; long money; if (!blinit) panic("bot before init."); /* toggle from previous iteration */ - idx = 1 - idx; /* 0 -> 1, 1 -> 0 */ + idx = 1 - now_or_before_idx; /* 0 -> 1, 1 -> 0 */ + now_or_before_idx = idx; /* clear the "value set" indicators */ (void) memset((genericptr_t) valset, 0, MAXBLSTATS * sizeof (boolean)); @@ -710,6 +726,24 @@ bot_via_windowport() evaluate_and_notify_windowport(valset, idx); } +/* update just the status lines' 'time' field */ +STATIC_OVL void +stat_update_time() +{ + int idx = now_or_before_idx; /* no 0/1 toggle */ + int fld = BL_TIME; + + /* Time (moves) */ + blstats[idx][fld].a.a_long = moves; + valset[fld] = FALSE; + + eval_notify_windowport_field(fld, valset, idx); + if ((windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) + status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, + NO_COLOR, (unsigned long *) 0); + return; +} + STATIC_OVL boolean eval_notify_windowport_field(fld, valsetlist, idx) int fld, idx; @@ -854,7 +888,7 @@ boolean *valsetlist; status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, NO_COLOR, (unsigned long *) 0); - context.botl = context.botlx = FALSE; + context.botl = context.botlx = iflags.time_botl = FALSE; update_all = FALSE; } diff --git a/src/display.c b/src/display.c index 7666aa928..d8abb384e 100644 --- a/src/display.c +++ b/src/display.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 display.c $NHDT-Date: 1553895319 2019/03/29 21:35:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.98 $ */ +/* NetHack 3.6 display.c $NHDT-Date: 1554045810 2019/03/31 15:23:30 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.99 $ */ /* Copyright (c) Dean Luick, with acknowledgements to Kevin Darcy */ /* and Dave Cohrs, 1990. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1625,6 +1625,8 @@ int cursor_on_u; flushing = 0; if (context.botl || context.botlx) bot(); + else if (iflags.time_botl) + timebot(); } /* ======================================================================== */ diff --git a/src/end.c b/src/end.c index e52145755..4dcf3947c 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1553652951 2019/03/27 02:15:51 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.166 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1554045810 2019/03/31 15:23:30 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.167 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1048,7 +1048,7 @@ int how; #endif ) { /* skip status update if panicking or disconnected */ - context.botl = context.botlx = FALSE; + context.botl = context.botlx = iflags.time_botl = FALSE; } else { /* otherwise force full status update */ context.botlx = TRUE; From cd12422af5eac6889669f81348763d052bae6435 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Mar 2019 15:34:46 -0700 Subject: [PATCH 12/17] curses message suppression The curses interface was using 'moves' as if it meant "moves" rather than "turns". Typing ESC at >> (curses' terser version of --More--) prompt would suppress messages for the rest of the current turn rather than just the rest of the current move. So if the hero got an extra move due to being Fast, there would be no feedback during that move. --- doc/fixes36.2 | 5 ++++- win/curses/cursdial.c | 10 ++++++++++ win/curses/cursmain.c | 5 +++++ win/curses/cursmesg.c | 26 +++++++++++++++----------- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 5ee1657c4..c1490f5fc 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.293 $ $NHDT-Date: 1554045807 2019/03/31 15:23:27 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.294 $ $NHDT-Date: 1554071680 2019/03/31 22:34:40 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -529,6 +529,9 @@ curses: popup window to show ^P output was removed from screen but never curses: preserve ^P message history across save/restore curses: highlighting of status conditions was broken by the fix for timing out of temporary highlights setting off unnecessary status updates +curses: if player pressed ESC at More>> prompt to suppress remaining messages + for the current move and then hero got another move on the same turn, + messages and most prompts would stay suppressed during that extra move vms: add compile of isaac64.c to Makefile.src and vmsbuild.com vms+curses: add compile support but it is known to fail to build diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index af337f858..deee077fd 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -17,6 +17,8 @@ #define strncasecmp strncmpi #endif +extern long curs_mesg_suppress_turn; /* from cursmesg.c */ + /* * Note: * @@ -120,6 +122,10 @@ curses_line_input_dialog(const char *prompt, char *answer, int buffer) int height = prompt_height; char input[BUFSZ]; + /* if messages were being suppressed for the remainder of the turn, + re-activate them now that input is being requested */ + curs_mesg_suppress_turn = -1; + if (buffer >= (int) sizeof input) buffer = (int) sizeof input - 1; maxwidth = term_cols - 2; @@ -201,6 +207,10 @@ curses_character_input_dialog(const char *prompt, const char *choices, boolean any_choice = FALSE; boolean accept_count = FALSE; + /* if messages were being suppressed for the remainder of the turn, + re-activate them now that input is being requested */ + curs_mesg_suppress_turn = -1; + if (invent || (moves > 1)) { curses_get_window_size(MAP_WIN, &map_height, &map_width); } else { diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 8dba85a93..c3cceb1f7 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -9,6 +9,8 @@ #include "color.h" #include "wincurs.h" +extern long curs_mesg_suppress_turn; /* from cursmesg.c */ + /* Public functions for curses NetHack interface */ /* Interface definition, for windows.c */ @@ -687,6 +689,9 @@ curses_nhgetch() { int ch; + /* if messages are being suppressed, reenable them */ + curs_mesg_suppress_turn = -1; + curses_prehousekeeping(); ch = curses_read_char(); curses_posthousekeeping(); diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index 57cc5636b..c3d0aa565 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -9,6 +9,10 @@ #include "cursmesg.h" #include +/* player can type ESC at More>> prompt to avoid seeing more messages + for the current move; but hero might get more than one move per turn, + so the input routines need to be able to cancel this */ +long curs_mesg_suppress_turn = -1; /* also used in cursdial.c && cursmain.c */ /* Message window routines for curses interface */ @@ -46,16 +50,13 @@ curses_message_win_puts(const char *message, boolean recursed) boolean border = curses_window_has_border(MESSAGE_WIN); int message_length = strlen(message); int border_space = 0; - static long suppress_turn = -1; -#if 1 +#if 0 /* - * Handled by core's use of putstr(WIN_MESSAGE,ATR_NOHISTORY,message) - * for intermediate counts, but get_count() also uses putmsghistory() - * for the final count, to remember that without showing it. But - * curses is using genl_putmsghistory() which just delivers the text - * via a normal pline(). This hides that at cost of not having it - * in ^P recall and being out of sync with DUMPLOG's message history. + * This was useful when curses used genl_putmsghistory() but is not + * needed now that it has its own curses_putmsghistory() that's + * capable of putting something into the ^P recall history without + * displaying it at the same time. */ if (strncmp("Count:", message, 6) == 0) { curses_count_window(message); @@ -63,8 +64,8 @@ curses_message_win_puts(const char *message, boolean recursed) } #endif - if (suppress_turn == moves) { - return; + if (curs_mesg_suppress_turn == moves) { + return; /* user has typed ESC to avoid seeing remaining messages. */ } curses_get_window_size(MESSAGE_WIN, &height, &width); @@ -105,7 +106,7 @@ curses_message_win_puts(const char *message, boolean recursed) /* Pause until key is hit - Esc suppresses any further messages that turn */ if (curses_more() == '\033') { - suppress_turn = moves; + curs_mesg_suppress_turn = moves; return; } } else { @@ -156,6 +157,9 @@ curses_block(boolean noscroll) /* noscroll - blocking because of msgtype WINDOW *win = curses_get_nhwin(MESSAGE_WIN); const char *resp = " \r\n\033"; /* space, enter, esc */ + /* if messages are being suppressed, reenable them */ + curs_mesg_suppress_turn = -1; + curses_get_window_size(MESSAGE_WIN, &height, &width); curses_toggle_color_attr(win, MORECOLOR, NONE, ON); mvwprintw(win, my, mx, ">>"); From 0cad96042842d70bf050c1b67ea9ec3f5cd401b6 Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Apr 2019 08:58:49 -0700 Subject: [PATCH 13/17] X11 extended command selection The expansion of the extended commands list to include every command has made picking extended commands out of X11's menu become tedious. This uses the existing 'extmenu' option (previously tty-only) to control whether all the commands are present or just the traditional subset not bound to non-meta keystrokes ('adjust', 'chat', 'loot', &c). --- dat/opthelp | 5 +-- doc/Guidebook.mn | 10 ++++-- doc/Guidebook.tex | 9 ++++-- doc/fixes36.2 | 4 ++- win/X11/winmisc.c | 78 ++++++++++++++++++++++++++++++----------------- 5 files changed, 70 insertions(+), 36 deletions(-) diff --git a/dat/opthelp b/dat/opthelp index dd379be79..560acc463 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -1,6 +1,6 @@ Boolean options not under specific compile flags (with default values in []): (You can learn which options exist in your version by checking your current -option setting, which is reached via the 'O' cmd.) +option setting, which is reached via the 'O' command.) acoustics can your character hear anything [TRUE] autodescribe describe the terrain under cursor [FALSE] @@ -17,7 +17,8 @@ cmdassist give help for errors on direction & other commands [TRUE] confirm ask before hitting tame or peaceful monsters [TRUE] dark_room show floor not in sight in different color [TRUE] eight_bit_tty send 8-bit characters straight to terminal [FALSE] -extmenu use a menu for selecting extended commands (#) [FALSE] +extmenu tty: use a menu for selecting extended commands (#)[FALSE] + X11: menu has all commands (T) or traditional subset (F) fixinv try to retain the same letter for the same object [TRUE] force_invmenu commands asking for inventory item show a menu [FALSE] goldX when filtering objects by bless/curse state, [FALSE] diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index 956ba2aa1..5992a75c0 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -1,4 +1,4 @@ -.\" $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.302 $ $NHDT-Date: 1553858473 2019/03/29 11:21:13 $ +.\" $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.303 $ $NHDT-Date: 1554134322 2019/04/01 15:58:42 $ .\" .\" This is an excerpt from the 'roff' man page from the 'groff' package. .\" NetHack's Guidebook.mn currently does *not* adhere to these guidelines. @@ -2972,8 +2972,12 @@ Changes the extended commands interface to pop-up a menu of available commands. It is keystroke compatible with the traditional interface except that it does not require that you hit \fIEnter\fP. -It is implemented only by the tty port -(default off), when the game has been compiled to support tty graphics. +It is implemented for the tty interface (default off). +.lp "" +For the X11 interface, which always uses a menu for choosing an extended +command, it controls whether the menu shows all available commands (on) +or just the subset of commands which have traditionally been considered +extended ones (off). .lp female An obsolete synonym for \(lqgender:female\(rq. Cannot be set with the \(oqO\(cq command. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 151bed238..9230ac309 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -3266,8 +3266,13 @@ Cannot be set with the `{\tt O}' command. \item[\ib{extmenu}] Changes the extended commands interface to pop-up a menu of available commands. It is keystroke compatible with the traditional interface except that it does -not require that you hit Enter. It is implemented only by the tty port -(default off), when the game has been compiled to support tty graphics. +not require that you hit Enter. +It is implemented for the tty interface (default off). +.lp "" +For the X11 interface, which always uses a menu for choosing an extended +command, it controls whether the menu shows all available commands (on) +or just the subset of commands which have traditionally been considered +extended ones (off). %.lp \item[\ib{female}] An obsolete synonym for ``{\tt gender:female}''. Cannot be set with the diff --git a/doc/fixes36.2 b/doc/fixes36.2 index c1490f5fc..fcf4c17e6 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.294 $ $NHDT-Date: 1554071680 2019/03/31 22:34:40 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.295 $ $NHDT-Date: 1554134321 2019/04/01 15:58:41 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -499,6 +499,8 @@ tty: when clipping was used to show a subset of the map on a small display, X11: its use of genl_status_update exposed a negative index use that could lead to a segfault X11: rollback disabling of keystroke input for PICK_NONE menus (for scrolling) +X11: make use of the 'extmenu' option: On to choose among all commands, Off + to choose among the traditional extended command subset curses: catch up with tty to not put dolook/whatis autodescribe feedback into ^P message recall (multi-digit count feedback was already handled) curses: if the interface code ran out of memory, it would crash rather than diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 59883cdaf..13740a99b 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 winmisc.c $NHDT-Date: 1543830350 2018/12/03 09:45:50 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.42 $ */ +/* NetHack 3.6 winmisc.c $NHDT-Date: 1554134316 2019/04/01 15:58:36 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.43 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -41,6 +41,8 @@ static Widget extended_command_popup = 0; static Widget extended_command_form; static Widget *extended_commands = 0; +static const char **command_list; +static short *command_indx; static int extended_cmd_selected; /* index of the selected command; */ static int ps_selected; /* index of selected role */ #define PS_RANDOM (-50) @@ -50,6 +52,7 @@ static const char ps_randchars[] = "*@\n\rrR"; static const char ps_quitchars[] = "\033qQ"; #define EC_NCHARS 32 +static boolean ec_full_list = FALSE; static boolean ec_active = FALSE; static int ec_nchars = 0; static char ec_chars[EC_NCHARS]; @@ -111,13 +114,14 @@ XtPointer i2xtp(i) int i; { - return (XtPointer)(long)i; + return (XtPointer) (ptrdiff_t) i; } + int xtp2i(x) XtPointer x; { - return (long)x; + return (int) (ptrdiff_t) x; } /* Player Selection ------------------------------------------------------- */ @@ -1581,9 +1585,16 @@ X11_player_selection() } } +/* called by core to have the player pick an extended command */ int X11_get_ext_cmd() { + if (iflags.extmenu != ec_full_list) { + /* player has toggled the 'extmenu' option, toss the old widgets */ + if (extended_commands) + release_extended_cmds(); /* will set extended_commands to Null */ + ec_full_list = iflags.extmenu; + } if (!extended_commands) init_extended_commands_popup(); @@ -1597,15 +1608,19 @@ X11_get_ext_cmd() /* The callbacks will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); - return extended_cmd_selected; + if (extended_cmd_selected < 0) + return -1; + return command_indx[extended_cmd_selected]; } void release_extended_cmds() { if (extended_commands) { - XtDestroyWidget(extended_command_popup); + XtDestroyWidget(extended_command_popup), extended_command_popup = 0; free((genericptr_t) extended_commands), extended_commands = 0; + free((genericptr_t) command_list), command_list = (const char **) 0; + free((genericptr_t) command_indx), command_indx = (short *) 0; } } @@ -1816,6 +1831,22 @@ int ec_indx; /* might be greater than extended_cmd_selected */ } } +/* decide whether extcmdlist[idx] should be part of extended commands menu */ +static boolean +ignore_extcmd(idx) +int idx; +{ + /* #shell or #suspect might not be available; + 'extmenu' option controls whether we show full list + or just the traditional extended commands */ + if ((extcmdlist[idx].flags & CMD_NOT_AVAILABLE) != 0 + || ((extcmdlist[idx].flags & AUTOCOMPLETE) == 0 && !ec_full_list) + || strlen(extcmdlist[idx].ef_txt) < 2) /* ignore "#" and "?" */ + return TRUE; + + return FALSE; +} + /* ARGSUSED */ void ec_key(w, event, params, num_params) @@ -1886,19 +1917,14 @@ Cardinal *num_params; if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); extended_cmd_selected = -1; /* dismiss */ - ec_chars[0] = ec_chars[ec_nchars-1]; + ec_chars[0] = ec_chars[ec_nchars - 1]; ec_nchars = 1; } - for (i = 0; extcmdlist[i].ef_txt; i++) { - if (extcmdlist[i].flags & CMD_NOT_AVAILABLE) - continue; - if (extcmdlist[i].ef_txt[0] == '?') - continue; - - if (!strncmp(ec_chars, extcmdlist[i].ef_txt, ec_nchars)) { + for (i = 0; command_list[i]; ++i) { + if (!strncmp(ec_chars, command_list[i], ec_nchars)) { if (extended_cmd_selected != i) { - /* I should use set() and unset() actions, but how do */ - /* I send the an action to the widget? */ + /* I should use set() and unset() actions, but how do + I send the an action to the widget? */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); extended_cmd_selected = i; @@ -1908,11 +1934,10 @@ Cardinal *num_params; ambiguous choices, plus one to show thare aren't any more such, will scroll into view */ do { - if (!extcmdlist[i + 1].ef_txt - || *extcmdlist[i + 1].ef_txt == '?') + if (!command_list[i + 1]) break; /* end of list */ ++i; - } while (!strncmp(ec_chars, extcmdlist[i].ef_txt, ec_nchars)); + } while (!strncmp(ec_chars, command_list[i], ec_nchars)); ec_scroll_to_view(i); return; @@ -1929,25 +1954,24 @@ static void init_extended_commands_popup() { int i, j, num_commands, ignore_cmds = 0; - const char **command_list; /* count commands */ for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++) - if (extcmdlist[num_commands].flags & CMD_NOT_AVAILABLE) + if (ignore_extcmd(num_commands)) ++ignore_cmds; - /* If the last entry is "help", don't use it. */ - if (strcmp(extcmdlist[num_commands - 1].ef_txt, "?") == 0) - --num_commands; - j = num_commands - ignore_cmds; - command_list = (const char **) alloc((unsigned) j * sizeof (char *)); + command_list = (const char **) alloc((unsigned) (j * sizeof (char *) + 1)); + command_indx = (short *) alloc((unsigned) (j * sizeof (short) + 1)); for (i = j = 0; i < num_commands; i++) { - if (extcmdlist[i].flags & CMD_NOT_AVAILABLE) + if (ignore_extcmd(i)) continue; + command_indx[j] = (short) i; command_list[j++] = extcmdlist[i].ef_txt; } + command_list[j] = (char *) 0; + command_indx[j] = -1; num_commands = j; extended_command_popup = @@ -1955,8 +1979,6 @@ init_extended_commands_popup() extended_command_translations, "dismiss", extend_dismiss, "help", extend_help, num_commands, command_list, &extended_commands, extend_select, &extended_command_form); - - free((char *) command_list); } /* ------------------------------------------------------------------------ */ From 10dac50433f0e4d0a2c8cf86791b3a5f7ba9f9ea Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Apr 2019 09:27:09 -0700 Subject: [PATCH 14/17] X11 extended commands menu scrolling Support for scrolling within menus via first-/previous-/next-/last- page keystrokes ("^<>|" by default) was added to X11's general menu handling but the extended commands menu uses a special menu rather than a general one. This clones the relevant code to add support for those keys to extended commands. --- doc/fixes36.2 | 5 +++-- win/X11/winmisc.c | 28 ++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/doc/fixes36.2 b/doc/fixes36.2 index fcf4c17e6..ea7eab82d 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -1,4 +1,4 @@ -$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.295 $ $NHDT-Date: 1554134321 2019/04/01 15:58:41 $ +$NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.296 $ $NHDT-Date: 1554136021 2019/04/01 16:27:01 $ This fixes36.2 file is here to capture information about updates in the 3.6.x lineage following the release of 3.6.1 in April 2018. Please note, however, @@ -501,6 +501,7 @@ X11: its use of genl_status_update exposed a negative index use that could X11: rollback disabling of keystroke input for PICK_NONE menus (for scrolling) X11: make use of the 'extmenu' option: On to choose among all commands, Off to choose among the traditional extended command subset +X11: support menu scrolling via (^ < > |) in the extended commands menu curses: catch up with tty to not put dolook/whatis autodescribe feedback into ^P message recall (multi-digit count feedback was already handled) curses: if the interface code ran out of memory, it would crash rather than @@ -614,7 +615,7 @@ windows-tty: add support for mouse_support:0 (disabled), mouse_support:1 X11: implement menucolors and allow menus to obey some attributes X11: make key translations work with menus on Linux X11: allow mouse wheel scrolling to work in menus by default -X11: handle paged menu control keys +X11: handle menu scrolling via first-/previous-/next-/last-page keys (^ < > |) X11: remember perm_invent window geometry X11: handle X errors via panic X11: don't reuse perm_invent window for picking an object diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index 13740a99b..04eb682c9 100644 --- a/win/X11/winmisc.c +++ b/win/X11/winmisc.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 winmisc.c $NHDT-Date: 1554134316 2019/04/01 15:58:36 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.43 $ */ +/* NetHack 3.6 winmisc.c $NHDT-Date: 1554135506 2019/04/01 16:18:26 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.44 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -1856,11 +1856,12 @@ String *params; Cardinal *num_params; { char ch; - int i; - int pass; + int i, pass; + float shown, top; + Arg arg[2]; + Widget hbar, vbar; XKeyEvent *xkey = (XKeyEvent *) event; - nhUse(w); nhUse(params); nhUse(num_params); @@ -1888,6 +1889,25 @@ Cardinal *num_params; exit_x_event = TRUE; /* leave event loop */ ec_active = FALSE; return; + } else if (ch == MENU_FIRST_PAGE || ch == MENU_LAST_PAGE) { + hbar = vbar = (Widget) 0; + find_scrollbars(w, &hbar, &vbar); + if (vbar) { + top = (ch == MENU_FIRST_PAGE) ? 0.0 : 1.0; + XtCallCallbacks(vbar, XtNjumpProc, &top); + } + return; + } else if (ch == MENU_NEXT_PAGE || ch == MENU_PREVIOUS_PAGE) { + hbar = vbar = (Widget) 0; + find_scrollbars(w, &hbar, &vbar); + if (vbar) { + XtSetArg(arg[0], nhStr(XtNshown), &shown); + XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top); + XtGetValues(vbar, arg, TWO); + top += ((ch == MENU_NEXT_PAGE) ? shown : -shown); + XtCallCallbacks(vbar, XtNjumpProc, &top); + } + return; } /* From 72696a36a566c18c68d81c3e1962afb146a2cf8c Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Apr 2019 14:56:00 -0700 Subject: [PATCH 15/17] build fix for X11-only I didn't noticed this because I've been building for tty+curses+X11 and either of the first two cause iflags.extmenu to exist. Make it unconditional; there's not much benefit from trying to suppress it for configurations that don't need it. --- include/flag.h | 4 +--- src/options.c | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/include/flag.h b/include/flag.h index fa11a3dfa..b5e1bcfda 100644 --- a/include/flag.h +++ b/include/flag.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 flag.h $NHDT-Date: 1554045808 2019/03/31 15:23:28 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.149 $ */ +/* NetHack 3.6 flag.h $NHDT-Date: 1554155745 2019/04/01 21:55:45 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.150 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2006. */ /* NetHack may be freely redistributed. See license for details. */ @@ -322,10 +322,8 @@ struct instance_flags { #endif #endif uchar bouldersym; /* symbol for boulder display */ -#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) char prevmsg_window; /* type of old message window to use */ boolean extmenu; /* extended commands use menu interface */ -#endif #ifdef MFLOPPY boolean checkspace; /* check disk space before writing files */ /* (in iflags to allow restore after moving diff --git a/src/options.c b/src/options.c index c4471cf61..a9701c23e 100644 --- a/src/options.c +++ b/src/options.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 options.c $NHDT-Date: 1553858470 2019/03/29 11:21:10 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.361 $ */ +/* NetHack 3.6 options.c $NHDT-Date: 1554155747 2019/04/01 21:55:47 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.362 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2008. */ /* NetHack may be freely redistributed. See license for details. */ @@ -114,7 +114,7 @@ static struct Bool_Opt { { "confirm", &flags.confirm, TRUE, SET_IN_GAME }, { "dark_room", &flags.dark_room, TRUE, SET_IN_GAME }, { "eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME }, /*WC*/ -#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) || defined(X11_GRAPHICS) { "extmenu", &iflags.extmenu, FALSE, SET_IN_GAME }, #else { "extmenu", (boolean *) 0, FALSE, SET_IN_FILE }, From be966cfe5aaa017146435e2342a83e9610c02595 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 2 Apr 2019 01:11:59 -0700 Subject: [PATCH 16/17] curses ^P msg_window:Full This changes the recently added msg_window:f for curses to start viewing the old messages on the last page rather than the first. For msg_window:Reversed (the default for curses) and for either direction when all of the message history happens to fit on one page, there's no change. But for multiple pages, the FIFO feedback now pads the top of the first page with blank lines so that the last page is full, and it starts out showing that last page first. So if you only want to go back few or several messages, they will be in view immediately. Old layout: |first message (oldest) | |1st message of last page | |2nd message of 1st page | | ... | | ... | |final (most recent) mesg | | ... | | (blank filler) | |last message of 1st page | | (blank filler) | | (1 of 2) => | | <= (2 of 2) | and ^P started with first page visible and needed normal menu handling, or '>' or '|', to go forward to view the most recent messages. New layout: |1st message of last page | | (blank filler) | |2nd message of last page | | (blank filler) | | ... | |first message (oldest) | | ... | | ... | |final (most recent) | |last message of 1st page | | <= (2 of 2) | | (1 of 2) => | and ^P starts on last page (two of two in this example) but can go back with '<' and '^'. So if the total size takes one and third pages (which isn't uncommon for the default number of kept messages), you'll see 3/4 of the most recent messages on the initial screen, then you can page backward if you want to see the other 1/4. The page indicator is deliberately drawn a bit differently just to draw attention to the fact you're starting on the last page. I'm not sure whether that is actually worthwhile but it was trivial to do. --- include/wincurs.h | 1 + win/curses/cursdial.c | 142 +++++++++++++++++++++++++++++++++--------- win/curses/cursdial.h | 1 + win/curses/cursmesg.c | 2 + 4 files changed, 117 insertions(+), 29 deletions(-) diff --git a/include/wincurs.h b/include/wincurs.h index 165facaba..1dc6400d5 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -182,6 +182,7 @@ extern void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier, CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str, BOOLEAN_P presel); +extern void curs_menu_set_bottom_heavy(winid); extern void curses_finalize_nhmenu(winid wid, const char *prompt); extern int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P **_selected); extern boolean curses_menu_exists(winid wid); diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index deee077fd..8ec5d20b5 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -81,6 +81,7 @@ typedef struct nhm { int height; /* Window height of menu */ int width; /* Window width of menu */ boolean reuse_accels; /* Non-unique accelerators per page */ + boolean bottom_heavy; /* display multi-page menu starting at end */ struct nhm *prev_menu; /* Pointer to previous entry */ struct nhm *next_menu; /* Pointer to next entry */ } nhmenu; @@ -91,6 +92,8 @@ typedef enum menu_op_type { INVERT } menu_op; +static nhmenu_item *curs_new_menu_item(winid, const char *); +static void curs_pad_menu(nhmenu *, boolean); static nhmenu *get_menu(winid wid); static char menu_get_accel(boolean first); static void menu_determine_pages(nhmenu *menu); @@ -379,6 +382,7 @@ curses_ext_cmd() starty = 0; if (iflags.wc_popup_dialog) { /* Prompt in popup window */ int x0, y0, w, h; /* bounding coords of popup */ + extwin2 = curses_create_window(25, 1, UP); wrefresh(extwin2); /* create window inside window to prevent overwriting of border */ @@ -523,6 +527,7 @@ curses_create_nhmenu(winid wid) new_menu->height = 0; new_menu->width = 0; new_menu->reuse_accels = FALSE; + new_menu->bottom_heavy = FALSE; return; } @@ -534,6 +539,7 @@ curses_create_nhmenu(winid wid) new_menu->height = 0; new_menu->width = 0; new_menu->reuse_accels = FALSE; + new_menu->bottom_heavy = FALSE; new_menu->next_menu = NULL; if (nhmenus == NULL) { /* no menus in memory yet */ @@ -548,15 +554,38 @@ curses_create_nhmenu(winid wid) } } +static nhmenu_item * +curs_new_menu_item(winid wid, const char *str) +{ + char *new_str; + nhmenu_item *new_item; + new_str = curses_copy_of(str); + curses_rtrim(new_str); + new_item = (nhmenu_item *) alloc((unsigned) sizeof (nhmenu_item)); + new_item->wid = wid; + new_item->glyph = NO_GLYPH; + new_item->identifier = zeroany; + new_item->accelerator = '\0';; + new_item->group_accel = '\0'; + new_item->attr = 0; + new_item->str = new_str; + new_item->presel = FALSE; + new_item->selected = FALSE; + new_item->page_num = 0; + new_item->line_num = 0; + new_item->num_lines = 0; + new_item->count = -1; + new_item->next_item = NULL; + return new_item; +} /* Add a menu item to the given menu window */ void -curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier, +curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier, CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str, BOOLEAN_P presel) { - char *new_str; nhmenu_item *new_item, *current_items, *menu_item_ptr; nhmenu *current_menu = get_menu(wid); @@ -570,23 +599,13 @@ curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier, return; } - new_str = curses_copy_of(str); - curses_rtrim((char *) new_str); - new_item = (nhmenu_item *) alloc((unsigned) sizeof (nhmenu_item)); - new_item->wid = wid; + new_item = curs_new_menu_item(wid, str); new_item->glyph = glyph; new_item->identifier = *identifier; new_item->accelerator = accelerator; new_item->group_accel = group_accel; new_item->attr = attr; - new_item->str = new_str; new_item->presel = presel; - new_item->selected = FALSE; - new_item->page_num = 0; - new_item->line_num = 0; - new_item->num_lines = 0; - new_item->count = -1; - new_item->next_item = NULL; current_items = current_menu->entries; menu_item_ptr = current_items; @@ -603,6 +622,67 @@ curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier, } } +/* for menu->bottom_heavy -- insert enough blank lines at top of + first page to make the last page become a full one */ +static void +curs_pad_menu(nhmenu *current_menu, boolean do_pad UNUSED) +{ + nhmenu_item *menu_item_ptr; + int numpages = current_menu->num_pages; + + /* caller has already called menu_win_size() */ + menu_determine_pages(current_menu); /* sets 'menu->num_pages' */ + numpages = current_menu->num_pages; + /* pad beginning of menu so that partial last page becomes full; + might be slightly less than full if any entries take multiple + lines and the padding would force those to span page boundary + and that gets prevented; so we re-count the number of pages + with every insertion instead of trying to calculate the number + of them to add */ + do { + menu_item_ptr = curs_new_menu_item(current_menu->wid, ""); + menu_item_ptr->next_item = current_menu->entries; + current_menu->entries->prev_item = menu_item_ptr; + current_menu->entries = menu_item_ptr; + current_menu->num_entries += 1; + + menu_determine_pages(current_menu); + } while (current_menu->num_pages == numpages); + + /* we inserted blank lines at beginning until final entry spilled + over to another page; take the most recent blank one back out */ + current_menu->num_entries -= 1; + current_menu->entries = menu_item_ptr->next_item; + current_menu->entries->prev_item = (nhmenu_item *) 0; + free((genericptr_t) menu_item_ptr->str); + free((genericptr_t) menu_item_ptr); + + /* reset page count; shouldn't need to re-count */ + current_menu->num_pages = numpages; + return; +} + +/* mark ^P message recall menu, for msg_window:full (FIFO), where we'll + start viewing on the last page so be able to see most recent immediately */ +void +curs_menu_set_bottom_heavy(winid wid) +{ + nhmenu *menu = get_menu(wid); + + /* + * Called after end_menu + finalize_nhmenu, + * before select_menu + display_nhmenu. + */ + menu_win_size(menu); /* (normally not done until display_nhmenu) */ + if (menu_is_multipage(menu, menu->width, menu->height)) { + menu->bottom_heavy = TRUE; + + /* insert enough blank lines at top of first page to make the + last page become a full one */ + curs_pad_menu(menu, TRUE); + } + return; +} /* No more entries are to be added to menu, so details of the menu can be finalized in memory */ @@ -699,8 +779,8 @@ curses_display_nhmenu(winid wid, int how, MENU_ITEM_P ** _selected) } if (count != num_chosen) { - impossible("curses_display_nhmenu: Selected items less than " - "expected number"); + impossible( + "curses_display_nhmenu: Selected items less than expected number"); } } @@ -753,7 +833,7 @@ curses_del_menu(winid wid, boolean del_wid_too) if ((tmpmenu = current_menu->prev_menu) != NULL) { tmpmenu->next_menu = current_menu->next_menu; } else { - nhmenus = current_menu->next_menu; /* New head mode or NULL */ + nhmenus = current_menu->next_menu; /* New head node or NULL */ } if ((tmpmenu = current_menu->next_menu) != NULL) { tmpmenu->prev_menu = current_menu->prev_menu; @@ -821,7 +901,7 @@ menu_is_multipage(nhmenu *menu, int width, int height) int curline = 0; nhmenu_item *menu_item_ptr = menu->entries; - if (strlen(menu->prompt) > 0) { + if (*menu->prompt) { curline += curses_num_lines(menu->prompt, width) + 1; } @@ -864,7 +944,7 @@ menu_determine_pages(nhmenu *menu) int page_end = height; - if (strlen(menu->prompt) > 0) { + if (*menu->prompt) { curline += curses_num_lines(menu->prompt, width) + 1; } @@ -932,8 +1012,7 @@ menu_win_size(nhmenu *menu) /* Add space for accelerator */ curentrywidth = strlen(menu_item_ptr->str) + 4; #if 0 /* FIXME: menu glyphs */ - if (menu_item_ptr->glyph != NO_GLYPH - && iflags.use_menu_glyphs) + if (menu_item_ptr->glyph != NO_GLYPH && iflags.use_menu_glyphs) curentrywidth += 2; #endif } @@ -989,7 +1068,7 @@ static void menu_display_page(nhmenu *menu, WINDOW * win, int page_num) { nhmenu_item *menu_item_ptr; - int count, curletter, entry_cols, start_col, num_lines, footer_x; + int count, curletter, entry_cols, start_col, num_lines; char *tmpstr; boolean first_accel = TRUE; int color = NO_COLOR; @@ -1112,20 +1191,25 @@ menu_display_page(nhmenu *menu, WINDOW * win, int page_num) } if (menu->num_pages > 1) { - footer_x = menu->width - strlen("<- (Page X of Y) ->"); - if (menu->num_pages > 9) { /* Unlikely */ - footer_x -= 2; + int footer_x, footwidth, shoesize = menu->num_pages; + + footwidth = (int) (sizeof "<- (Page X of Y) ->" - sizeof ""); + while (shoesize >= 10) { /* possible for pickup from big piles... */ + /* room for wider feet; extra digit for both X and Y */ + footwidth += 2; + shoesize /= 10; } - mvwprintw(win, menu->height, footer_x + 3, "(Page %d of %d)", - page_num, menu->num_pages); + footer_x = !menu->bottom_heavy ? (menu->width - footwidth) : 2; if (page_num != 1) { curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); mvwaddstr(win, menu->height, footer_x, "<="); curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); } + mvwprintw(win, menu->height, footer_x + 2, " (Page %d of %d) ", + page_num, menu->num_pages); if (page_num != menu->num_pages) { curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); - mvwaddstr(win, menu->height, menu->width - 2, "=>"); + mvwaddstr(win, menu->height, footer_x + footwidth - 2, "=>"); curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); } } @@ -1142,13 +1226,13 @@ menu_get_selections(WINDOW * win, nhmenu *menu, int how) int curletter; int count = -1; int count_letter = '\0'; - int curpage = 1; + int curpage = !menu->bottom_heavy ? 1 : menu->num_pages; int num_selected = 0; boolean dismiss = FALSE; char search_key[BUFSZ]; nhmenu_item *menu_item_ptr = menu->entries; - menu_display_page(menu, win, 1); + menu_display_page(menu, win, curpage); while (!dismiss) { curletter = getch(); diff --git a/win/curses/cursdial.h b/win/curses/cursdial.h index 0e12be6a5..acc6f855b 100644 --- a/win/curses/cursdial.h +++ b/win/curses/cursdial.h @@ -16,6 +16,7 @@ void curses_create_nhmenu(winid wid); void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier, CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str, BOOLEAN_P presel); +void curs_menu_set_bottom_heavy(winid); void curses_finalize_nhmenu(winid wid, const char *prompt); int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P **_selected); boolean curses_menu_exists(winid wid); diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index c3d0aa565..2c1decb9a 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -313,6 +313,8 @@ curses_prev_mesg() "[No past messages available.]", FALSE); curses_end_menu(wid, ""); + if (!do_lifo) + curs_menu_set_bottom_heavy(wid); curses_select_menu(wid, PICK_NONE, &selected); curses_del_wid(wid); } From add4d4d72485c841de30e84bfe247f5ea9cf14a5 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 2 Apr 2019 07:38:57 -0700 Subject: [PATCH 17/17] untested build fix for term_attr_fixup() modified: sys/mac/mttymain.c sys/msdos/video.c sys/winnt/nttty.c --- sys/mac/mttymain.c | 9 ++++++++- sys/msdos/video.c | 8 +++++++- sys/winnt/nttty.c | 8 +++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/sys/mac/mttymain.c b/sys/mac/mttymain.c index f4b762053..5231e84da 100644 --- a/sys/mac/mttymain.c +++ b/sys/mac/mttymain.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mttymain.c $NHDT-Date: 1432512797 2015/05/25 00:13:17 $ $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ */ +/* NetHack 3.6 mttymain.c $NHDT-Date: 1554215928 2019/04/02 14:38:48 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.13 $ */ /* Copyright (c) Jon W{tte, 1993 */ /* NetHack may be freely redistributed. See license for details. */ @@ -369,6 +369,13 @@ _mt_set_colors(long *colors) err = set_tty_attrib(_mt_window, TTY_ATTRIB_BACKGROUND, colors[1]); } +int +term_attr_fixup(int attrmask) +{ + attrmask &= ~ATR_DIM; + return attrmask; +} + void term_end_attr(int attr) { diff --git a/sys/msdos/video.c b/sys/msdos/video.c index cfa5e972e..76002cdf6 100644 --- a/sys/msdos/video.c +++ b/sys/msdos/video.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 video.c $NHDT-Date: 1457207042 2016/03/05 19:44:02 $ $NHDT-Branch: chasonr $:$NHDT-Revision: 1.11 $ */ +/* NetHack 3.6 video.c $NHDT-Date: 1554215931 2019/04/02 14:38:51 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.12 $ */ /* Copyright (c) NetHack PC Development Team 1993, 1994, 2001 */ /* NetHack may be freely redistributed. See license for details. */ /* */ @@ -285,6 +285,12 @@ standoutend() g_attribute = iflags.grmode ? attrib_gr_normal : attrib_text_normal; } +int +term_attr_fixup(int attrmask) +{ + return attrmask; +} + void term_end_attr(int attr) { diff --git a/sys/winnt/nttty.c b/sys/winnt/nttty.c index 72354c967..3233dc9cb 100644 --- a/sys/winnt/nttty.c +++ b/sys/winnt/nttty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 nttty.c $NHDT-Date: 1524931557 2018/04/28 16:05:57 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.71 $ */ +/* NetHack 3.6 nttty.c $NHDT-Date: 1554215932 2019/04/02 14:38:52 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.99 $ */ /* Copyright (c) NetHack PC Development Team 1993 */ /* NetHack may be freely redistributed. See license for details. */ @@ -802,6 +802,12 @@ has_color(int color) return 0; } +int +term_attr_fixup(int attrmask) +{ + return attrmask; +} + void term_start_attr(int attrib) {