diff --git a/dat/Valkyrie.des b/dat/Valkyrie.des index 21541ab0a..252c6ce82 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: 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,9 +216,14 @@ 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 from the south (portcullis) to further +# north (lowered span), southern one from the north to further south 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/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 051f61299..5992a75c0 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.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. @@ -3657,7 +3661,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..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 @@ -4057,7 +4062,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 9439ab4d3..ea7eab82d 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.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, @@ -398,6 +398,14 @@ 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 +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 @@ -478,9 +486,22 @@ 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) +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) +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 @@ -508,6 +529,12 @@ 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 +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 @@ -588,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 @@ -646,10 +673,13 @@ 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 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/botl.h b/include/botl.h index 4c9d1a71f..587d9805f 100644 --- a/include/botl.h +++ b/include/botl.h @@ -104,6 +104,8 @@ enum hlattribs { HL_UNDEF = 0x00, HL_BLINK = 0x10, HL_DIM = 0x20 }; +#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; @@ -111,7 +113,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; @@ -130,6 +132,8 @@ 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 }; diff --git a/include/decl.h b/include/decl.h index 28b4bf6b1..5226beee1 100644 --- a/include/decl.h +++ b/include/decl.h @@ -644,6 +644,7 @@ struct instance_globals { boolean valset[MAXBLSTATS]; long bl_hilite_moves; unsigned long cond_hilites[BL_ATTCLR_MAX]; + int now_or_before_idx; /* 0..1 for array[2][] first index */ /* cmd.c */ struct cmd Cmd; /* flag.h */ diff --git a/include/extern.h b/include/extern.h index 8d6e5d483..85b5e8994 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: 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. */ @@ -160,6 +160,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)); @@ -352,6 +353,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/include/flag.h b/include/flag.h index 9b4828e7e..ea69ba239 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: 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 @@ -364,9 +362,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/include/wincurs.h b/include/wincurs.h index 79893ddcd..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); @@ -222,6 +223,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/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/allmain.c b/src/allmain.c index 074ed6ec4..110dd8857 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. */ @@ -183,7 +183,7 @@ boolean resuming; if (u.ublesscnt) u.ublesscnt--; if (flags.time && !g.context.run) - g.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. @@ -209,8 +209,10 @@ boolean resuming; : g.moves % 10)) { if (Upolyd && u.mh > 1) { u.mh--; + g.context.botl = TRUE; } else if (!Upolyd && u.uhp > 1) { u.uhp--; + g.context.botl = TRUE; } else { You("pass out from exertion!"); exercise(A_CON, FALSE); @@ -236,6 +238,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()) { @@ -361,6 +364,9 @@ boolean resuming; if (g.context.botl || g.context.botlx) { bot(); curs_on_u(); + } else if (iflags.time_botl) { + timebot(); + curs_on_u(); } g.context.move = 1; diff --git a/src/botl.c b/src/botl.c index ddd709ceb..22605c3d2 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: 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. */ @@ -16,6 +16,7 @@ const char *const enc_stat[] = { "", "Burdened", "Stressed", 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 * @@ -243,7 +244,21 @@ bot() putmixed(WIN_STATUS, 0, do_statusline2()); #endif } - g.context.botl = g.context.botlx = 0; + g.context.botl = g.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) */ @@ -405,21 +420,19 @@ 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 *)); @@ -437,18 +450,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) (g.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), @@ -469,13 +489,14 @@ 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 /* we don't put this next declaration in #ifdef STATUS_HILITES. * In the absence of STATUS_HILITES, each array @@ -489,15 +510,17 @@ void bot_via_windowport() { char buf[BUFSZ]; + const char *titl; register char *nb; - static int i, idx = 0, idx_p, cap; + int i, idx, cap; long money; if (!g.blinit) panic("bot before init."); - idx_p = idx; - idx = 1 - idx; /* 0 -> 1, 1 -> 0 */ + /* toggle from previous iteration */ + idx = 1 - g.now_or_before_idx; /* 0 -> 1, 1 -> 0 */ + g.now_or_before_idx = idx; /* clear the "value set" indicators */ (void) memset((genericptr_t) g.valset, 0, MAXBLSTATS * sizeof (boolean)); @@ -513,15 +536,23 @@ bot_via_windowport() */ Strcpy(nb = buf, g.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(g.blstats[idx][BL_TITLE].val, "%-29s", buf); + } + Sprintf(g.blstats[idx][BL_TITLE].val, "%-30s", buf); g.valset[BL_TITLE] = TRUE; /* indicate val already set */ /* Strength */ @@ -653,12 +684,30 @@ bot_via_windowport() g.blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_FLY; if (u.usteed) g.blstats[idx][BL_CONDITION].a.a_ulong |= BL_MASK_RIDE; - evaluate_and_notify_windowport(g.valset, idx, idx_p); + evaluate_and_notify_windowport(g.valset, idx); +} + +/* update just the status lines' 'time' field */ +STATIC_OVL void +stat_update_time() +{ + int idx = g.now_or_before_idx; /* no 0/1 toggle */ + int fld = BL_TIME; + + /* Time (moves) */ + g.blstats[idx][fld].a.a_long = g.moves; + g.valset[fld] = FALSE; + + eval_notify_windowport_field(fld, g.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 -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; @@ -674,7 +723,7 @@ boolean *valsetlist; */ anytype = g.blstats[idx][fld].anytype; curr = &g.blstats[idx][fld]; - prev = &g.blstats[idx_p][fld]; + prev = &g.blstats[1 - idx][fld]; color = NO_COLOR; chg = g.update_all ? 0 : compare_blstats(prev, curr); @@ -707,7 +756,7 @@ boolean *valsetlist; reset = FALSE; #ifdef STATUS_HILITES - if (!g.update_all && !chg) { + if (!g.update_all && !chg && curr->time) { reset = hilite_reset_needed(prev, g.bl_hilite_moves); if (reset) curr->time = prev->time = 0L; @@ -723,9 +772,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; @@ -733,11 +784,11 @@ boolean *valsetlist; } #endif /* STATUS_HILITES */ status_update(fld, (genericptr_t) curr->val, - chg, pc, color, &g.cond_hilites[0]); + chg, pc, color, (unsigned long *) 0); } else { /* Color for conditions is done through g.cond_hilites[] */ - status_update(fld, (genericptr_t) &curr->a.a_ulong, chg, pc, - color, &g.cond_hilites[0]); + status_update(fld, (genericptr_t) &curr->a.a_ulong, + chg, pc, color, g.cond_hilites); } curr->chg = prev->chg = TRUE; updated = TRUE; @@ -746,8 +797,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; @@ -764,7 +815,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++; } /* @@ -792,13 +843,13 @@ boolean *valsetlist; */ if (g.context.botlx && (windowprocs.wincap2 & WC2_RESET_STATUS) != 0L) status_update(BL_RESET, (genericptr_t) 0, 0, 0, - NO_COLOR, &g.cond_hilites[0]); + NO_COLOR, (unsigned long *) 0); else if ((updated || g.context.botlx) && (windowprocs.wincap2 & WC2_FLUSH_STATUS) != 0L) status_update(BL_FLUSH, (genericptr_t) 0, 0, 0, - NO_COLOR, &g.cond_hilites[0]); + NO_COLOR, (unsigned long *) 0); - g.context.botl = g.context.botlx = 0; + g.context.botl = g.context.botlx = iflags.time_botl = FALSE; g.update_all = FALSE; } @@ -806,11 +857,12 @@ void status_eval_next_unhilite() { int i; - struct istat_s *curr = NULL; + struct istat_s *curr; long next_unhilite, this_unhilite; - g.bl_hilite_moves = g.moves; - /* figure out when the next unhilight needs to be performed */ + g.bl_hilite_moves = g.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 = &g.blstats[0][i]; /* g.blstats[0][*].time == g.blstats[1][*].time */ @@ -819,10 +871,17 @@ status_eval_next_unhilite() struct istat_s *prev = &g.blstats[1][i]; #ifdef STATUS_HILITES - curr->time = prev->time = (g.bl_hilite_moves + iflags.hilite_delta); + if (Is_Temp_Hilite(curr->hilite_rule)) + curr->time = prev->time = (g.bl_hilite_moves + + iflags.hilite_delta); + else + curr->time = prev->time = 0L; #endif curr->chg = prev->chg = FALSE; + g.context.botl = TRUE; } + if (g.context.botl) + continue; /* just process other g.blstats[][].time and .chg */ this_unhilite = curr->time; if (this_unhilite > 0L @@ -830,21 +889,22 @@ status_eval_next_unhilite() #ifdef STATUS_HILITES && hilite_reset_needed(curr, this_unhilite + 1L) #endif - ) + ) { next_unhilite = this_unhilite; + if (next_unhilite < g.bl_hilite_moves) + g.context.botl = TRUE; + } } - if (next_unhilite > 0L && next_unhilite < g.bl_hilite_moves) - g.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 (g.blinit) @@ -852,22 +912,22 @@ boolean init_blstats(); (*windowprocs.win_status_init)(); g.blinit = TRUE; + } else if (!g.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); } g.update_all = TRUE; } @@ -888,14 +948,17 @@ status_finish() if (g.blstats[1][i].val) free((genericptr_t) g.blstats[1][i].val), g.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 */ + g.blstats[0][i].hilite_rule = g.blstats[1][i].hilite_rule = 0; if (g.blstats[0][i].thresholds) { - struct hilite_s *temp, *next = 0; + struct hilite_s *temp, *next; for (temp = g.blstats[0][i].thresholds; temp; temp = next) { next = temp->next; free((genericptr_t) temp); - g.blstats[0][i].thresholds = g.blstats[1][i].thresholds = 0; } + g.blstats[0][i].thresholds = g.blstats[1][i].thresholds = 0; } #endif /* STATUS_HILITES */ } @@ -911,9 +974,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 = g.blstats[i][j].thresholds; @@ -931,6 +992,7 @@ init_blstats() #endif } } + initalready = TRUE; } /* @@ -1192,7 +1254,7 @@ struct istat_s *bl, *maxbl; /* Core status hiliting support */ /****************************************************************************/ -static const struct fieldid_t { +static struct fieldid_t { const char *fieldname; enum statusfields fldid; } fieldids_alias[] = { @@ -1279,26 +1341,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 (g.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 */ @@ -1329,33 +1384,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 @@ -1364,28 +1399,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 (g.blstats[idx][fldidx].thresholds) { + if (has_hilite(fldidx)) { int dt; /* there are hilites set here */ int max_pc = -1, min_pc = 101; @@ -1399,7 +1428,7 @@ int *colorptr; perc_or_abs = FALSE; /* min_/max_ are used to track best fit */ - for (hl = g.blstats[idx][fldidx].thresholds; hl; hl = hl->next) { + for (hl = g.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 @@ -1414,7 +1443,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) { @@ -1422,25 +1451,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; } @@ -1449,13 +1478,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; @@ -1468,7 +1497,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) { @@ -1476,32 +1505,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) { @@ -1509,25 +1538,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; } @@ -1540,18 +1569,18 @@ int *colorptr; txtstr += (strlen(g.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; @@ -1560,8 +1589,8 @@ int *colorptr; } } } - *colorptr = bestcolor; - return; + *colorptr = rule ? rule->coloridx : NO_COLOR; + return rule; } STATIC_OVL void @@ -2071,9 +2100,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); } @@ -2259,6 +2288,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 */ @@ -2362,17 +2400,15 @@ clear_status_hilites() int i; for (i = 0; i < MAXBLSTATS; ++i) { - if (g.blstats[0][i].thresholds) { - struct hilite_s *temp = g.blstats[0][i].thresholds, - *next = (struct hilite_s *)0; - while (temp) { - next = temp->next; - free(temp); - g.blstats[0][i].thresholds = (struct hilite_s *)0; - g.blstats[1][i].thresholds = g.blstats[0][i].thresholds; - temp = next; - } + struct hilite_s *temp, *next; + + for (temp = g.blstats[0][i].thresholds; temp; temp = next) { + next = temp->next; + free(temp); } + g.blstats[0][i].thresholds = g.blstats[1][i].thresholds = 0; + /* pointer into thresholds list, now stale */ + g.blstats[0][i].hilite_rule = g.blstats[1][i].hilite_rule = 0; } } @@ -3152,7 +3188,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++) { @@ -3199,7 +3235,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; @@ -3309,25 +3345,25 @@ int id; return TRUE; } else { int fld = hlstr->fld; - struct hilite_s *hl = g.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 { - g.blstats[0][fld].thresholds = hl->next; - g.blstats[1][fld].thresholds = - g.blstats[0][fld].thresholds; - } - free((genericptr_t) hl); - return TRUE; + for (hl = g.blstats[0][fld].thresholds; hl; hl = hl->next) { + if (hlstr->hl == hl) { + if (hlprev) { + hlprev->next = hl->next; + } else { + g.blstats[0][fld].thresholds = hl->next; + g.blstats[1][fld].thresholds = g.blstats[0][fld].thresholds; } - hlprev = hl; - hl = hl->next; + if (g.blstats[0][fld].hilite_rule == hl) { + g.blstats[0][fld].hilite_rule + = g.blstats[1][fld].hilite_rule = (struct hilite_s *) 0; + g.blstats[0][fld].time = g.blstats[1][fld].time = 0L; + } + free((genericptr_t) hl); + return TRUE; } + hlprev = hl; } } return FALSE; diff --git a/src/decl.c b/src/decl.c index 7cd721a80..c8d214e79 100644 --- a/src/decl.c +++ b/src/decl.c @@ -220,6 +220,7 @@ const struct instance_globals g_init = { UNDEFINED_VALUES, /* valset */ 0, /* bl_hilite_moves */ UNDEFINED_VALUES, /* cond_hilites */ + 0, /* now_or_before_idx */ /* cmd.c */ UNDEFINED_VALUES, /* Cmd */ diff --git a/src/display.c b/src/display.c index 786e0f64b..dfb379789 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: 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. */ @@ -1213,8 +1213,7 @@ int mode; } } -/* ========================================================================= - */ +/* ======================================================================== */ /* * Loop through all of the monsters and update them. Called when: @@ -1380,10 +1379,39 @@ docrt() g.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 || g.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) =========================================== */ /* FIXME: This is a dirty hack, because newsym() doesn't distinguish * between object piles and single objects, it doesn't mark the location @@ -1527,7 +1555,7 @@ int start, stop, y; for (x = start; x <= stop; x++) if (g.gbuf[y][x].glyph != cmap_to_glyph(S_stone)) - print_glyph(WIN_MAP, x, y, g.gbuf[y][x].glyph, get_bk_glyph(x,y)); + print_glyph(WIN_MAP, x, y, g.gbuf[y][x].glyph, get_bk_glyph(x, y)); } void @@ -1588,10 +1616,11 @@ int cursor_on_u; flushing = 0; if (g.context.botl || g.context.botlx) bot(); + else if (iflags.time_botl) + timebot(); } -/* ========================================================================= - */ +/* ======================================================================== */ /* * back_to_glyph() @@ -1795,16 +1824,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/src/do_name.c b/src/do_name.c index 6f169fa1b..0bfd9e179 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 ", diff --git a/src/end.c b/src/end.c index ae7a35bf7..c48b1c1af 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. */ @@ -1025,7 +1025,7 @@ int how; #endif ) { /* skip status update if panicking or disconnected */ - g.context.botl = g.context.botlx = FALSE; + g.context.botl = g.context.botlx = iflags.time_botl = FALSE; } else { /* otherwise force full status update */ g.context.botlx = TRUE; diff --git a/src/options.c b/src/options.c index a2e71654f..648895688 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: 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 const 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 }, @@ -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", @@ -3693,6 +3696,7 @@ boolean tinitial, tfrom_file; } return retval; } +#endif /* CURSES_GRAPHICS */ /* WINCAP2 * statuslines:n */ @@ -3718,7 +3722,6 @@ boolean tinitial, tfrom_file; } return retval; } -#endif /* CURSES_GRAPHICS */ /* menustyle:traditional or combination or full or partial */ fullname = "menustyle"; @@ -5725,8 +5728,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/src/sp_lev.c b/src/sp_lev.c index 5dffaea32..4237bf638 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. */ @@ -4691,13 +4691,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."); g.SpLev_Map[x][y] = 1; diff --git a/src/teleport.c b/src/teleport.c index 90cdda2a2..956d19453 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. */ @@ -475,7 +475,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) diff --git a/sys/amiga/winfuncs.c b/sys/amiga/winfuncs.c index e87daed84..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 && g.moves > 1) - (void) doredraw(); + redraw_map(); flush_glyph_buffer(amii_wins[WIN_MAP]->win); } reclip = 0; 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 a95d6ac84..343006f8a 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) { diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c index ee3e2a079..b73c9789c 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: 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. */ @@ -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) @@ -1825,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); @@ -1857,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; } /* @@ -1886,19 +1937,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 +1954,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 +1974,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 +1999,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); } /* ------------------------------------------------------------------------ */ diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index 80ab71828..44cff1332 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: * @@ -79,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; @@ -89,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); @@ -120,6 +125,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 +210,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 (g.invent || (g.moves > 1)) { curses_get_window_size(MAP_WIN, &map_height, &map_width); } else { @@ -369,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 */ @@ -513,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; } @@ -524,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 */ @@ -538,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 = cg.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); @@ -560,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; @@ -593,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 */ @@ -689,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"); } } @@ -743,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; @@ -811,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; } @@ -854,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; } @@ -922,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 } @@ -979,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; @@ -1102,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); } } @@ -1132,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/cursmain.c b/win/curses/cursmain.c index 78d27eb69..cdba9614a 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 */ @@ -72,8 +74,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, @@ -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 9f4bea8c6..b0ca7cc7f 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 == g.moves) { - return; + if (curs_mesg_suppress_turn == g.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 = g.moves; + curs_mesg_suppress_turn = g.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, ">>"); @@ -309,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); } @@ -692,4 +698,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 */ diff --git a/win/curses/cursstat.c b/win/curses/cursstat.c index bcbbd5919..fd3699475 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 && !g.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 } diff --git a/win/tty/termcap.c b/win/tty/termcap.c index e30111f18..4d9f4e363 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 9afb9b927..da2ab4e38 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: 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. */ @@ -94,7 +94,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, @@ -121,11 +121,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, @@ -177,8 +173,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 *)); @@ -196,23 +194,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 /* @@ -320,19 +315,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(); @@ -357,6 +341,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) @@ -365,14 +384,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 @@ -412,6 +434,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... */ @@ -1365,7 +1412,7 @@ tty_create_nhwindow(type) int type; { struct WinDesc *newwin; - int i; + int i, rowoffset; int newid; if (maxwin == MAXWIN) @@ -1400,15 +1447,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: @@ -1422,9 +1476,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; @@ -1447,18 +1500,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; } @@ -1545,6 +1595,7 @@ void tty_clear_nhwindow(window) winid window; { + int i, j, m, n; register struct WinDesc *cw = 0; HUPSKIP(); @@ -1565,10 +1616,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;*/ + } + g.context.botlx = 1; break; case NHW_MAP: /* cheap -- clear the whole thing and tell nethack to redraw botl */ @@ -1576,6 +1635,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: @@ -2610,7 +2671,7 @@ const char *str; case NHW_STATUS: ob = &cw->data[cw->cury][j = cw->curx]; if (g.context.botlx) - *ob = 0; + *ob = '\0'; if (!cw->cury && (int) strlen(str) >= CO) { /* the characters before "St:" are unnecessary */ nb = index(str, ':'); @@ -2635,7 +2696,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 */ @@ -2948,7 +3009,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?) */ @@ -3245,7 +3306,7 @@ setclipped() clipping = TRUE; clipx = clipy = 0; clipxmax = CO; - clipymax = LI - 3; + clipymax = LI - 1 - iflags.wc2_statuslines; } void @@ -3266,14 +3327,13 @@ 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) && !g.restoring) - (void) doredraw(); + redraw_map(); /* ask the core to resend the map window's data */ } } #endif /* CLIPPING */ @@ -3579,14 +3639,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 *)); @@ -3594,10 +3646,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; @@ -3623,21 +3672,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; @@ -3670,18 +3733,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; @@ -3700,10 +3764,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 @@ -3771,10 +3831,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)) @@ -3783,13 +3844,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; @@ -3806,13 +3860,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; @@ -3864,11 +3933,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 */ } @@ -3880,40 +3952,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 */ } @@ -3921,7 +3979,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; } @@ -3929,9 +3986,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); @@ -3953,23 +4012,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) @@ -3980,109 +4039,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++; } } @@ -4093,23 +4141,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 @@ -4117,9 +4165,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]); } @@ -4151,14 +4199,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; @@ -4260,10 +4301,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) { @@ -4271,22 +4312,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); @@ -4296,68 +4334,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) { /* @@ -4378,6 +4425,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 */ @@ -4392,12 +4440,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(); @@ -4405,11 +4453,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 { /* * +-----------------------------+ @@ -4418,43 +4465,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: @@ -4462,12 +4513,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; }